You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2026/05/29 18:47:51 UTC
(tika) branch main updated: TIKA-4744 (#2850)
This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git
The following commit(s) were added to refs/heads/main by this push:
new 81d75a85d7 TIKA-4744 (#2850)
81d75a85d7 is described below
commit 81d75a85d78d33aaa641e5ea69d34b65bd16800c
Author: Tim Allison <ta...@apache.org>
AuthorDate: Fri May 29 14:47:44 2026 -0400
TIKA-4744 (#2850)
---
.../tika/parser/iwork/KeynoteContentHandler.java | 6 +
.../parser/iwork/KeynoteContentHandlerTest.java | 84 ++
.../microsoft/ooxml/FormattingTagManager.java | 19 +-
.../microsoft/ooxml/OOXMLTikaBodyPartHandler.java | 22 +-
.../ooxml/OOXMLWordAndPowerPointTextHandler.java | 72 +-
.../ooxml/XSSFExcelExtractorDecorator.java | 7 +
.../parser/microsoft/rtf/RTFEmbObjHandler.java | 15 +-
.../tika/parser/microsoft/rtf/RTFParser.java | 18 +-
.../tika/parser/microsoft/rtf/TextExtractor.java | 77 +-
.../microsoft/xml/AbstractXML2003Parser.java | 19 +-
.../tika/parser/microsoft/xml/WordMLParser.java | 16 +-
.../parser/microsoft/ooxml/OOXMLDocxSAXTest.java | 46 +
.../tika/parser/microsoft/rtf/RTFParserTest.java | 33 +
.../parser/microsoft/xml/XML2003ParserTest.java | 15 +
.../testRTF_nestedHyperlinkPageRef.rtf | 9 +
.../test-documents/testRTF_parInsideHyperlink.rtf | 9 +
.../testWORDML_tableInsideParagraph.doc | 1467 ++++++++++++++++++++
.../testWORD_emptyParaInTextbox.docx | Bin 0 -> 15982 bytes
.../testWORD_formattingFlipUnderlineOrder.docx | Bin 0 -> 18506 bytes
.../testWORD_tableInRunInsideParagraph.docx | Bin 0 -> 18606 bytes
.../org/apache/tika/parser/epub/EpubParser.java | 8 +
.../tika/parser/odf/FlatOpenDocumentParser.java | 108 +-
.../tika/parser/odf/OpenDocumentBodyHandler.java | 55 +-
.../org/apache/tika/parser/odf/ODFParserTest.java | 29 +
.../test-documents/testFODS_embeddedChart.fods | 547 ++++++++
.../testODT_svgTitleInStyledSpan.odt | Bin 0 -> 6820 bytes
.../org/apache/tika/parser/pdf/XFAExtractor.java | 134 +-
.../org/apache/tika/parser/pdf/PDFParserTest.java | 25 +
.../test-documents/testPDF_malformedXFA.pdf | 27 +
29 files changed, 2726 insertions(+), 141 deletions(-)
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/KeynoteContentHandler.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/KeynoteContentHandler.java
index a3ad3db0a8..7e33716ab6 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/KeynoteContentHandler.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/main/java/org/apache/tika/parser/iwork/KeynoteContentHandler.java
@@ -142,6 +142,12 @@ class KeynoteContentHandler extends DefaultHandler {
} else if (inMetadata && "key:authors".equals(qName)) {
inMetaDataAuthors = false;
} else if (inSlide && "sf:tabular-model".equals(qName)) {
+ // If the final row has fewer cells than numberOfColumns,
+ // parseTableData never reaches its </tr> emit. Close any open
+ // row before </table> so the SAX stream stays balanced.
+ if (currentColumn != null && currentColumn != 0) {
+ xhtml.endElement("tr");
+ }
xhtml.endElement("table");
tableId = null;
numberOfColumns = null;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/test/java/org/apache/tika/parser/iwork/KeynoteContentHandlerTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/test/java/org/apache/tika/parser/iwork/KeynoteContentHandlerTest.java
new file mode 100644
index 0000000000..c67bc445aa
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-apple-module/src/test/java/org/apache/tika/parser/iwork/KeynoteContentHandlerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.iwork;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.StrictXHTMLValidator;
+import org.apache.tika.sax.ToXMLContentHandler;
+import org.apache.tika.sax.XHTMLContentHandler;
+
+public class KeynoteContentHandlerTest {
+
+ /**
+ * TIKA-4744: Keynote tables whose final row contains fewer cells than
+ * numberOfColumns left {@code <tr>} open when {@code </sf:tabular-model>}
+ * fired {@code </table>}. Drive the handler with a synthetic 3-column
+ * table whose final row has only 2 cells and assert the XHTML stays
+ * balanced through endDocument.
+ */
+ @Test
+ public void testIncompleteFinalRowClosesTr() throws Exception {
+ Metadata md = new Metadata();
+ ToXMLContentHandler xml = new ToXMLContentHandler();
+ XHTMLContentHandler xhtml = new XHTMLContentHandler(
+ new StrictXHTMLValidator(xml), md, new ParseContext());
+ KeynoteContentHandler h = new KeynoteContentHandler(xhtml, md);
+
+ xhtml.startDocument();
+
+ // <key:slide>
+ h.startElement("", "slide", "key:slide", new AttributesImpl());
+ // <sf:tabular-model sfa:ID="t1">
+ AttributesImpl tabAttrs = new AttributesImpl();
+ tabAttrs.addAttribute("", "sfa:ID", "sfa:ID", "CDATA", "t1");
+ h.startElement("", "tabular-model", "sf:tabular-model", tabAttrs);
+ // <sf:columns sf:count="3">
+ AttributesImpl colAttrs = new AttributesImpl();
+ colAttrs.addAttribute("", "sf:count", "sf:count", "CDATA", "3");
+ h.startElement("", "columns", "sf:columns", colAttrs);
+ h.endElement("", "columns", "sf:columns");
+ // Two cells (less than 3) -> incomplete final row.
+ AttributesImpl cell = new AttributesImpl();
+ cell.addAttribute("", "sfa:s", "sfa:s", "CDATA", "A");
+ h.startElement("", "ct", "sf:ct", cell);
+ h.endElement("", "ct", "sf:ct");
+ cell = new AttributesImpl();
+ cell.addAttribute("", "sfa:s", "sfa:s", "CDATA", "B");
+ h.startElement("", "ct", "sf:ct", cell);
+ h.endElement("", "ct", "sf:ct");
+ // </sf:tabular-model> -- without the fix this emits </table> while
+ // <tr> is still topmost and StrictXHTMLValidator throws.
+ h.endElement("", "tabular-model", "sf:tabular-model");
+ // </key:slide>
+ h.endElement("", "slide", "key:slide");
+
+ xhtml.endDocument();
+
+ String out = xml.toString();
+ // Sanity: the cells emitted, the row closed, and the table closed.
+ assertTrue(out.contains("<td>A</td>"), "expected td A; got: " + out);
+ assertTrue(out.contains("<td>B</td>"), "expected td B; got: " + out);
+ assertTrue(out.contains("</tr>"), "expected </tr>; got: " + out);
+ assertTrue(out.contains("</table>"), "expected </table>; got: " + out);
+ }
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/FormattingTagManager.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/FormattingTagManager.java
index 0545cd0037..3e8ae2f0de 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/FormattingTagManager.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/FormattingTagManager.java
@@ -114,14 +114,16 @@ class FormattingTagManager {
}
if (runProperties.isBold() != isBold) {
- if (isStrikeThrough) {
- xhtml.endElement("s");
- isStrikeThrough = false;
- }
+ // Close inner tags before flipping <b>. Nesting is <b><i><s><u>
+ // (outermost to innermost), so close innermost first: u, s, i.
if (isUnderline) {
xhtml.endElement("u");
isUnderline = false;
}
+ if (isStrikeThrough) {
+ xhtml.endElement("s");
+ isStrikeThrough = false;
+ }
if (isItalics) {
xhtml.endElement("i");
isItalics = false;
@@ -135,14 +137,15 @@ class FormattingTagManager {
}
if (runProperties.isItalics() != isItalics) {
- if (isStrikeThrough) {
- xhtml.endElement("s");
- isStrikeThrough = false;
- }
+ // Close inner tags before flipping <i>: u then s (u is innermost).
if (isUnderline) {
xhtml.endElement("u");
isUnderline = false;
}
+ if (isStrikeThrough) {
+ xhtml.endElement("s");
+ isStrikeThrough = false;
+ }
if (runProperties.isItalics()) {
xhtml.startElement("i");
} else {
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLTikaBodyPartHandler.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLTikaBodyPartHandler.java
index 1e11e80b78..0366ab9670 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLTikaBodyPartHandler.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLTikaBodyPartHandler.java
@@ -265,7 +265,16 @@ public class OOXMLTikaBodyPartHandler
@Override
public void startTable() throws SAXException {
-
+ // A <w:tbl> can appear nested inside an outer <w:p> -- corrupt-ish but
+ // present in the corpus (e.g., <w:p><w:r>...<wps:txbx><w:txbxContent>
+ // <w:tbl>...). At that point a run-level <b>/<i>/<u>/<s>/<a> may be on
+ // the SAX stack just above where the <table> is about to land. When a
+ // later paragraph inside a cell ends, formattingTags.closeAll() tries
+ // to emit </b> for the outer-paragraph state, but <td> is topmost --
+ // strict validator rejects the mismatch. Close pending formatting now
+ // so the table opens at a clean layer and the outer style is forgotten.
+ // Mirrors startSDT()'s same-shape guard.
+ formattingTags.closeAll();
xhtml.startElement("table");
openStructuralTags.push("table");
tableDepth++;
@@ -377,14 +386,21 @@ public class OOXMLTikaBodyPartHandler
// from the footnote/endnote parts (needed for picture resolution)
Map<String, String> noteRelationships = inlinePartMap.getLinkedRelationships();
xhtml.startElement("div", "class", cssClass);
+ // Track the inner handler so we can call its closeAnyPending() if
+ // the inline-note parseSAX aborts mid-element. Without the drain
+ // the surrounding </div> mismatches whatever the inner handler
+ // left on the SAX stack (<p>/<td>/etc.) and StrictXHTMLValidator
+ // propagates a misleading error.
+ OOXMLTikaBodyPartHandler innerHandler = new OOXMLTikaBodyPartHandler(xhtml);
try {
XMLReaderUtils.parseSAX(new ByteArrayInputStream(xml),
new EmbeddedContentHandler(
new OOXMLWordAndPowerPointTextHandler(
- new OOXMLTikaBodyPartHandler(xhtml),
+ innerHandler,
noteRelationships)),
parseContext);
- } catch (TikaException | IOException e) {
+ } catch (TikaException | IOException | SAXException e) {
+ innerHandler.closeAnyPending();
xhtml.characters("[" + cssClass + " parse error]");
}
xhtml.endElement("div");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
index 0fb05407a5..4f591ae4c6 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/OOXMLWordAndPowerPointTextHandler.java
@@ -146,7 +146,11 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
//have we signaled the start of a p?
//pPr can happen multiple times within a p
//<p><pPr/><r><t>text</t></r><pPr></p>
- private boolean pStarted = false;
+ //
+ //Stack rather than a single boolean: nested <w:p> (e.g., inside
+ //<w:txbxContent>) must not clobber the outer paragraph's "started" marker,
+ //or the outer </w:p> will skip its endParagraph and leave <p> open.
+ private final java.util.Deque<Boolean> pStartedStack = new java.util.ArrayDeque<>();
//alternate content can be embedded in itself.
//need to track depth.
//preferACChoice controls which branch is processed:
@@ -238,6 +242,7 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
} else if (lastStartElementWasP) {
// First child of <p> is not pPr — start paragraph immediately with defaults.
bodyContentsHandler.startParagraph(currPProperties);
+ markCurrentParagraphStarted();
}
lastStartElementWasP = false;
@@ -271,16 +276,15 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
runBuffer.append(TAB_CHAR);
} else if (P.equals(localName)) {
lastStartElementWasP = true;
- // Each <w:p> needs its own pStarted lifecycle. Without this,
- // a nested <w:p> (e.g., inside <wps:txbx>/<w:txbxContent>) would
- // inherit the outer paragraph's pStarted=true, suppress its own
- // startParagraph in the </w:pPr> branch, then fire its
- // endParagraph on </w:p> -- producing an unbalanced start/end
- // count that desyncs the XHTML <p>/<p> stream.
- pStarted = false;
+ // Push a fresh frame for this <w:p>. A nested <w:p> (e.g., inside
+ // <w:txbxContent>) must not share the outer paragraph's started-flag,
+ // or the outer </w:p>'s endParagraph either fires twice (older bug)
+ // or gets skipped (after the pStarted guard fix), and the XHTML
+ // <p>/<p> stream desyncs either way.
+ pStartedStack.push(Boolean.FALSE);
} else if (B.equals(localName)) { //TODO: add bCs
if (inR && inRPr) {
- currRunProperties.setBold(true);
+ currRunProperties.setBold(getOnOff(atts, true));
}
} else if (TC.equals(localName)) {
bodyContentsHandler.startTableCell();
@@ -290,11 +294,11 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
} else if (I.equals(localName)) { //TODO: add iCs
//rprs don't have to be inR; ignore those that aren't
if (inR && inRPr) {
- currRunProperties.setItalics(true);
+ currRunProperties.setItalics(getOnOff(atts, true));
}
} else if (STRIKE.equals(localName)) {
if (inR && inRPr) {
- currRunProperties.setStrike(true);
+ currRunProperties.setStrike(getOnOff(atts, true));
}
} else if (U.equals(localName)) {
if (inR && inRPr) {
@@ -491,6 +495,33 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
return "";
}
+ /**
+ * Reads a {@code ST_OnOff} {@code w:val} attribute: {@code "0"}/{@code "false"}/
+ * {@code "off"} are off, anything else (including absent) follows the supplied
+ * default. The toggle elements (<w:b/>, <w:i/>, <w:strike/>)
+ * default to on when {@code w:val} is absent, but must respect an explicit
+ * {@code w:val="0"} that turns the toggle off (overriding a style-inherited on).
+ */
+ private boolean getOnOff(Attributes atts, boolean defaultValue) {
+ String v = atts.getValue(W_NS, VAL);
+ if (v == null) {
+ return defaultValue;
+ }
+ return !("0".equals(v) || "false".equals(v) || "off".equals(v));
+ }
+
+ private boolean isCurrentParagraphStarted() {
+ Boolean top = pStartedStack.peek();
+ return top != null && top;
+ }
+
+ private void markCurrentParagraphStarted() {
+ if (!pStartedStack.isEmpty()) {
+ pStartedStack.pop();
+ }
+ pStartedStack.push(Boolean.TRUE);
+ }
+
private int getIntVal(Attributes atts) {
String valString = atts.getValue(W_NS, VAL);
if (valString != null) {
@@ -531,9 +562,9 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
} else if (PPR.equals(localName) && inParagraphLevelPPr) {
// Only process as paragraph properties if this pPr was a direct child of <p>.
// pPr inside other elements (e.g., <a:fld> fields) must be ignored.
- if (!pStarted) {
+ if (!isCurrentParagraphStarted()) {
bodyContentsHandler.startParagraph(currPProperties);
- pStarted = true;
+ markCurrentParagraphStarted();
}
currPProperties.reset();
inParagraphLevelPPr = false;
@@ -544,8 +575,19 @@ public class OOXMLWordAndPowerPointTextHandler extends DefaultHandler {
bodyContentsHandler.run(currRunProperties, runBuffer.toString());
runBuffer.setLength(0);
}
- pStarted = false;
- bodyContentsHandler.endParagraph();
+ // Only fire endParagraph if startParagraph was actually called for this <w:p>.
+ // A self-closing <w:p/> (e.g., inside <w:txbxContent>) has no children, so
+ // neither the </pPr> branch nor the lastStartElementWasP branch fires
+ // startParagraph -- but endElement(p) still runs. Without this guard the
+ // body handler's pDepth counter desyncs and the outer paragraph's </p> gets
+ // emitted prematurely, leaving the XHTML stack mismatched at endDocument.
+ boolean started = pStartedStack.isEmpty() ? false : pStartedStack.pop();
+ if (started) {
+ bodyContentsHandler.endParagraph();
+ }
+ // Clear the "first child of p" trigger so the next outer-level startElement
+ // doesn't spuriously fire startParagraph for this already-closed <w:p/>.
+ lastStartElementWasP = false;
} else if (TC.equals(localName)) {
bodyContentsHandler.endTableCell();
} else if (TR.equals(localName)) {
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java
index dae608b7ac..111b94b578 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/XSSFExcelExtractorDecorator.java
@@ -239,6 +239,13 @@ public class XSSFExcelExtractorDecorator extends AbstractOOXMLExtractor {
// the </tbody></table></div> emitted below land in the
// right place.
sheetExtractor.closeAnyPending();
+ } catch (IOException e) {
+ // Truncated stream — same risk: partial <tr>/<td> still
+ // open. Close them so the surrounding </tbody></table>
+ // stays balanced, record the failure, and keep going.
+ metadata.add(TikaCoreProperties.TIKA_META_EXCEPTION_WARNING,
+ ExceptionUtils.getStackTrace(e));
+ sheetExtractor.closeAnyPending();
}
try {
getThreadedComments(container, sheetPart, xhtml);
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFEmbObjHandler.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFEmbObjHandler.java
index 5b52f19acb..0939d87cde 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFEmbObjHandler.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFEmbObjHandler.java
@@ -35,6 +35,7 @@ import org.apache.tika.metadata.RTFMetadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.EmbeddedContentHandler;
+import org.apache.tika.sax.XHTMLBalancingHandler;
/**
* This class buffers data from embedded objects and pictures.
@@ -235,12 +236,24 @@ class RTFEmbObjHandler {
}
metadata.set(TikaCoreProperties.RESOURCE_NAME_EXTENSION_INFERRED, true);
}
+ // Wrap the outer handler in a balancing handler so that if
+ // the embedded parser aborts mid-element (leaving <p>/<b>/etc
+ // open on the wire), we can drain those before propagating.
+ // Without this, the outer RTF parser's subsequent
+ // </b>/</p>/</body> sequence trips StrictXHTMLValidator on
+ // unbalanced nesting and surfaces as a misleading error at
+ // endDocument. Same shape as the SXWPF / Epub catch arms.
+ XHTMLBalancingHandler balancer = new XHTMLBalancingHandler(handler);
try {
embeddedDocumentUtil
- .parseEmbedded(tis, new EmbeddedContentHandler(handler), metadata,
+ .parseEmbedded(tis, new EmbeddedContentHandler(balancer), metadata,
true);
} catch (IOException e) {
+ balancer.drainOpenElements();
EmbeddedDocumentUtil.recordEmbeddedStreamException(e, metadata);
+ } catch (SAXException e) {
+ balancer.drainOpenElements();
+ EmbeddedDocumentUtil.recordException(e, metadata);
}
}
}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java
index 5e8dca35bc..25e58c265c 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/RTFParser.java
@@ -35,6 +35,7 @@ import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
+import org.apache.tika.sax.XHTMLBalancingHandler;
import org.apache.tika.sax.XHTMLContentHandler;
/**
@@ -90,11 +91,26 @@ public class RTFParser implements Parser {
TaggedInputStream tagged = new TaggedInputStream(tis);
XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata, context);
xhtml.startDocument();
+ // Wrap xhtml in a balancing handler so the finally's endDocument
+ // doesn't fire on an unbalanced stack if the RTF state machine
+ // emits inconsistent SAX events (e.g., </b> while <p> is topmost
+ // due to state-vs-stack drift on certain corpus files). Without
+ // this, StrictXHTMLValidator's </body> vs <p>/<b> at endDocument
+ // masks the real well-formedness error from inside extract().
+ XHTMLBalancingHandler balancer = new XHTMLBalancingHandler(xhtml);
try {
- parseInline(tis, xhtml, metadata, context);
+ parseInline(tis, balancer, metadata, context);
} catch (IOException e) {
tagged.throwIfCauseOf(e);
+ balancer.drainOpenElements();
throw new TikaException("Error parsing an RTF document", e);
+ } catch (SAXException | TikaException e) {
+ // Drain on any exception escaping parseInline. parseInline can
+ // throw TikaException too (memory limits, embedded extraction,
+ // etc.); without the drain, the finally's xhtml.endDocument()
+ // throws on the unbalanced stack and masks the real error.
+ balancer.drainOpenElements();
+ throw e;
} finally {
xhtml.endDocument();
}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/TextExtractor.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/TextExtractor.java
index 72913ae437..0e7252423e 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/TextExtractor.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/rtf/TextExtractor.java
@@ -311,6 +311,12 @@ final class TextExtractor {
// Non-null if we've seen the url for a HYPERLINK but not yet
// its text:
private String pendingURL;
+ // Group depth at which the current <a href=...> was opened (in
+ // groupState.depth units). Used to defer </a> emission until we leave
+ // the fldrslt group that opened it, so a nested \field (e.g., PAGEREF
+ // inside a HYPERLINK's fldrslt) doesn't prematurely close the outer <a>
+ // via the fieldState==3 branch in processGroupEnd. -1 means no <a> open.
+ private int hyperlinkAnchorDepth = -1;
// Used to process the sub-groups inside the upr
// group:
private int uprState = -1;
@@ -467,7 +473,6 @@ final class TextExtractor {
}
private void extract(PushbackInputStream in) throws IOException, SAXException, TikaException {
-
while (true) {
final int b = in.read();
if (b == -1) {
@@ -642,6 +647,16 @@ final class TextExtractor {
lazyStartParagraph();
}
if (inParagraph || paragraphStack.size() > 0) {
+ // A HYPERLINK's <a> can be open mid-fldrslt when a \par is
+ // encountered. Closing </p> while <a> is topmost on the SAX
+ // stack trips StrictXHTMLValidator -- close </a> first and
+ // forget the hyperlink state (the paragraph break terminates
+ // the link visually anyway).
+ if (hyperlinkAnchorDepth >= 0) {
+ end("a");
+ fieldState = 0;
+ hyperlinkAnchorDepth = -1;
+ }
if (groupState.italic) {
end("i");
groupState.italic = preserveStyles;
@@ -1365,16 +1380,37 @@ final class TextExtractor {
addOutputChar('\u201D');
}
} else if (equals("fldinst")) {
- fieldState = 1;
- groupState.ignore = false;
+ if (fieldState == 0) {
+ fieldState = 1;
+ groupState.ignore = false;
+ } else {
+ // Nested \fldinst inside an outer field (e.g., PAGEREF inside
+ // a HYPERLINK's fldrslt). Suppress the nested instruction text
+ // and leave the outer fieldState/pendingURL untouched: the
+ // outer field's closing group still needs to see fieldState==3
+ // to emit </a>. The accompanying \fldrslt of the nested field
+ // emits its display text into the outer hyperlink's <a>.
+ groupState.ignore = true;
+ }
} else if (equals("fldrslt") && fieldState == 2) {
assert pendingURL != null;
+ // Temporarily clear fieldState so lazyStartParagraph's endStyles
+ // actually flushes any pending <b>/<i>. endStyles short-circuits
+ // when fieldState != 0 to keep style flips inside an already-open
+ // <a> from leaking; but here we're about to OPEN the <a>, so any
+ // outer <b>/<i> still on the SAX stack must close first or <p>
+ // would land on top of <b> and the link's </b> later mismatches.
+ fieldState = 0;
lazyStartParagraph();
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute(XHTML, "href", "href", "CDATA", pendingURL);
out.startElement("", "a", "a", attrs);
pendingURL = null;
fieldState = 3;
+ // Remember which group depth owns this <a>. processGroupEnd only
+ // emits </a> once we leave this depth, so nested groups inside
+ // the fldrslt (e.g., a PAGEREF \field) don't trip the close early.
+ hyperlinkAnchorDepth = groupState.depth;
groupState.ignore = false;
}
}
@@ -1465,6 +1501,16 @@ final class TextExtractor {
embObjHandler.handleCompletedObject();
} catch (TikaException | IOException e) {
EmbeddedDocumentUtil.recordException(e, metadata);
+ } catch (SecurityException e) {
+ // Security-relevant exceptions must always propagate
+ // immediately -- never swallow them as a warning.
+ throw e;
+ } catch (RuntimeException e) {
+ // POI dispatch on a zero-byte embedded object throws
+ // EmptyFileException; other malformed embedded payloads
+ // can surface as a variety of runtime exceptions. Record
+ // and continue rather than aborting the outer RTF parse.
+ EmbeddedDocumentUtil.recordException(e, metadata);
}
groupState.objdata = false;
} else if (groupState.pictDepth > 0) {
@@ -1491,6 +1537,18 @@ final class TextExtractor {
if (groupStates.size() > 0) {
// Restore group state:
final GroupState outerGroupState = groupStates.removeLast();
+ // If we're leaving the fldrslt group that owns the current <a>,
+ // close </a> BEFORE the style restore runs. Otherwise the
+ // restore's start("b") would land on top of <a>, and the next
+ // </a>/<b> would mismatch. Doing this here also flips fieldState
+ // to 0, which un-gates the style compare below so the outer
+ // bold/italic context is actually re-emitted.
+ if (fieldState == 3 && hyperlinkAnchorDepth >= 0
+ && outerGroupState.depth < hyperlinkAnchorDepth) {
+ end("a");
+ fieldState = 0;
+ hyperlinkAnchorDepth = -1;
+ }
//only modify styles if we're not in a hyperlink
if (fieldState == 0) {
// Close italic, if outer does not have italic or
@@ -1547,8 +1605,17 @@ final class TextExtractor {
// inlined, but fail to record them in metadata
// as a field value.
} else if (fieldState == 3) {
- end("a");
- fieldState = 0;
+ // Only close </a> once we've left the fldrslt group that opened
+ // it. groupState.depth here is the OUTER group we just restored
+ // to; if it's now below the recorded anchor depth, the fldrslt
+ // group has closed. This guards against nested-field group ends
+ // (e.g., a PAGEREF \fldinst inside the HYPERLINK fldrslt) closing
+ // the outer </a> too early.
+ if (hyperlinkAnchorDepth >= 0 && groupState.depth < hyperlinkAnchorDepth) {
+ end("a");
+ fieldState = 0;
+ hyperlinkAnchorDepth = -1;
+ }
}
}
}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/AbstractXML2003Parser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/AbstractXML2003Parser.java
index 6d7e4ef8b9..0810cfb4ec 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/AbstractXML2003Parser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/AbstractXML2003Parser.java
@@ -38,6 +38,7 @@ import org.apache.tika.parser.xml.ElementMetadataHandler;
import org.apache.tika.sax.EmbeddedContentHandler;
import org.apache.tika.sax.TaggedContentHandler;
import org.apache.tika.sax.TeeContentHandler;
+import org.apache.tika.sax.XHTMLBalancingHandler;
import org.apache.tika.sax.XHTMLContentHandler;
import org.apache.tika.utils.XMLReaderUtils;
@@ -88,7 +89,15 @@ public abstract class AbstractXML2003Parser implements Parser {
final XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata, context);
xhtml.startDocument();
- TaggedContentHandler tagged = new TaggedContentHandler(xhtml);
+ // Wrap xhtml in a balancing handler so that if the inner SAX parser
+ // aborts mid-element (e.g., on truncated input -- common in crawled
+ // sources clipped to 1MB), we can drain the open elements before
+ // the finally block calls xhtml.endDocument(). Without this drain
+ // the trailing </body></html> mismatches the unclosed <p>/<tr>/etc.
+ // left on the wire, and StrictXHTMLValidator masks the real parse
+ // error with a misleading well-formedness exception.
+ final XHTMLBalancingHandler balancer = new XHTMLBalancingHandler(xhtml);
+ TaggedContentHandler tagged = new TaggedContentHandler(balancer);
tis.setCloseShield();
try {
//need to get new SAXParser because
@@ -99,7 +108,15 @@ public abstract class AbstractXML2003Parser implements Parser {
getContentHandler(tagged, metadata, context)));
} catch (SAXException e) {
WriteLimitReachedException.throwIfWriteLimitReached(e);
+ // Close anything the aborted parse left open, then propagate.
+ balancer.drainOpenElements();
throw new TikaException("XML parse error", e);
+ } catch (IOException e) {
+ // Truncated streams etc. -- same problem as the SAX path: without
+ // a drain, the finally's xhtml.endDocument() throws on the
+ // unbalanced stack and masks the real IO error.
+ balancer.drainOpenElements();
+ throw e;
} finally {
tis.removeCloseShield();
xhtml.endDocument();
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java
index 934ebcaa69..743f3a4d8e 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/xml/WordMLParser.java
@@ -121,13 +121,27 @@ public class WordMLParser extends AbstractXML2003Parser {
//close p if already in a p to prevent nested <p>
if (inP) {
handler.endElement(XHTMLContentHandler.XHTML, P, P);
+ inP = false;
}
- inP = true;
+ } else if (html.equals(TABLE) && inP) {
+ // WordML allows <w:tbl> nested inside <w:p>. XHTML
+ // does not allow a block-level table inside an
+ // inline <p>: the inline <p> would still be on the
+ // SAX stack when the table's <tr>/<td> children
+ // (and their inner <w:p>'s) emit, and the next
+ // "close p before nested p" event would land on top
+ // of <td>/<tr> instead of <p>. Close the open <p>
+ // before opening the table.
+ handler.endElement(XHTMLContentHandler.XHTML, P, P);
+ inP = false;
}
handler.startElement(XHTMLContentHandler.XHTML, html, html, EMPTY_ATTRS);
if (html.equals(TABLE)) {
handler.startElement(XHTMLContentHandler.XHTML, TBODY, TBODY, EMPTY_ATTRS);
}
+ if (P.equals(localName)) {
+ inP = true;
+ }
}
if (BR.equals(localName)) {
handler.characters(NEWLINE, 0, 1);
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ooxml/OOXMLDocxSAXTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ooxml/OOXMLDocxSAXTest.java
index 173922f388..ab10905228 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ooxml/OOXMLDocxSAXTest.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/ooxml/OOXMLDocxSAXTest.java
@@ -264,6 +264,52 @@ public class OOXMLDocxSAXTest extends TikaTest {
assertContainsCount("inside-text", xml, 1);
}
+ @Test
+ public void testEmptyParagraphInTextbox() throws Exception {
+ // TIKA-4744: a self-closing <w:p/> inside <w:txbxContent> used to fire
+ // endParagraph without a matching startParagraph, prematurely closing
+ // the outer paragraph and leaving the XHTML stack mismatched at
+ // endDocument. Also exercises <w:b w:val="0"/>/<w:strike w:val="0"/>
+ // which used to be read as "on" regardless of the val attribute.
+ // getXML wraps the handler in StrictXHTMLValidator -- a balance error
+ // would throw before any assertions ran.
+ XMLResult r = getXML("testWORD_emptyParaInTextbox.docx");
+ // Confirm the toggle-off attribute is now respected: this file's body
+ // text has explicit <w:b w:val="0"/>, so no spurious <b>/<s> wrapping.
+ assertContains("<p>Meno, priezvisko, tituly", r.xml);
+ assertNotContained("<b><s>Meno", r.xml);
+ // And the picture-bearing paragraph stays in one <p>, not split.
+ assertContains("Vedný odbor:", r.xml);
+ }
+
+ @Test //TIKA-4744
+ public void testTableInRunInsideParagraph() throws Exception {
+ // A <w:tbl> nested deeply inside an outer <w:p>'s run (via
+ // <mc:AlternateContent>/<w:drawing>/<wps:txbx>/<w:txbxContent>) used
+ // to leave the run's <b>/<i>/etc on the SAX stack below the new
+ // <table>. A subsequent paragraph end inside a cell then tried to
+ // closeFormattingTags() for the outer state, emitting </b> while
+ // <td> was topmost. startTable now closes pending formatting first,
+ // mirroring startSDT.
+ XMLResult r = getXML("testWORD_tableInRunInsideParagraph.docx");
+ assertContains("<table>", r.xml);
+ }
+
+ @Test //TIKA-4744
+ public void testFormattingFlipPreservesNestingOrder() throws Exception {
+ // FormattingTagManager.applyFormatting used to close </s> before </u>
+ // in the bold-flip and italic-flip branches, but XHTML nesting is
+ // <b><i><s><u> (u is innermost). A run with bold+strike+underline
+ // followed by a non-bold run trips the wrong-order close: </s>
+ // emitted while <u> is topmost, then the catch arm's closeAnyPending
+ // tries </u> against <s> -- the visible </u> vs <s> the validator
+ // flagged on three corpus files.
+ XMLResult r = getXML("testWORD_formattingFlipUnderlineOrder.docx");
+ // Just confirm something extracted -- the real check is no validator
+ // throw, which getXML enforces via StrictXHTMLValidator.
+ assertContains("<body>", r.xml);
+ }
+
@Test
public void testDOCXOverrideParagraphNumbering() throws Exception {
String xml = getXML("testWORD_override_list_numbering.docx").xml;
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/rtf/RTFParserTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/rtf/RTFParserTest.java
index 33243c963a..1b95235cc9 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/rtf/RTFParserTest.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/rtf/RTFParserTest.java
@@ -60,6 +60,39 @@ public class RTFParserTest extends TikaTest {
assertContains("indexation Word", content);
}
+ @Test //TIKA-4744
+ public void testParInsideHyperlink() throws Exception {
+ // A \par inside a HYPERLINK's \fldrslt used to leave <a> on the SAX
+ // stack while endParagraph emitted </p>, tripping the strict
+ // validator with </p> vs <a>. The exception was masked by the
+ // finally's xhtml.endDocument() throwing </body> vs <p>; visible
+ // end-state was just "<p> open at endDocument". Closing pending <a>
+ // before </p> drops the link span at the paragraph break, which is
+ // the right XHTML rendering.
+ XMLResult r = getXML("testRTF_parInsideHyperlink.rtf");
+ // First paragraph contains the link text, properly closed.
+ assertContains("<a href=\"#target\">line1</a>", r.xml);
+ // Second paragraph picks up the post-\par text without an open <a>.
+ assertContains("line2", r.xml);
+ }
+
+ @Test //TIKA-4744
+ public void testNestedHyperlinkPageRef() throws Exception {
+ // A HYPERLINK field with a PAGEREF \field nested inside its \fldrslt
+ // used to leak: the nested \fldinst would overwrite fieldState=3 with
+ // 1, so the outer fldrslt's group close skipped the </a> emission and
+ // <a> stayed open. The cascade surfaced at endDocument as the strict
+ // validator complaining </body> didn't match topmost <p>.
+ // getXML wraps the handler in StrictXHTMLValidator, so any imbalance
+ // would throw before the assertions.
+ XMLResult r = getXML("testRTF_nestedHyperlinkPageRef.rtf");
+ // PAGEREF result "42" should render INSIDE the outer hyperlink's <a>,
+ // not after a prematurely-closed </a>:
+ assertContains("<a href=\"#target\">42</a>", r.xml);
+ assertContains("Before", r.xml);
+ assertContains("after.", r.xml);
+ }
+
@Test
public void testUmlautSpacesExtraction2() throws Exception {
String content = getText("testRTFUmlautSpaces2.rtf");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/xml/XML2003ParserTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/xml/XML2003ParserTest.java
index 3e00b600dd..bf07d2af5a 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/xml/XML2003ParserTest.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/java/org/apache/tika/parser/microsoft/xml/XML2003ParserTest.java
@@ -63,6 +63,21 @@ public class XML2003ParserTest extends MultiThreadedTikaTest {
}
+ @Test //TIKA-4744
+ public void testTableInsideParagraph() throws Exception {
+ // WordML allows <w:tbl> nested inside <w:p>. The XHTML emission must
+ // close the <p> before opening the <table>, otherwise the inline
+ // <p> stays on the SAX stack while <tr>/<td> emit -- and the inner
+ // paragraphs inside cells trip the "close p before nested p" logic
+ // emitting </p> while <td> is topmost.
+ // getXML wraps the handler in StrictXHTMLValidator, so any imbalance
+ // would throw before the assertions.
+ XMLResult r = getXML("testWORDML_tableInsideParagraph.doc");
+ assertEquals("application/vnd.ms-wordml", r.metadata.get(Metadata.CONTENT_TYPE));
+ // Structural sanity: tables emit with tbody and rows.
+ assertContains("<table><tbody>", r.xml);
+ }
+
@Test
@Timeout(60000)
public void testMultiThreaded() throws Exception {
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testRTF_nestedHyperlinkPageRef.rtf b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testRTF_nestedHyperlinkPageRef.rtf
new file mode 100644
index 0000000000..250b56c558
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testRTF_nestedHyperlinkPageRef.rtf
@@ -0,0 +1,9 @@
+{\rtf1\ansi
+{\fonttbl{\f0 Arial;}}
+\pard {\f0 Before }
+{\field {\*\fldinst HYPERLINK "#target" }{\fldrslt
+{\field {\*\fldinst { PAGEREF "target" } }{\fldrslt 42}}
+}}
+{\f0 after.}
+\par
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testRTF_parInsideHyperlink.rtf b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testRTF_parInsideHyperlink.rtf
new file mode 100644
index 0000000000..f5b8a139f3
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testRTF_parInsideHyperlink.rtf
@@ -0,0 +1,9 @@
+{\rtf1\ansi
+{\fonttbl{\f0 Arial;}}
+\pard {\f0 Before }
+{\field {\*\fldinst HYPERLINK "#target" }{\fldrslt
+{\f0 line1\par line2}
+}}
+{\f0 after.}
+\par
+}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORDML_tableInsideParagraph.doc b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORDML_tableInsideParagraph.doc
new file mode 100644
index 0000000000..9309c484f9
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORDML_tableInsideParagraph.doc
@@ -0,0 +1,1467 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?mso-application progid="Word.Document"?>
+
+<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" xmlns:aml="http://schemas.microsoft.com/aml/2001/core" xml:space="preserve">
+ <o:DocumentProperties>
+ <o:Author/>
+ <o:Title/>
+ <o:Description/>
+ <o:Subject/>
+ </o:DocumentProperties>
+ <w:styles>
+ <w:style w:type="paragraph" w:styleId="4">
+ <w:name w:val="toc 4"/>
+ <wx:uiName wx:val="catalog 4"/>
+ <w:autoRedefine/>
+ <w:semiHidden/>
+ <w:rsid w:val="009B3C8F"/>
+ <w:pPr>
+ <w:pStyle w:val="4"/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:rPr>
+ <wx:font wx:val="Times New Roman"/>
+ </w:rPr>
+ </w:style>
+ <w:style w:type="character" w:styleId="Hyperlink">
+ <w:name w:val="Hyperlink"/>
+ <w:rPr>
+ <w:u w:val="single"/>
+ <w:color w:val="0000ff"/>
+ </w:rPr>
+ </w:style>
+ <w:style w:type="table" w:default="on" styleId="TableNormal">
+ <w:name w:val="Normal Table"/>
+ <wx:uiName wx:val="Table Normal"/>
+ <w:rPr>
+ <wx:font wx:val="Calibri"/>
+ <w:lang w:val="EN-US" w:fareast="ZH-CN" w:bidi="AR-SA"/>
+ </w:rPr>
+ <w:tblPr>
+ <w:tblInd w:w="0" w:type="dxa"/>
+ <w:tblCellMar>
+ <w:top w:w="0" w:type="dxa"/>
+ <w:left w:w="108" w:type="dxa"/>
+ <w:bottom w:w="0" w:type="dxa"/>
+ <w:right w:w="108" w:type="dxa"/>
+ </w:tblCellMar>
+ </w:tblPr>
+ </w:style>
+ </w:styles>
+ <w:displayBackgroundShape/>
+ <w:docPr>
+ <w:view w:val="print"/>
+ <w:zoom w:percent="100"/>
+ </w:docPr>
+ <w:body>
+ <w:bgPict/>
+ <wx:sect>
+ <w:tbl>
+ <w:tblPr>
+ <w:tblInd w:w="0" w:type="dxa"/>
+ <w:tblStyle w:val="TableGrid"/>
+ <w:tblOverlap w:val="Never"/>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ <w:tblBorders/>
+ </w:tblPr>
+ <w:tblGrid>
+ <w:gridCol w:val="3840"/>
+ <w:gridCol w:val="3840"/>
+ <w:gridCol w:val="3840"/>
+ </w:tblGrid>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[First Page]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[Some Data]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[Second column]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[Third]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[tic-tac-toe]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ <w:p>
+ <w:pPr>
+ <w:sectPr>
+ <w:hdr w:type="odd">
+ <w:tbl>
+ <w:tblPr>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ </w:tblPr>
+ <w:tr>
+ <w:trPr>
+ <w:trHeight w:val="720"/>
+ </w:trPr>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="11520" w:type="dxa"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ </w:hdr>
+ <w:ftr w:type="odd">
+ <w:tbl>
+ <w:tblPr>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ </w:tblPr>
+ <w:tr>
+ <w:trPr>
+ <w:trHeight w:val="720"/>
+ </w:trPr>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="11520" w:type="dxa"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ <w:tbl>
+ <w:tblPr>
+ <w:tblInd w:w="0" w:type="dxa"/>
+ <w:tblStyle w:val="TableGrid"/>
+ <w:tblOverlap w:val="Never"/>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ <w:tblBorders/>
+ </w:tblPr>
+ <w:tblGrid>
+ <w:gridCol w:val="3840"/>
+ <w:gridCol w:val="3840"/>
+ <w:gridCol w:val="3840"/>
+ </w:tblGrid>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="begin"/>
+ </w:r>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:instrText>PAGE</w:instrText>
+ </w:r>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="end"/>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[/]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="begin"/>
+ </w:r>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:instrText>NUMPAGES</w:instrText>
+ </w:r>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="end"/>
+ </w:r>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ </w:ftr>
+ <w:pgSz w:w="12240" w:h="15840" w:orient="auto"/>
+ <w:pgMar w:top="360" w:bottom="360" w:left="360" w:right="360" w:header="360" w:footer="360"/>
+ <w:pgBorders w:offset-from="page"/>
+ </w:sectPr>
+ </w:pPr>
+ </w:p>
+ </wx:sect>
+ <wx:sect>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ <w:tbl>
+ <w:tblPr>
+ <w:tblInd w:w="0" w:type="dxa"/>
+ <w:tblStyle w:val="TableGrid"/>
+ <w:tblOverlap w:val="Never"/>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ <w:tblBorders/>
+ </w:tblPr>
+ <w:tblGrid>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ <w:gridCol w:val="576"/>
+ </w:tblGrid>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[1]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[20]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="576" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ <w:sectPr>
+ <w:hdr w:type="odd">
+ <w:tbl>
+ <w:tblPr>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ </w:tblPr>
+ <w:tr>
+ <w:trPr>
+ <w:trHeight w:val="720"/>
+ </w:trPr>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="11520" w:type="dxa"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ </w:hdr>
+ <w:ftr w:type="odd">
+ <w:tbl>
+ <w:tblPr>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ </w:tblPr>
+ <w:tr>
+ <w:trPr>
+ <w:trHeight w:val="720"/>
+ </w:trPr>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="11520" w:type="dxa"/>
+ </w:tcPr>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ <w:tbl>
+ <w:tblPr>
+ <w:tblInd w:w="0" w:type="dxa"/>
+ <w:tblStyle w:val="TableGrid"/>
+ <w:tblOverlap w:val="Never"/>
+ <w:tblW w:w="11520" w:type="dxa"/>
+ <w:tblLook w:val="01E0"/>
+ <w:tblLayout w:type="Fixed"/>
+ <w:tblBorders/>
+ </w:tblPr>
+ <w:tblGrid>
+ <w:gridCol w:val="3840"/>
+ <w:gridCol w:val="3840"/>
+ <w:gridCol w:val="3840"/>
+ </w:tblGrid>
+ <w:tr>
+ <w:trPr/>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="begin"/>
+ </w:r>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:instrText>PAGE</w:instrText>
+ </w:r>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="end"/>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:t><![CDATA[/]]></w:t>
+ </w:r>
+ </w:p>
+ </w:tc>
+ <w:tc>
+ <w:tcPr>
+ <w:tcW w:w="3840" w:type="dxa"/>
+ <w:tcBorders/>
+ <w:vAlign w:val="baseline"/>
+ <w:noWrap w:val="off"/>
+ </w:tcPr>
+ <w:p>
+ <w:pPr>
+ <w:pBdr/>
+ <w:bidi w:val="off"/>
+ </w:pPr>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="begin"/>
+ </w:r>
+ <w:r>
+ <w:rPr>
+ <w:color w:val="000000"/>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ </w:rPr>
+ <w:instrText>NUMPAGES</w:instrText>
+ </w:r>
+ <w:r>
+ <w:rFonts w:ascii="Arial" w:fareast="Arial" w:h-ansi="Arial" w:cs="Arial"/>
+ <w:sz w:val="20"/>
+ <w:sz-cs w:val="20"/>
+ <w:spacing w:val="0"/>
+ <w:fldChar w:fldCharType="end"/>
+ </w:r>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ <w:p>
+ <w:rPr>
+ <w:vanish w:val="on"/>
+ </w:rPr>
+ </w:p>
+ </w:tc>
+ </w:tr>
+ </w:tbl>
+ </w:ftr>
+ <w:pgSz w:w="12240" w:h="15840" w:orient="auto"/>
+ <w:pgMar w:top="360" w:bottom="360" w:left="360" w:right="360" w:header="360" w:footer="360"/>
+ <w:pgBorders w:offset-from="page"/>
+ </w:sectPr>
+ </wx:sect>
+ </w:body>
+</w:wordDocument>
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_emptyParaInTextbox.docx b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_emptyParaInTextbox.docx
new file mode 100644
index 0000000000..3d1be3cae5
Binary files /dev/null and b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_emptyParaInTextbox.docx differ
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_formattingFlipUnderlineOrder.docx b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_formattingFlipUnderlineOrder.docx
new file mode 100755
index 0000000000..06d4467a9c
Binary files /dev/null and b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_formattingFlipUnderlineOrder.docx differ
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_tableInRunInsideParagraph.docx b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_tableInRunInsideParagraph.docx
new file mode 100644
index 0000000000..bbdefdd1ff
Binary files /dev/null and b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testWORD_tableInRunInsideParagraph.docx differ
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java
index 9d2e2239be..b91e7e2993 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/epub/EpubParser.java
@@ -248,6 +248,11 @@ public class EpubParser implements Parser {
normalizer.drainOpenElements();
} catch (IOException ioe) {
LOG.trace("epub spine read IOException on {}: {}", path, ioe.toString());
+ // Same risk as the SAX path: the partial parse may
+ // have left elements open. Drain before rethrow so
+ // subsequent spine items and the outer </body>
+ // don't land on a corrupted stack.
+ normalizer.drainOpenElements();
throw ioe;
} finally {
processed.add(id);
@@ -351,6 +356,9 @@ public class EpubParser implements Parser {
} catch (IOException e) {
failed++;
LOG.trace("epub fallback: IO failure on {}: {}", entry.getName(), e.toString());
+ // Same drain need as the SAX path: a partial parse before the
+ // IO failure may have left elements open for the next iter.
+ normalizer.drainOpenElements();
}
}
LOG.trace("epub fallback summary: parsed={} failed={}", parsed, failed);
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java
index 30eae3d8f5..25c3013cd4 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/FlatOpenDocumentParser.java
@@ -38,6 +38,7 @@ import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.ContentHandlerDecorator;
import org.apache.tika.sax.EmbeddedContentHandler;
+import org.apache.tika.sax.XHTMLBalancingHandler;
import org.apache.tika.sax.XHTMLContentHandler;
import org.apache.tika.utils.XMLReaderUtils;
@@ -100,9 +101,16 @@ public class FlatOpenDocumentParser implements Parser {
final XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata, context);
xhtml.startDocument();
+ // A balancing handler so that if the inner SAX parser aborts mid-
+ // element (e.g., malformed flat-doc XML -- some corpus FODT files
+ // omit </text:note-citation>), we can drain pending opens before
+ // the finally's xhtml.endDocument() fires its </body>/</html>.
+ // Without the drain, that endDocument throws </body> vs <p>/etc
+ // and masks the real well-formedness error.
+ final XHTMLBalancingHandler balancer = new XHTMLBalancingHandler(xhtml);
tis.setCloseShield();
try {
- ContentHandler fodHandler = getContentHandler(xhtml, metadata, context);
+ ContentHandler fodHandler = getContentHandler(balancer, metadata, context);
XMLReaderUtils.parseSAX(tis,
new EmbeddedContentHandler(fodHandler), context);
//can only detect subtype (text/pres/sheet) during parse.
@@ -111,6 +119,13 @@ public class FlatOpenDocumentParser implements Parser {
if (detected != null) {
metadata.set(Metadata.CONTENT_TYPE, detected.toString());
}
+ } catch (SAXException | IOException | TikaException e) {
+ // Drain the balancer on ANY exception that escapes the parse
+ // loop. Without this, the finally's xhtml.endDocument() throws
+ // on the unbalanced stack and the masked </body> vs <p>/<b>
+ // hides the real failure (truncated input, malformed XML, etc.).
+ balancer.drainOpenElements();
+ throw e;
} finally {
tis.removeCloseShield();
xhtml.endDocument();
@@ -144,6 +159,20 @@ public class FlatOpenDocumentParser implements Parser {
private final boolean extractMacros;
private ContentHandler currentHandler = defaultHandler;
private MediaType detectedType = null;
+ // <office:document> can nest: a flat ODS can embed a full inner
+ // <office:document> inside <draw:object> (e.g., for an embedded
+ // chart). Each level has its own <office:meta>/<office:body>/
+ // <office:scripts>. We only honour those sections at the OUTER
+ // document level -- inner ones must stay inside whatever handler
+ // is already active (so the outer body's <draw:object> content
+ // keeps emitting through bodyHandler), or the trailing </draw:object>
+ // and surrounding events get routed to a no-op handler and leave
+ // <object>/<table> unclosed on the XHTML stream.
+ private int documentDepth = 0;
+ // Track outer-section depth so the matching close switches back to
+ // defaultHandler only when the section actually ends, not on a
+ // nested-doc occurrence.
+ private int outerSectionDepth = 0;
private FlatOpenDocumentParserHandler(ContentHandler baseHandler, Metadata metadata,
ParseContext parseContext, boolean extractMacros) {
@@ -169,27 +198,48 @@ public class FlatOpenDocumentParser implements Parser {
@Override
public void startElement(String namespaceURI, String localName, String qName,
Attributes attrs) throws SAXException {
-
- if (META.equals(localName)) {
- currentHandler = metadataHandler;
- } else if (BODY.equals(localName)) {
- currentHandler = bodyHandler;
- } else if (extractMacros && SCRIPTS.equals(localName)) {
- currentHandler = macroHandler;
- }
-
- //trust the mimetype element if it exists for the subtype
if (DOCUMENT.equals(localName)) {
- String mime = XMLReaderUtils.getAttrValue("mimetype", attrs);
- if (mime != null) {
- if (mime.equals(ODT.toString())) {
- detectedType = FLAT_ODT;
- } else if (mime.equals(ODP.toString())) {
- detectedType = FLAT_ODP;
- } else if (mime.equals(ODS.toString())) {
- detectedType = FLAT_ODS;
+ documentDepth++;
+ //trust the mimetype element if it exists for the subtype.
+ //Only consult the outer <office:document>; inner ones (e.g.,
+ //an embedded chart's document) would otherwise overwrite the
+ //container's detected subtype.
+ if (documentDepth == 1) {
+ String mime = XMLReaderUtils.getAttrValue("mimetype", attrs);
+ if (mime != null) {
+ if (mime.equals(ODT.toString())) {
+ detectedType = FLAT_ODT;
+ } else if (mime.equals(ODP.toString())) {
+ detectedType = FLAT_ODP;
+ } else if (mime.equals(ODS.toString())) {
+ detectedType = FLAT_ODS;
+ }
}
}
+ } else if (documentDepth == 1 && outerSectionDepth == 0) {
+ // Only outermost-level META/BODY/SCRIPTS triggers a handler
+ // switch. Inner-document occurrences are forwarded to whichever
+ // outer section's handler is already active.
+ if (META.equals(localName)) {
+ currentHandler = metadataHandler;
+ outerSectionDepth = 1;
+ } else if (BODY.equals(localName)) {
+ currentHandler = bodyHandler;
+ outerSectionDepth = 1;
+ } else if (extractMacros && SCRIPTS.equals(localName)) {
+ currentHandler = macroHandler;
+ outerSectionDepth = 1;
+ }
+ } else if (outerSectionDepth > 0
+ && (META.equals(localName) || BODY.equals(localName)
+ || (extractMacros && SCRIPTS.equals(localName)))) {
+ // Track nested occurrences so the matching end doesn't
+ // prematurely flip currentHandler back to defaultHandler.
+ // SCRIPTS is gated on extractMacros to match the decrement
+ // condition in endElement -- otherwise a <office:scripts>
+ // inside an outer section would inflate the counter without
+ // a matching decrement, stranding currentHandler.
+ outerSectionDepth++;
}
currentHandler.startElement(namespaceURI, localName, qName, attrs);
}
@@ -202,14 +252,20 @@ public class FlatOpenDocumentParser implements Parser {
@Override
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
- if (META.equals(localName)) {
- currentHandler = defaultHandler;
- } else if (BODY.equals(localName)) {
- currentHandler = defaultHandler;
- } else if (extractMacros && SCRIPTS.equals(localName)) {
- currentHandler = defaultHandler;
- }
+ // Forward the end event to the still-active handler BEFORE we
+ // consider switching back. Otherwise the </office:body> close
+ // itself would land on defaultHandler.
currentHandler.endElement(namespaceURI, localName, qName);
+ if (DOCUMENT.equals(localName)) {
+ documentDepth--;
+ } else if (outerSectionDepth > 0
+ && (META.equals(localName) || BODY.equals(localName)
+ || (extractMacros && SCRIPTS.equals(localName)))) {
+ outerSectionDepth--;
+ if (outerSectionDepth == 0) {
+ currentHandler = defaultHandler;
+ }
+ }
}
}
}
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentBodyHandler.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentBodyHandler.java
index 6bbc7eaa86..8c121db125 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentBodyHandler.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/main/java/org/apache/tika/parser/odf/OpenDocumentBodyHandler.java
@@ -337,20 +337,32 @@ class OpenDocumentBodyHandler extends ElementMappingContentHandler {
}
/**
- * Returns true for ODF elements that map to block-level XHTML and so
- * shouldn't sit inside open inline-style tags. When such an element opens
- * while {@code <b>/<i>/<u>} are on the SAX stack, the inline tags would
- * trap the new block element underneath them; subsequent style flips
- * inside the block would emit close events that don't match the topmost
- * open element. The startElement handler closes pending style tags
- * before opening any of these.
+ * Returns true for ODF elements that shouldn't sit inside open inline-style
+ * tags. When such an element opens while {@code <b>/<i>/<u>} are on the SAX
+ * stack, the inline tags would trap the new element underneath them;
+ * subsequent style flips inside would emit close events that don't match
+ * the topmost open element. The startElement handler closes pending style
+ * tags before opening any of these.
* <p>
+ * Two cases qualify:
+ * <ul>
+ * <li>Block-level XHTML targets (draw:text-box, table/row/cell, list-item)
+ * — opening a block under inline styles produces malformed XHTML even
+ * if the SAX stream happened to balance.</li>
+ * <li>svg:title / svg:desc — empty or near-empty inline elements that map
+ * to {@code <span>} via MAPPINGS. When their parent {@code <text:span>}
+ * had a bold/italic/underline style, the outer {@code <b>/<i>/<u>}
+ * is still on top of the SAX stack when the svg's {@code <span>}
+ * opens; the existing endElement closeStyleTags then tries to close
+ * {@code </b>} while the svg span is topmost, which the strict
+ * validator (correctly) rejects.</li>
+ * </ul>
* text:p / text:h / text:list / annotation / note / notes / a are handled
* by their own branches in startElement and never reach the default
* branch where this check is used.
*/
- private static boolean isBlockLevelOpen(String uri, String localName) {
- if (DRAW_NS.equals(uri) && "text-box".equals(localName)) {
+ private static boolean closeStylesBeforeOpen(String uri, String localName) {
+ if (DRAW_NS.equals(uri) && ("text-box".equals(localName) || "object".equals(localName))) {
return true;
}
if (TABLE_NS.equals(uri) &&
@@ -358,6 +370,9 @@ class OpenDocumentBodyHandler extends ElementMappingContentHandler {
|| "table-cell".equals(localName))) {
return true;
}
+ if (SVG_NS.equals(uri) && ("title".equals(localName) || "desc".equals(localName))) {
+ return true;
+ }
return TEXT_NS.equals(uri) && "list-item".equals(localName);
}
@@ -391,6 +406,11 @@ class OpenDocumentBodyHandler extends ElementMappingContentHandler {
throws SAXException {
if (DRAW_NS.equals(namespaceURI) && "image".equals(localName)) {
+ // Close any pending inline styles (e.g., a <b> opened lazily by a
+ // prior <text:span>) before emitting the <img> -- without this
+ // the <img> lands inside <b>, the styles are never flushed, and
+ // <b> reaches endDocument unbalanced.
+ closeStyleTags();
String link = attrs.getValue(XLINK_NS, "href");
AttributesImpl attr = new AttributesImpl();
if (!StringUtils.isEmpty(link)) {
@@ -486,14 +506,15 @@ class OpenDocumentBodyHandler extends ElementMappingContentHandler {
// inside it. See updateStyleTags / closeStyleTags.
anchorDepth++;
super.startElement(namespaceURI, localName, qName, attrs);
- } else if (isBlockLevelOpen(namespaceURI, localName)) {
- // Block-level structural elements (draw:text-box -> <div>,
- // table:table -> <table>, etc.) opened while <b>/<i>/<u> are
- // on top would trap those inline tags. Subsequent style flips
- // inside would emit </b> while the block is on top, producing
- // cross-nested XHTML. Close pending styles before opening the
- // block; if there's still text to emit at the same style after
- // the block closes, updateStyleTags() will reopen them.
+ } else if (closeStylesBeforeOpen(namespaceURI, localName)) {
+ // Elements that mustn't open under <b>/<i>/<u>: block-level
+ // structural elements (draw:text-box -> <div>, table:table ->
+ // <table>, etc.) and svg:title / svg:desc inline shells that
+ // map to <span>. Closing pending style tags first ensures the
+ // new element opens at body/paragraph/span level, not nested
+ // under stale inline styling. If there's still text to emit at
+ // the same style after the element closes, updateStyleTags()
+ // will reopen them.
closeStyleTags();
super.startElement(namespaceURI, localName, qName, attrs);
} else {
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/java/org/apache/tika/parser/odf/ODFParserTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/java/org/apache/tika/parser/odf/ODFParserTest.java
index cded9ea148..66df1ed41f 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/java/org/apache/tika/parser/odf/ODFParserTest.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/java/org/apache/tika/parser/odf/ODFParserTest.java
@@ -365,6 +365,35 @@ public class ODFParserTest extends TikaTest {
xml);
}
+ @Test //TIKA-4744
+ public void testFlatFodsEmbeddedChart() throws Exception {
+ // A flat ODS can embed a chart as a nested <office:document> inside
+ // <draw:object>. The dispatch handler in FlatOpenDocumentParser used
+ // to flip back to defaultHandler when the inner document's
+ // <office:meta>/<office:body> closed, sending the trailing
+ // </draw:object> / </table> / </office:body> to a no-op handler --
+ // <object>/<table> stayed open and the strict validator caught it.
+ // getXML wraps in StrictXHTMLValidator, so an imbalance throws here.
+ XMLResult r = getXML("testFODS_embeddedChart.fods");
+ // The outer <object> wrapper should close cleanly.
+ assertContains("<object>", r.xml);
+ assertContains("</object>", r.xml);
+ }
+
+ @Test //TIKA-4744
+ public void testSvgTitleInStyledSpan() throws Exception {
+ // Empty <svg:title/>/<svg:desc/> inside a <draw:connector> or
+ // <draw:custom-shape> that is itself wrapped in a styled <text:span>
+ // used to leave the SAX stack with the svg's <span> sitting above the
+ // outer <b>. The endElement closeStyleTags (TIKA-4728) then emitted
+ // </b> while <span> was topmost, which StrictXHTMLValidator rejects.
+ // getXML wraps the handler in StrictXHTMLValidator, so a desync would
+ // throw before any assertions ran.
+ String xml = getXML("testODT_svgTitleInStyledSpan.odt").xml;
+ assertContains("國立雲林科技大學國際", xml);
+ assertContains("學生簽章", xml);
+ }
+
@Test
public void testEmbedded() throws Exception {
List<Metadata> metadataList = getRecursiveMetadata("testODTEmbedded.odt");
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/resources/test-documents/testFODS_embeddedChart.fods b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/resources/test-documents/testFODS_embeddedChart.fods
new file mode 100644
index 0000000000..68b3fd6fa9
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/resources/test-documents/testFODS_embeddedChart.fods
@@ -0,0 +1,547 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns: [...]
+ <office:meta><meta:creation-date>2014-10-22T14:37:30.263776144</meta:creation-date><dc:date>2014-10-22T15:04:07.755937395</dc:date><meta:editing-duration>PT10M34S</meta:editing-duration><meta:editing-cycles>5</meta:editing-cycles><meta:generator>LibreOffice/4.3.1.2$Linux_x86 LibreOffice_project/958349dc3b25111dbca392fbc281a05559ef6848</meta:generator><meta:document-statistic meta:table-count="2" meta:cell-count="2" meta:object-count="1"/></office:meta>
+ <office:settings>
+ <config:config-item-set config:name="ooo:view-settings">
+ <config:config-item config:name="VisibleAreaTop" config:type="int">2257</config:config-item>
+ <config:config-item config:name="VisibleAreaLeft" config:type="int">2257</config:config-item>
+ <config:config-item config:name="VisibleAreaWidth" config:type="int">18062</config:config-item>
+ <config:config-item config:name="VisibleAreaHeight" config:type="int">9031</config:config-item>
+ <config:config-item-map-indexed config:name="Views">
+ <config:config-item-map-entry>
+ <config:config-item config:name="ViewId" config:type="string">view1</config:config-item>
+ <config:config-item-map-named config:name="Tables">
+ <config:config-item-map-entry config:name="Sheet1">
+ <config:config-item config:name="CursorPositionX" config:type="int">0</config:config-item>
+ <config:config-item config:name="CursorPositionY" config:type="int">0</config:config-item>
+ <config:config-item config:name="HorizontalSplitMode" config:type="short">0</config:config-item>
+ <config:config-item config:name="VerticalSplitMode" config:type="short">0</config:config-item>
+ <config:config-item config:name="HorizontalSplitPosition" config:type="int">0</config:config-item>
+ <config:config-item config:name="VerticalSplitPosition" config:type="int">0</config:config-item>
+ <config:config-item config:name="ActiveSplitRange" config:type="short">2</config:config-item>
+ <config:config-item config:name="PositionLeft" config:type="int">0</config:config-item>
+ <config:config-item config:name="PositionRight" config:type="int">0</config:config-item>
+ <config:config-item config:name="PositionTop" config:type="int">0</config:config-item>
+ <config:config-item config:name="PositionBottom" config:type="int">0</config:config-item>
+ <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
+ <config:config-item config:name="ZoomValue" config:type="int">88</config:config-item>
+ <config:config-item config:name="PageViewZoomValue" config:type="int">60</config:config-item>
+ <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
+ </config:config-item-map-entry>
+ <config:config-item-map-entry config:name="Sheet2">
+ <config:config-item config:name="CursorPositionX" config:type="int">16</config:config-item>
+ <config:config-item config:name="CursorPositionY" config:type="int">2</config:config-item>
+ <config:config-item config:name="HorizontalSplitMode" config:type="short">0</config:config-item>
+ <config:config-item config:name="VerticalSplitMode" config:type="short">0</config:config-item>
+ <config:config-item config:name="HorizontalSplitPosition" config:type="int">0</config:config-item>
+ <config:config-item config:name="VerticalSplitPosition" config:type="int">0</config:config-item>
+ <config:config-item config:name="ActiveSplitRange" config:type="short">2</config:config-item>
+ <config:config-item config:name="PositionLeft" config:type="int">0</config:config-item>
+ <config:config-item config:name="PositionRight" config:type="int">0</config:config-item>
+ <config:config-item config:name="PositionTop" config:type="int">0</config:config-item>
+ <config:config-item config:name="PositionBottom" config:type="int">0</config:config-item>
+ <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
+ <config:config-item config:name="ZoomValue" config:type="int">88</config:config-item>
+ <config:config-item config:name="PageViewZoomValue" config:type="int">60</config:config-item>
+ <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
+ </config:config-item-map-entry>
+ </config:config-item-map-named>
+ <config:config-item config:name="ActiveTable" config:type="string">Sheet2</config:config-item>
+ <config:config-item config:name="HorizontalScrollbarWidth" config:type="int">270</config:config-item>
+ <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
+ <config:config-item config:name="ZoomValue" config:type="int">88</config:config-item>
+ <config:config-item config:name="PageViewZoomValue" config:type="int">60</config:config-item>
+ <config:config-item config:name="ShowPageBreakPreview" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ShowZeroValues" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ShowNotes" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="GridColor" config:type="long">12632256</config:config-item>
+ <config:config-item config:name="ShowPageBreaks" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="HasColumnRowHeaders" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="HasSheetTabs" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="IsOutlineSymbolsSet" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="IsSnapToRaster" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="RasterIsVisible" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="RasterResolutionX" config:type="int">1000</config:config-item>
+ <config:config-item config:name="RasterResolutionY" config:type="int">1000</config:config-item>
+ <config:config-item config:name="RasterSubdivisionX" config:type="int">1</config:config-item>
+ <config:config-item config:name="RasterSubdivisionY" config:type="int">1</config:config-item>
+ <config:config-item config:name="IsRasterAxisSynchronized" config:type="boolean">true</config:config-item>
+ </config:config-item-map-entry>
+ </config:config-item-map-indexed>
+ </config:config-item-set>
+ <config:config-item-set config:name="ooo:configuration-settings">
+ <config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="IsDocumentShared" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="AutoCalculate" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="PrinterSetup" config:type="base64Binary">lQH+/0Vwc29uLVN0eWx1cy1TWDIzNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ1VQUzpFcHNvbi1TdHlsdXMtU1gyMzUAAAAAAAAAAAAWAAMAtwAAAAAAAAAEAAhSAAAEdAAASm9iRGF0YSAxCnByaW50ZXI9RXBzb24tU3R5bHVzLVNYMjM1Cm9yaWVudGF0aW9uPVBvcnRyYWl0CmNvcGllcz0xCmNvbGxhdGU9ZmFsc2UKbWFyZ2luZGFqdXN0bWVudD0wLDAsMCwwCmNvbG9yZGVwdGg9MjQKcHNsZXZ [...]
+ <config:config-item config:name="RasterIsVisible" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="PrinterName" config:type="string">Epson-Stylus-SX235</config:config-item>
+ <config:config-item config:name="RasterResolutionY" config:type="int">1000</config:config-item>
+ <config:config-item config:name="IsRasterAxisSynchronized" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
+ <config:config-item config:name="RasterResolutionX" config:type="int">1000</config:config-item>
+ <config:config-item config:name="IsSnapToRaster" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="HasColumnRowHeaders" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="RasterSubdivisionX" config:type="int">1</config:config-item>
+ <config:config-item config:name="GridColor" config:type="long">12632256</config:config-item>
+ <config:config-item config:name="ShowZeroValues" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="RasterSubdivisionY" config:type="int">1</config:config-item>
+ <config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
+ <config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="LinkUpdateMode" config:type="short">3</config:config-item>
+ <config:config-item config:name="IsOutlineSymbolsSet" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ShowPageBreaks" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="HasSheetTabs" config:type="boolean">true</config:config-item>
+ <config:config-item config:name="ShowNotes" config:type="boolean">true</config:config-item>
+ </config:config-item-set>
+ </office:settings>
+ <office:scripts>
+ <office:script script:language="ooo:Basic">
+ <ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <ooo:library-embedded ooo:name="Standard"/>
+ </ooo:libraries>
+ </office:script>
+ </office:scripts>
+ <office:font-face-decls>
+ <style:font-face style:name="Liberation Sans" svg:font-family="'Liberation Sans'" style:font-family-generic="swiss" style:font-pitch="variable"/>
+ <style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-family-generic="system" style:font-pitch="variable"/>
+ <style:font-face style:name="FreeSans" svg:font-family="FreeSans" style:font-family-generic="system" style:font-pitch="variable"/>
+ <style:font-face style:name="WenQuanYi Micro Hei" svg:font-family="'WenQuanYi Micro Hei'" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+ <style:default-style style:family="table-cell">
+ <style:paragraph-properties style:tab-stop-distance="0.4921in"/>
+ <style:text-properties style:font-name="Liberation Sans" fo:language="en" fo:country="US" style:font-name-asian="DejaVu Sans" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="DejaVu Sans" style:language-complex="hi" style:country-complex="IN"/>
+ </style:default-style>
+ <style:default-style style:family="graphic">
+ <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.1181in" draw:shadow-offset-y="0.1181in"/>
+ <style:paragraph-properties style:text-autospace="ideograph-alpha" style:punctuation-wrap="simple" style:line-break="strict" style:writing-mode="page" style:font-independent-line-spacing="false">
+ <style:tab-stops/>
+ </style:paragraph-properties>
+ <style:text-properties style:use-window-font-color="true" fo:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="DejaVu Sans" style:font-size-asian="12pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="DejaVu Sans" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/>
+ </style:default-style>
+ <number:number-style style:name="N0">
+ <number:number number:min-integer-digits="1"/>
+ </number:number-style>
+ <number:currency-style style:name="N121P0" style:volatile="true">
+ <number:number number:decimal-places="2" number:min-integer-digits="1" number:grouping="true"/>
+ <number:text> </number:text>
+ <number:currency-symbol number:language="hu" number:country="HU">Ft</number:currency-symbol>
+ </number:currency-style>
+ <number:currency-style style:name="N121">
+ <style:text-properties fo:color="#ff0000"/>
+ <number:text>-</number:text>
+ <number:number number:decimal-places="2" number:min-integer-digits="1" number:grouping="true"/>
+ <number:text> </number:text>
+ <number:currency-symbol number:language="hu" number:country="HU">Ft</number:currency-symbol>
+ <style:map style:condition="value()>=0" style:apply-style-name="N121P0"/>
+ </number:currency-style>
+ <style:style style:name="Default" style:family="table-cell">
+ <style:text-properties style:font-name-asian="WenQuanYi Micro Hei" style:font-family-asian="'WenQuanYi Micro Hei'" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-name-complex="FreeSans" style:font-family-complex="FreeSans" style:font-family-generic-complex="system" style:font-pitch-complex="variable"/>
+ </style:style>
+ <style:style style:name="Result" style:family="table-cell" style:parent-style-name="Default">
+ <style:text-properties fo:font-style="italic" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" fo:font-weight="bold"/>
+ </style:style>
+ <style:style style:name="Result2" style:family="table-cell" style:parent-style-name="Result" style:data-style-name="N121"/>
+ <style:style style:name="Heading" style:family="table-cell" style:parent-style-name="Default">
+ <style:table-cell-properties style:text-align-source="fix" style:repeat-content="false"/>
+ <style:paragraph-properties fo:text-align="center"/>
+ <style:text-properties fo:font-size="16pt" fo:font-style="italic" fo:font-weight="bold"/>
+ </style:style>
+ <style:style style:name="Heading1" style:family="table-cell" style:parent-style-name="Heading">
+ <style:table-cell-properties style:rotation-angle="90"/>
+ </style:style>
+ </office:styles>
+ <office:automatic-styles>
+ <style:style style:name="co1" style:family="table-column">
+ <style:table-column-properties fo:break-before="auto" style:column-width="0.889in"/>
+ </style:style>
+ <style:style style:name="ro1" style:family="table-row">
+ <style:table-row-properties style:row-height="0.178in" fo:break-before="auto" style:use-optimal-row-height="true"/>
+ </style:style>
+ <style:style style:name="ta1" style:family="table" style:master-page-name="Default">
+ <style:table-properties table:display="true" style:writing-mode="lr-tb"/>
+ </style:style>
+ <style:style style:name="gr1" style:family="graphic">
+ <style:graphic-properties draw:stroke="none" draw:fill="none" draw:textarea-horizontal-align="center" draw:textarea-vertical-align="middle" draw:ole-draw-aspect="1"/>
+ </style:style>
+ <style:page-layout style:name="pm1">
+ <style:page-layout-properties style:writing-mode="lr-tb"/>
+ <style:header-style>
+ <style:header-footer-properties fo:min-height="0.2953in" fo:margin-left="0in" fo:margin-right="0in" fo:margin-bottom="0.0984in"/>
+ </style:header-style>
+ <style:footer-style>
+ <style:header-footer-properties fo:min-height="0.2953in" fo:margin-left="0in" fo:margin-right="0in" fo:margin-top="0.0984in"/>
+ </style:footer-style>
+ </style:page-layout>
+ <style:page-layout style:name="pm2">
+ <style:page-layout-properties style:writing-mode="lr-tb"/>
+ <style:header-style>
+ <style:header-footer-properties fo:min-height="0.2953in" fo:margin-left="0in" fo:margin-right="0in" fo:margin-bottom="0.0984in" fo:border="2.49pt solid #000000" fo:padding="0.0071in" fo:background-color="#c0c0c0">
+ <style:background-image/>
+ </style:header-footer-properties>
+ </style:header-style>
+ <style:footer-style>
+ <style:header-footer-properties fo:min-height="0.2953in" fo:margin-left="0in" fo:margin-right="0in" fo:margin-top="0.0984in" fo:border="2.49pt solid #000000" fo:padding="0.0071in" fo:background-color="#c0c0c0">
+ <style:background-image/>
+ </style:header-footer-properties>
+ </style:footer-style>
+ </style:page-layout>
+ <style:style style:name="P1" style:family="paragraph">
+ <style:paragraph-properties fo:text-align="center"/>
+ </style:style>
+ </office:automatic-styles>
+ <office:master-styles>
+ <style:master-page style:name="Default" style:page-layout-name="pm1">
+ <style:header>
+ <text:p><text:sheet-name>???</text:sheet-name></text:p>
+ </style:header>
+ <style:header-left style:display="false"/>
+ <style:footer>
+ <text:p>Oldal <text:page-number>1</text:page-number></text:p>
+ </style:footer>
+ <style:footer-left style:display="false"/>
+ </style:master-page>
+ <style:master-page style:name="Report" style:page-layout-name="pm2">
+ <style:header>
+ <style:region-left>
+ <text:p><text:sheet-name>???</text:sheet-name> (<text:title>???</text:title>)</text:p>
+ </style:region-left>
+ <style:region-right>
+ <text:p><text:date style:data-style-name="N2" text:date-value="2014-10-22">00/00/0000</text:date>, <text:time style:data-style-name="N2" text:time-value="15:03:18.803538570">00:00:00</text:time></text:p>
+ </style:region-right>
+ </style:header>
+ <style:header-left style:display="false"/>
+ <style:footer>
+ <text:p>Oldal <text:page-number>1</text:page-number> / <text:page-count>99</text:page-count></text:p>
+ </style:footer>
+ <style:footer-left style:display="false"/>
+ </style:master-page>
+ </office:master-styles>
+ <office:body>
+ <office:spreadsheet>
+ <table:table table:name="Sheet2" table:style-name="ta1">
+ <table:shapes>
+ <draw:frame draw:z-index="0" draw:style-name="gr1" draw:text-style-name="P1" svg:width="6.2988in" svg:height="3.5429in" svg:x="0.8894in" svg:y="0.8894in">
+ <draw:object draw:notify-on-update-of-ranges="Sheet1.A1:Sheet1.A1 Sheet1.A2:Sheet1.A2">
+ <office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" [...]
+ <office:meta><meta:generator>LibreOffice/4.3.1.2$Linux_x86 LibreOffice_project/958349dc3b25111dbca392fbc281a05559ef6848</meta:generator></office:meta>
+ <office:styles/>
+ <office:automatic-styles>
+ <number:number-style style:name="N0">
+ <number:number number:min-integer-digits="1"/>
+ </number:number-style>
+ <style:style style:name="ch1" style:family="chart">
+ <style:graphic-properties draw:stroke="none"/>
+ </style:style>
+ <style:style style:name="ch2" style:family="chart">
+ <style:chart-properties chart:auto-position="true"/>
+ <style:graphic-properties draw:stroke="none" svg:stroke-color="#b3b3b3" draw:fill="none" draw:fill-color="#e6e6e6"/>
+ <style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
+ </style:style>
+ <style:style style:name="ch3" style:family="chart">
+ <style:chart-properties chart:include-hidden-cells="false" chart:auto-position="true" chart:auto-size="true" chart:treat-empty-cells="leave-gap" chart:right-angled-axes="true"/>
+ </style:style>
+ <style:style style:name="ch4" style:family="chart" style:data-style-name="N0">
+ <style:chart-properties chart:display-label="true" chart:logarithmic="false" chart:reverse-direction="false" text:line-break="false" chart:link-data-style-to-source="true" chart:axis-position="0"/>
+ <style:graphic-properties svg:stroke-color="#b3b3b3"/>
+ <style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
+ </style:style>
+ <style:style style:name="ch5" style:family="chart" style:data-style-name="N0">
+ <style:chart-properties chart:display-label="true" chart:logarithmic="false" chart:reverse-direction="false" text:line-break="false" chart:link-data-style-to-source="true" chart:axis-position="0"/>
+ <style:graphic-properties svg:stroke-color="#b3b3b3"/>
+ <style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
+ </style:style>
+ <style:style style:name="ch6" style:family="chart">
+ <style:graphic-properties svg:stroke-color="#b3b3b3"/>
+ </style:style>
+ <style:style style:name="ch7" style:family="chart" style:data-style-name="N0">
+ <style:chart-properties chart:link-data-style-to-source="true"/>
+ <style:graphic-properties draw:stroke="none" draw:fill-color="#004586" dr3d:edge-rounding="5%"/>
+ <style:text-properties fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
+ </style:style>
+ <style:style style:name="ch8" style:family="chart">
+ <style:graphic-properties draw:stroke="solid" svg:stroke-color="#b3b3b3" draw:fill="none" draw:fill-color="#e6e6e6"/>
+ </style:style>
+ <style:style style:name="ch9" style:family="chart">
+ <style:graphic-properties svg:stroke-color="#b3b3b3" draw:fill-color="#cccccc"/>
+ </style:style>
+ </office:automatic-styles>
+ <office:body>
+ <office:chart>
+ <chart:chart svg:width="16cm" svg:height="9cm" xlink:href=".." xlink:type="simple" chart:class="chart:bar" chart:style-name="ch1">
+ <chart:legend chart:legend-position="end" svg:x="15.006cm" svg:y="4.201cm" style:legend-expansion="high" chart:style-name="ch2"/>
+ <chart:plot-area chart:style-name="ch3" table:cell-range-address="Sheet1.A1:Sheet1.A2" chart:data-source-has-labels="row" svg:x="0.32cm" svg:y="0.18cm" svg:width="14.367cm" svg:height="8.64cm">
+ <chartooo:coordinate-region svg:x="1.047cm" svg:y="0.38cm" svg:width="13.64cm" svg:height="8.241cm"/>
+ <chart:axis chart:dimension="x" chart:name="primary-x" chart:style-name="ch4"/>
+ <chart:axis chart:dimension="y" chart:name="primary-y" chart:style-name="ch5">
+ <chart:grid chart:style-name="ch6" chart:class="major"/>
+ </chart:axis>
+ <chart:series chart:style-name="ch7" chart:values-cell-range-address="Sheet1.A2:Sheet1.A2" chart:label-cell-address="Sheet1.A1:Sheet1.A1" chart:class="chart:bar">
+ <chart:data-point/>
+ </chart:series>
+ <chart:wall chart:style-name="ch8"/>
+ <chart:floor chart:style-name="ch9"/>
+ </chart:plot-area>
+ <table:table table:name="local-table">
+ <table:table-header-columns>
+ <table:table-column/>
+ </table:table-header-columns>
+ <table:table-columns>
+ <table:table-column/>
+ </table:table-columns>
+ <table:table-header-rows>
+ <table:table-row>
+ <table:table-cell>
+ <text:p/>
+ </table:table-cell>
+ <table:table-cell office:value-type="string">
+ <text:p>A</text:p>
+ <draw:g>
+ <svg:desc>Sheet1.A1:Sheet1.A1</svg:desc></draw:g>
+ </table:table-cell>
+ </table:table-row>
+ </table:table-header-rows>
+ <table:table-rows>
+ <table:table-row>
+ <table:table-cell office:value-type="string"/>
+ <table:table-cell office:value-type="float" office:value="1">
+ <text:p>1</text:p>
+ <draw:g>
+ <svg:desc>Sheet1.A2:Sheet1.A2</svg:desc></draw:g>
+ </table:table-cell>
+ </table:table-row>
+ </table:table-rows>
+ </table:table>
+ </chart:chart>
+ </office:chart>
+ </office:body>
+ </office:document>
+ </draw:object><draw:image>
+ <office:binary-data>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
+ SU5UU0hBUEVfQkVHSU4AAAAAAAAAAIoAAQBBAAAAAwA7AAAADwBMaWJlcmF0aW9uIFNhbnMA
+ AAAAAABhAQAA//8AAAIABQAAAAAAAAAJBAAAAAAAAAAAAP8DAAAAAACIAAEAAgAAAAEAhwAB
+ AAUAAAD/////AIYAAQAEAAAAAAAAAHEAAgAuAAAAQQEAAGoXAAADAAAAMAAuADQAAAADAAMA
+ AAC5AAAAIwEAANwBAAADADAALgA0AAACAQATAAAACQBYVEVYVF9FT0MAAAAAAAAAAAACAQAT
+ AAAACQBYVEVYVF9FT0MBAAAAAAAAAAACAQATAAAACQBYVEVYVF9FT0MCAAAAAAAAAAACAQAT
+ AAAACQBYVEVYVF9FT0wAAAAAAAAAAAACAQATAAAACQBYVEVYVF9FT1AAAAAAAAAAAAACAQAe
+ AAAAFABYVEVYVF9QQUlOVFNIQVBFX0VORAAAAAAAAAAAAAIBACAAAAAWAFhURVhUX1BBSU5U
+ U0hBUEVfQkVHSU4AAAAAAAAAAIoAAQBBAAAAAwA7AAAADwBMaWJlcmF0aW9uIFNhbnMAAAAA
+ AABhAQAA//8AAAIABQAAAAAAAAAJBAAAAAAAAAAAAP8DAAAAAACIAAEAAgAAAAEAhwABAAUA
+ AAD/////AIYAAQAEAAAAAAAAAHEAAgAuAAAAQQEAAAwSAAADAAAAMAAuADYAAAADAAMAAAC5
+ AAAAIwEAANwBAAADADAALgA2AAACAQATAAAACQBYVEVYVF9FT0MAAAAAAAAAAAACAQATAAAA
+ CQBYVEVYVF9FT0MBAAAAAAAAAAACAQATAAAACQBYVEVYVF9FT0MCAAAAAAAAAAACAQATAAAA
+ CQBYVEVYVF9FT0wAAAAAAAAAAAACAQATAAAACQBYVEVYVF9FT1AAAAAAAAAAAAACAQAeAAAA
+ FABYVEVYVF9QQUlOVFNIQVBFX0VORAAAAAAAAAAAAAIBACAAAAAWAFhURVhUX1BBSU5UU0hB
+ UEVfQkVHSU4AAAAAAAAAAIoAAQBBAAAAAwA7AAAADwBMaWJlcmF0aW9uIFNhbnMAAAAAAABh
+ AQAA//8AAAIABQAAAAAAAAAJBAAAAAAAAAAAAP8DAAAAAACIAAEAAgAAAAEAhwABAAUAAAD/
+ ////AIYAAQAEAAAAAAAAAHEAAgAuAAAAQQEAAK8MAAADAAAAMAAuADgAAAADAAMAAAC5AAAA
+ IwEAANwBAAADADAALgA4AAACAQATAAAACQBYVEVYVF9FT0MAAAAAAAAAAAACAQATAAAACQBY
+ VEVYVF9FT0MBAAAAAAAAAAACAQATAAAACQBYVEVYVF9FT0MCAAAAAAAAAAACAQATAAAACQBY
+ VEVYVF9FT0wAAAAAAAAAAAACAQATAAAACQBYVEVYVF9FT1AAAAAAAAAAAAACAQAeAAAAFABY
+ VEVYVF9QQUlOVFNIQVBFX0VORAAAAAAAAAAAAAIBACAAAAAWAFhURVhUX1BBSU5UU0hBUEVf
+ QkVHSU4AAAAAAAAAAIoAAQBBAAAAAwA7AAAADwBMaWJlcmF0aW9uIFNhbnMAAAAAAABhAQAA
+ //8AAAIABQAAAAAAAAAJBAAAAAAAAAAAAP8DAAAAAACIAAEAAgAAAAEAhwABAAUAAAD/////
+ AIYAAQAEAAAAAAAAAHEAAgAeAAAAZAIAAFEHAAABAAAAMQAAAAEAAQAAALkAAAABADEAAAIB
+ ABMAAAAJAFhURVhUX0VPQwAAAAAAAAAAAAIBABMAAAAJAFhURVhUX0VPTAAAAAAAAAAAAAIB
+ ABMAAAAJAFhURVhUX0VPUAAAAAAAAAAAAAIBAB4AAAAUAFhURVhUX1BBSU5UU0hBUEVfRU5E
+ AAAAAAAAAAAAAgEAIAAAABYAWFRFWFRfUEFJTlRTSEFQRV9CRUdJTgAAAAAAAAAAigABAEEA
+ AAADADsAAAAPAExpYmVyYXRpb24gU2FucwAAAAAAAGEBAAD//wAAAgAFAAAAAAAAAAkEAAAA
+ AAAAAAAA/wMAAAAAAIgAAQACAAAAAQCHAAEABQAAAP////8AhgABAAQAAAAAAAAAcQACAC4A
+ AABBAQAA8wEAAAMAAAAxAC4AMgAAAAMAAwAAALkAAAAjAQAA3AEAAAMAMQAuADIAAAIBABMA
+ AAAJAFhURVhUX0VPQwAAAAAAAAAAAAIBABMAAAAJAFhURVhUX0VPQwEAAAAAAAAAAAIBABMA
+ AAAJAFhURVhUX0VPQwIAAAAAAAAAAAIBABMAAAAJAFhURVhUX0VPTAAAAAAAAAAAAAIBABMA
+ AAAJAFhURVhUX0VPUAAAAAAAAAAAAAIBAB4AAAAUAFhURVhUX1BBSU5UU0hBUEVfRU5EAAAA
+ AAAAAACFAAEABQAAAIZFAAABhAABAAUAAAAAAAAAAG8AAgA2AAAAAQAGAHw7AAD9EQAAEjsA
+ AP0RAAASOwAAKhEAAOU7AAAqEQAA5TsAAP0RAAB8OwAA/REAAAAAAAIBACAAAAAWAFhURVhU
+ X1BBSU5UU0hBUEVfQkVHSU4AAAAAAAAAAIoAAQBBAAAAAwA7AAAADwBMaWJlcmF0aW9uIFNh
+ bnMAAAAAAABhAQAA//8AAAIABQAAAAAAAAAJBAAAAAAAAAAAAP8DAAAAAACIAAEAAgAAAAEA
+ hwABAAUAAAD/////AIYAAQAEAAAAAAAAAHEAAgAeAAAASTwAAAsSAAABAAAAQQAAAAEAAQAA
+ AO4AAAABAEEAAAIBABMAAAAJAFhURVhUX0VPQwAAAAAAAAAAAAIBABMAAAAJAFhURVhUX0VP
+ TAAAAAAAAAAAAAIBABMAAAAJAFhURVhUX0VPUAAAAAAAAAAAAAIBAB4AAAAUAFhURVhUX1BB
+ SU5UU0hBUEVfRU5EAAAAAAAAAACMAAEAAAAAAIsAAQACAAAAIACCAAEAIQAAAAIAGwAAAAIA
+ AgAAAAAAAAAnIwAAAQAAAAAAfz4AAAIAAIwAAQAAAAAAjAABAAAAAAA=
+ </office:binary-data>
+ </draw:image>
+ </draw:frame></table:shapes>
+ <table:table-column table:style-name="co1" table:default-cell-style-name="Default"/>
+ <table:table-row table:style-name="ro1">
+ <table:table-cell/>
+ </table:table-row>
+ </table:table>
+ <table:table table:name="Sheet1" table:style-name="ta1">
+ <table:table-column table:style-name="co1" table:default-cell-style-name="Default"/>
+ <table:table-row table:style-name="ro1">
+ <table:table-cell office:value-type="string" calcext:value-type="string">
+ <text:p>A</text:p>
+ </table:table-cell>
+ </table:table-row>
+ <table:table-row table:style-name="ro1">
+ <table:table-cell office:value-type="float" office:value="1" calcext:value-type="float">
+ <text:p>1</text:p>
+ </table:table-cell>
+ </table:table-row>
+ </table:table>
+ <table:named-expressions/>
+ </office:spreadsheet>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/resources/test-documents/testODT_svgTitleInStyledSpan.odt b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/resources/test-documents/testODT_svgTitleInStyledSpan.odt
new file mode 100755
index 0000000000..d2f09b5d69
Binary files /dev/null and b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-miscoffice-module/src/test/resources/test-documents/testODT_svgTitleInStyledSpan.odt differ
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/XFAExtractor.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/XFAExtractor.java
index 2ca42620d2..3bc4cd83d9 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/XFAExtractor.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/XFAExtractor.java
@@ -74,77 +74,85 @@ class XFAExtractor {
void extract(InputStream xfaIs, XHTMLContentHandler xhtml, Metadata m, ParseContext context)
throws XMLStreamException, SAXException {
+ // The reader loop below can throw XMLStreamException on malformed XFA
+ // XML; the caller (AbstractPDF2XHTML.extractAcroForm) catches that and
+ // falls through to the AcroForm path, which emits more content. If the
+ // xfa_content div is left open at the throw point, that fallback
+ // content nests under it and the outer </body> can't balance. Wrap the
+ // body in try/finally so the open is always paired with a close.
xhtml.startElement("div", "class", "xfa_content");
-
- //TODO - replace this with multivalued map? This isn't
- //actually metadata, just a handy data structure.
- Metadata pdfObjRToValues = new Metadata();
-
- //for now, store and dump the fields in insertion order
- Map<String, XFAField> namedFields = new LinkedHashMap<>();
-
- //The strategy is to cache the fields in fields
- //and cache the values in pdfObjRToValues while
- //handling the text etc along the way.
- //
- //As a final step, dump the merged fields and the values.
-
- XMLStreamReader reader = XMLReaderUtils.getXMLInputFactory(context).createXMLStreamReader(xfaIs);
- while (reader.hasNext()) {
- switch (reader.next()) {
- case XMLStreamConstants.START_ELEMENT:
- QName name = reader.getName();
- String localName = name.getLocalPart();
- if (xfaTemplateMatcher.reset(name.getNamespaceURI()).find() &&
- FIELD_LN.equals(name.getLocalPart())) {
- handleField(reader, namedFields);
- } else if (XFA_DATA.equals(name)) { //full qname match is important!
- loadData(reader, pdfObjRToValues);
- } else if (textMatcher.reset(localName).find()) {
- scrapeTextUntil(reader, xhtml, name);
- }
- break;
- case XMLStreamConstants.END_ELEMENT:
- break;
+ try {
+ //TODO - replace this with multivalued map? This isn't
+ //actually metadata, just a handy data structure.
+ Metadata pdfObjRToValues = new Metadata();
+
+ //for now, store and dump the fields in insertion order
+ Map<String, XFAField> namedFields = new LinkedHashMap<>();
+
+ //The strategy is to cache the fields in fields
+ //and cache the values in pdfObjRToValues while
+ //handling the text etc along the way.
+ //
+ //As a final step, dump the merged fields and the values.
+
+ XMLStreamReader reader =
+ XMLReaderUtils.getXMLInputFactory(context).createXMLStreamReader(xfaIs);
+ while (reader.hasNext()) {
+ switch (reader.next()) {
+ case XMLStreamConstants.START_ELEMENT:
+ QName name = reader.getName();
+ String localName = name.getLocalPart();
+ if (xfaTemplateMatcher.reset(name.getNamespaceURI()).find() &&
+ FIELD_LN.equals(name.getLocalPart())) {
+ handleField(reader, namedFields);
+ } else if (XFA_DATA.equals(name)) { //full qname match is important!
+ loadData(reader, pdfObjRToValues);
+ } else if (textMatcher.reset(localName).find()) {
+ scrapeTextUntil(reader, xhtml, name);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ break;
+ }
}
- }
- if (namedFields.size() == 0) {
- xhtml.endElement("div");
- return;
- }
- //now dump fields and values
- xhtml.startElement("div", "class", "xfa_form");
- xhtml.startElement("ol");
- StringBuilder sb = new StringBuilder();
- for (Map.Entry<String, XFAField> e : namedFields.entrySet()) {
- String fieldName = e.getKey();
- XFAField field = e.getValue();
- String displayFieldName =
- (field.toolTip == null || field.toolTip.isBlank()) ? fieldName :
- field.toolTip;
- String[] fieldValues = pdfObjRToValues.getValues(fieldName);
- if (fieldValues.length == 0) {
- fieldValues = new String[]{""};
+ if (namedFields.size() == 0) {
+ return;
}
- for (String fieldValue : fieldValues) {
- AttributesImpl attrs = new AttributesImpl();
- attrs.addAttribute("", "fieldName", "fieldName", "CDATA", fieldName);
-
- sb.append(displayFieldName).append(": ");
- if (fieldValue != null) {
- sb.append(fieldValue);
+ //now dump fields and values
+ xhtml.startElement("div", "class", "xfa_form");
+ xhtml.startElement("ol");
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, XFAField> e : namedFields.entrySet()) {
+ String fieldName = e.getKey();
+ XFAField field = e.getValue();
+ String displayFieldName =
+ (field.toolTip == null || field.toolTip.isBlank()) ? fieldName :
+ field.toolTip;
+ String[] fieldValues = pdfObjRToValues.getValues(fieldName);
+ if (fieldValues.length == 0) {
+ fieldValues = new String[]{""};
}
+ for (String fieldValue : fieldValues) {
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addAttribute("", "fieldName", "fieldName", "CDATA", fieldName);
- xhtml.startElement("li", attrs);
- xhtml.characters(sb.toString());
- xhtml.endElement("li");
- sb.setLength(0);
+ sb.append(displayFieldName).append(": ");
+ if (fieldValue != null) {
+ sb.append(fieldValue);
+ }
+
+ xhtml.startElement("li", attrs);
+ xhtml.characters(sb.toString());
+ xhtml.endElement("li");
+ sb.setLength(0);
+ }
}
+ xhtml.endElement("ol");
+ xhtml.endElement("div");
+ } finally {
+ xhtml.endElement("div");
}
- xhtml.endElement("ol");
- xhtml.endElement("div");
- xhtml.endElement("div");
}
//try to scrape the text until the endElement
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
index e73471e353..ccae004def 100644
--- a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/java/org/apache/tika/parser/pdf/PDFParserTest.java
@@ -1037,6 +1037,31 @@ public class PDFParserTest extends TikaTest {
assertNotContained("Mount Rushmore National Memorial", xml);
}
+ @Test //TIKA-4744
+ public void testMalformedXFADivBalanced() throws Exception {
+ // A PDF whose XFA stream is malformed used to leave <div class=
+ // "xfa_content"> open: XFAExtractor.extract opened the div first and
+ // then threw XMLStreamException from reader.next(), the caller logged
+ // and fell through to AcroForm fallback (which emitted its own divs
+ // nested inside the unclosed xfa_content), and endDocument failed
+ // with </body> not matching topmost <div>.
+ // getXML wraps the handler in StrictXHTMLValidator, so any imbalance
+ // would throw before the assertions.
+ XMLResult r = getXML("testPDF_malformedXFA.pdf");
+ // Caller recorded the XMLStreamException as a warning rather than a
+ // fatal -- confirm that didn't change.
+ String[] warnings = r.metadata.getValues(
+ TikaCoreProperties.TIKA_META_EXCEPTION_WARNING);
+ boolean xfaWarningPresent = false;
+ for (String w : warnings) {
+ if (w.contains("XFAExtractor")) {
+ xfaWarningPresent = true;
+ break;
+ }
+ }
+ assertTrue(xfaWarningPresent, "expected an XFAExtractor warning");
+ }
+
@Test
public void testXMPMM() throws Exception {
diff --git a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/resources/test-documents/testPDF_malformedXFA.pdf b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/resources/test-documents/testPDF_malformedXFA.pdf
new file mode 100755
index 0000000000..8158f02a2e
--- /dev/null
+++ b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/test/resources/test-documents/testPDF_malformedXFA.pdf
@@ -0,0 +1,27 @@
+%PDF-1.7
+1 0 obj
+<</Parent 2 0 R/Contents 6 0 R/Type/Page/Resources<<>>/MediaBox[0 0 1 1]>>
+endobj
+2 0 obj
+<</Kids[1 0 R]/Type/Pages/Count 1>>
+endobj
+3 0 obj
+<<>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"><pdf/><template><subform use="xfa">
+endstream
+endobj
+4 0 obj
+<</XFA 3 0 R>>
+endobj
+5 0 obj
+<</AcroForm 4 0 R/Type/Catalog/Pages 2 0 R/NeedsRendering true>>
+endobj
+6 0 obj
+<</Length 0>>
+stream
+endstream
+endobj
+trailer
+<</Size 7/Root 5 0 R>>
+%%EOF
\ No newline at end of file