vcl/qa/cppunit/pdfexport/data/PDF_export_with_formcontrol.fodt | 174 ++++++ vcl/qa/cppunit/pdfexport/pdfexport.cxx | 257 ++++++++++ vcl/source/gdi/pdfextoutdevdata.cxx | 20 3 files changed, 447 insertions(+), 4 deletions(-)
New commits: commit 2e32aa1e9fc240c9cd9854655106d0decbd3694a Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Tue Oct 10 18:20:04 2023 +0200 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Thu Oct 12 11:06:10 2023 +0200 tdf#157397 vcl: PDF export: fix CreateControl replay The problem is that the CreateLink and CreateControl actions are replayed in a different order than they are recorded, because CreateLink is a global action, and CreateControl a page action. This means that the mCurId at the time when PDFExtOutDevData::CreateControl() is called does not correspond to a position in mParaIds when CreateControl is replayed; it will be inserted too early and bump all the CreateLink ones to later indexes. Avoid this by adding another global action CreateControlLink that is added when CreateControl is being replayed, which appears to work. (Another subtle problem is that, in case of PDF/A-1, the page actions could be discarded completely; this should work in that case too.) (regression from commit d4d471fc88fe4fd14f44dfccdfe360dec327d4f0) Change-Id: I92d89ac08db6548e9f0d1480d984aeacb4d22262 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157767 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/vcl/qa/cppunit/pdfexport/data/PDF_export_with_formcontrol.fodt b/vcl/qa/cppunit/pdfexport/data/PDF_export_with_formcontrol.fodt new file mode 100644 index 000000000000..4aa4a92b6710 --- /dev/null +++ b/vcl/qa/cppunit/pdfexport/data/PDF_export_with_formcontrol.fodt @@ -0,0 +1,174 @@ +<?xml version='1.0' encoding='UTF-8'?> +<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:c alcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext: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:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns: meta:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:initial-creator>A. Spielhoff</meta:initial-creator><meta:creation-date>2020-09-12T10:51:34.438117571</meta:creation-date><dc:date>2023-10-11T12:40:15.543658302</dc:date><meta:editing-duration>PT7H23M50S</meta:editing-duration><meta:editing-cycles>98</meta:editing-cycles><meta:generator>LibreOfficeDev/7.5.7.0.0$Linux_X86_64 LibreOffice_project/0325c0aa2d3e6df97ff554ca540d316273fd149a</meta:generator><meta:print-date>2023-09-23T14:07:35.317591779</meta:print-date><meta:printed-by>PDF-Dateien: A Spielhoff</meta:printed-by><meta:document-statistic meta:table-count="8" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="15" meta:word-count="129" meta:character-count="882" meta:non-whitespace-character-count="767"/></office:meta> + <office:font-face-decls> + <style:font-face style:name="Arial" svg:font-family="Arial" style:font-family-generic="swiss"/> + <style:font-face style:name="Arial2" svg:font-family="Arial" style:font-adornments="Kursiv" style:font-family-generic="swiss"/> + <style:font-face style:name="SimSun" svg:font-family="SimSun" style:font-family-generic="system" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <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" draw:start-line-spacing-horizontal="0.1114in" draw:start-line-spacing-vertical="0.1114in" draw:end-line-spacing-horizontal="0.1114in" draw:end-line-spacing-vertical="0.1114in" style:writing-mode="lr-tb" style:flow-with-text="false"/> + <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:writing-mode="lr-tb" style:font-independent-line-spacing="false"> + <style:tab-stops/> + </style:paragraph-properties> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" loext:color-lum-mod="100%" loext:color-lum-off="0%" style:font-name="Arial" fo:font-size="11pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="SimSun" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Arial" style:font-size-complex="11pt" style:language-complex="hi" style:country-complex="IN"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="0.4925in" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Arial" fo:font-size="11pt" fo:language="de" fo:country="DE" style:letter-kerning="true" style:font-name-asian="SimSun" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="Arial" style:font-size-complex="11pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="no-limit" loext:hyphenation-zone="no-limit"/> + </style:default-style> + <style:default-style style:family="table"> + <style:table-properties table:border-model="collapsing"/> + </style:default-style> + <style:default-style style:family="table-row"> + <style:table-row-properties fo:keep-together="auto"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text"> + <style:paragraph-properties fo:margin-top="0in" fo:margin-bottom="0.0972in" style:contextual-spacing="false" fo:line-height="115%"/> + </style:style> + <style:style style:name="Footnote_20_Symbol" style:display-name="Footnote Symbol" style:family="text"/> + <style:style style:name="Footnote_20_anchor" style:display-name="Footnote anchor" style:family="text"> + <style:text-properties style:text-position="super 58%"/> + </style:style> + <style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"> + <style:text-properties fo:color="#0000ff" loext:opacity="100%" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/> + </style:style> + <style:style style:name="Visited_20_Internet_20_Link" style:display-name="Visited Internet Link" style:family="text"> + <style:text-properties fo:color="#954f72" loext:opacity="100%" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/> + </style:style> + <text:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + </text:outline-style> + <text:notes-configuration text:note-class="footnote" text:citation-style-name="Footnote_20_Symbol" text:citation-body-style-name="Footnote_20_anchor" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/> + <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/> + <text:linenumbering-configuration text:number-lines="false" text:offset="0.1965in" style:num-format="1" text:number-position="left" text:increment="5"/> + </office:styles> + <office:automatic-styles> + <style:style style:name="P13" style:family="paragraph" style:parent-style-name="Text_20_body"> + <style:text-properties/> + </style:style> + <style:style style:name="P14" style:family="paragraph" style:parent-style-name="Text_20_body"> + <style:text-properties/> + </style:style> + <style:style style:name="P15" style:family="paragraph" style:parent-style-name="Text_20_body" style:master-page-name="Standard"> + <style:paragraph-properties style:page-number="auto"/> + </style:style> + <style:style style:name="P28" style:family="paragraph"> + <style:paragraph-properties fo:text-align="start"/> + <style:text-properties fo:color="#3465a4" style:text-line-through-style="none" style:text-line-through-type="none" style:font-name="Arial2" fo:font-size="11pt" fo:font-style="italic" style:text-underline-style="none"/> + </style:style> + <style:style style:name="T1" style:family="text"> + <style:text-properties/> + </style:style> + <style:style style:name="T5" style:family="text"> + <style:text-properties/> + </style:style> + <style:style style:name="T6" style:family="text"> + <style:text-properties/> + </style:style> + <style:style style:name="gr1" style:family="graphic"> + <style:graphic-properties fo:background-color="#f5f5f5" fo:border="solid #3465a4" style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" style:vertical-pos="from-top" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" draw:wrap-influence-on-position="once-concurrent" loext:allow-overlap="true" style:flow-with-text="false"/> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="8.2681in" fo:page-height="11.6929in" style:num-format="1" style:print-orientation="portrait" fo:margin-top="0.3937in" fo:margin-bottom="0.3937in" fo:margin-left="0.7874in" fo:margin-right="0.7874in" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.278in" style:layout-grid-ruby-height="0.139in" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0in" loext:margin-gutter="0in"> + <style:columns fo:column-count="1" fo:column-gap="0in"/> + <style:footnote-sep style:width="0.0071in" style:distance-before-sep="0.0398in" style:distance-after-sep="0.0398in" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/> + </style:page-layout-properties> + <style:header-style/> + <style:footer-style/> + </style:page-layout> + <style:style style:name="dp1" style:family="drawing-page"> + <style:drawing-page-properties draw:background-size="full"/> + </style:style> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1"/> + </office:master-styles> + <office:body> + <office:text> + <office:forms form:automatic-focus="false" form:apply-design-mode="false"> + <form:form form:name="Formular" form:apply-filter="true" form:command-type="table" form:control-implementation="ooo:com.sun.star.form.component.Form" office:target-frame=""> + <form:properties> + <form:property form:property-name="PropertyChangeNotificationEnabled" office:value-type="boolean" office:boolean-value="true"/> + <form:property form:property-name="TargetURL" office:value-type="string" office:string-value=""/> + </form:properties> + <form:textarea form:name="XXXX-" form:control-implementation="ooo:com.sun.star.form.component.TextField" xml:id="control1" form:id="control1" form:tab-stop="false" form:input-required="false" form:convert-empty-to-null="true"> + <form:properties> + <form:property form:property-name="ControlTypeinMSO" office:value-type="float" office:value="0"/> + <form:property form:property-name="DefaultControl" office:value-type="string" office:string-value="com.sun.star.form.control.TextField"/> + <form:property form:property-name="MultiLine" office:value-type="boolean" office:boolean-value="true"/> + <form:property form:property-name="ObjIDinMSO" office:value-type="float" office:value="65535"/> + </form:properties> + </form:textarea> + </form:form> + </office:forms> + <text:sequence-decls> + <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/> + <text:sequence-decl text:display-outline-level="0" text:name="Table"/> + <text:sequence-decl text:display-outline-level="0" text:name="Text"/> + <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/> + <text:sequence-decl text:display-outline-level="0" text:name="Figure"/> + </text:sequence-decls> + <text:p text:style-name="P15">This <text:span text:style-name="T1">t</text:span>ext document contains some links and a text control.</text:p> + <text:p text:style-name="P13"><text:span text:style-name="T1">When exporting the document to PDF in LO 7.5.</text:span><text:span text:style-name="T6">3.2</text:span><text:span text:style-name="T1"> or newer the links won't have the right target any more. First link to "Kläranlage" will open last link to "#pano=24", </text:span><text:span text:style-name="T5">second link wont open anything and third link will open "Mechanische Vorreinigung"</text:span></text:p> + <text:p text:style-name="Text_20_body"><text:a xlink:type="simple" xlink:href="https://klexikon.zum.de/wiki/Kläranlage" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link">https://klexikon.zum.de/wiki/Kläranlage</text:a></text:p> + <text:p text:style-name="Text_20_body"><text:a xlink:type="simple" xlink:href="https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link">https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung</text:a></text:p> + <text:p text:style-name="Text_20_body"><text:a xlink:type="simple" xlink:href="https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link">https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24</text:a></text:p> + <text:p text:style-name="Text_20_body">Here a form control for getting possibility to input content.</text:p> + <text:p text:style-name="Text_20_body"><draw:control text:anchor-type="as-char" svg:y="-0.3146in" draw:z-index="0" draw:name="Form1" draw:style-name="gr1" draw:text-style-name="P28" svg:width="6.3776in" svg:height="1.7717in" draw:control="control1"/></text:p> + <text:p text:style-name="Text_20_body">When deleting the form control links will work as expected.</text:p> + <text:p text:style-name="P14">Up to LO 7.5.2.2 this bug won't appear.</text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index f67deec04ae2..801a890c6c0b 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -4349,6 +4349,263 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf154982) CPPUNIT_ASSERT_EQUAL(int(2), nFigure); } +CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157397) +{ + aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + + // Enable PDF/UA + uno::Sequence<beans::PropertyValue> aFilterData( + comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } })); + aMediaDescriptor["FilterData"] <<= aFilterData; + saveAsPDF(u"PDF_export_with_formcontrol.fodt"); + + vcl::filter::PDFDocument aDocument; + SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ); + CPPUNIT_ASSERT(aDocument.Read(aStream)); + + // The document has one page. + std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size()); + + vcl::filter::PDFObjectElement* pDocument(nullptr); + for (const auto& rDocElement : aDocument.GetElements()) + { + auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get()); + if (!pObject1) + continue; + auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type")); + if (pType1 && pType1->GetValue() == "StructElem") + { + auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S")); + if (pS1 && pS1->GetValue() == "Document") + { + pDocument = pObject1; + } + } + } + CPPUNIT_ASSERT(pDocument); + + auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K")); + CPPUNIT_ASSERT(pKids1); + // assume there are no MCID ref at this level + auto pKids1v = pKids1->GetElements(); + auto pRefKid12 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[2]); + CPPUNIT_ASSERT(pRefKid12); + auto pObject12 = pRefKid12->LookupObject(); + CPPUNIT_ASSERT(pObject12); + auto pType12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType12->GetValue()); + auto pS12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS12->GetValue()); + + auto pKids12 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject12->Lookup("K")); + CPPUNIT_ASSERT(pKids12); + // assume there are no MCID ref at this level + auto pKids12v = pKids12->GetElements(); + auto pRefKid120 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids12v[0]); + CPPUNIT_ASSERT(pRefKid120); + auto pObject120 = pRefKid120->LookupObject(); + CPPUNIT_ASSERT(pObject120); + auto pType120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType120->GetValue()); + auto pS120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pS120->GetValue()); + + { + auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject120->Lookup("K")); + auto nMCID(0); + auto nRef(0); + for (size_t i = 0; i < pKids->GetElements().size(); ++i) + { + auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i)); + auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i)); + if (pNum) + { + ++nMCID; + } + if (pRef) + { + ++nRef; + auto pObjR = pRef->LookupObject(); + auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue()); + auto pAnnotRef + = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj")); + auto pAnnot = pAnnotRef->LookupObject(); + auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue()); + auto pASubtype + = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype")); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue()); + auto pAContents + = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents")); + CPPUNIT_ASSERT_EQUAL( + u"https://klexikon.zum.de/wiki/Kläranlage"_ustr, + ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents)); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue()); + auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A")); + CPPUNIT_ASSERT(pAA); + auto pAAType + = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue()); + auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S")); + CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue()); + auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>( + pAA->LookupElement("URI")); + CPPUNIT_ASSERT_EQUAL(OString("https://klexikon.zum.de/wiki/Kl%C3%A4ranlage"), + pAAURI->GetValue()); + } + } + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID); + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef); + } + + auto pRefKid13 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[3]); + CPPUNIT_ASSERT(pRefKid13); + auto pObject13 = pRefKid13->LookupObject(); + CPPUNIT_ASSERT(pObject13); + auto pType13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType13->GetValue()); + auto pS13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS13->GetValue()); + + auto pKids13 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject13->Lookup("K")); + CPPUNIT_ASSERT(pKids13); + // assume there are no MCID ref at this level + auto pKids13v = pKids13->GetElements(); + auto pRefKid130 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids13v[0]); + CPPUNIT_ASSERT(pRefKid130); + auto pObject130 = pRefKid130->LookupObject(); + CPPUNIT_ASSERT(pObject130); + auto pType130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType130->GetValue()); + auto pS130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pS130->GetValue()); + + { + auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject130->Lookup("K")); + auto nMCID(0); + auto nRef(0); + for (size_t i = 0; i < pKids->GetElements().size(); ++i) + { + auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i)); + auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i)); + if (pNum) + { + ++nMCID; + } + if (pRef) + { + ++nRef; + auto pObjR = pRef->LookupObject(); + auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue()); + auto pAnnotRef + = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj")); + auto pAnnot = pAnnotRef->LookupObject(); + auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue()); + auto pASubtype + = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype")); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue()); + auto pAContents + = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents")); + CPPUNIT_ASSERT_EQUAL( + u"https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung"_ustr, + ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents)); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue()); + auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A")); + CPPUNIT_ASSERT(pAA); + auto pAAType + = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue()); + auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S")); + CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue()); + auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>( + pAA->LookupElement("URI")); + CPPUNIT_ASSERT_EQUAL( + OString( + "https://de.wikipedia.org/wiki/Kl%C3%A4ranlage#Mechanische_Vorreinigung"), + pAAURI->GetValue()); + } + } + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID); + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef); + } + + auto pRefKid14 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[4]); + CPPUNIT_ASSERT(pRefKid14); + auto pObject14 = pRefKid14->LookupObject(); + CPPUNIT_ASSERT(pObject14); + auto pType14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType14->GetValue()); + auto pS14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS14->GetValue()); + + auto pKids14 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject14->Lookup("K")); + CPPUNIT_ASSERT(pKids14); + // assume there are no MCID ref at this level + auto pKids14v = pKids14->GetElements(); + auto pRefKid140 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids14v[0]); + CPPUNIT_ASSERT(pRefKid140); + auto pObject140 = pRefKid140->LookupObject(); + CPPUNIT_ASSERT(pObject140); + auto pType140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType140->GetValue()); + auto pS140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pS140->GetValue()); + + { + auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject140->Lookup("K")); + auto nMCID(0); + auto nRef(0); + for (size_t i = 0; i < pKids->GetElements().size(); ++i) + { + auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i)); + auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i)); + if (pNum) + { + ++nMCID; + } + if (pRef) + { + ++nRef; + auto pObjR = pRef->LookupObject(); + auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue()); + auto pAnnotRef + = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj")); + auto pAnnot = pAnnotRef->LookupObject(); + auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue()); + auto pASubtype + = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype")); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue()); + auto pAContents + = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents")); + CPPUNIT_ASSERT_EQUAL( + u"https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"_ustr, + ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents)); + CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue()); + auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A")); + CPPUNIT_ASSERT(pAA); + auto pAAType + = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue()); + auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S")); + CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue()); + auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>( + pAA->LookupElement("URI")); + CPPUNIT_ASSERT_EQUAL( + OString("https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"), + pAAURI->GetValue()); + } + } + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID); + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef); + } +} + CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135192) { aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); diff --git a/vcl/source/gdi/pdfextoutdevdata.cxx b/vcl/source/gdi/pdfextoutdevdata.cxx index a4184c8cefa7..8bc7e044002e 100644 --- a/vcl/source/gdi/pdfextoutdevdata.cxx +++ b/vcl/source/gdi/pdfextoutdevdata.cxx @@ -42,6 +42,7 @@ struct PDFExtOutDevDataSync { enum Action{ CreateNamedDest, CreateDest, + CreateControlLink, CreateLink, CreateScreen, SetLinkDest, @@ -95,6 +96,7 @@ struct GlobalSyncData std::deque< PDFNote > mParaPDFNotes; std::deque< PDFWriter::PageTransition > mParaPageTransitions; ::std::map< sal_Int32, PDFLinkDestination > mFutureDestinations; + ::std::deque<sal_Int32> mControlIds; sal_Int32 GetMappedId(); @@ -169,6 +171,14 @@ void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter ) rWriter.Pop(); } break; + case PDFExtOutDevDataSync::CreateControlLink: + { + // tdf#157397: this must be called *in order* with CreateLink etc. + rWriter.SetLinkPropertyID(mControlIds.front(), sal_Int32(mParaIds.size())); + mParaIds.push_back(mControlIds.front()); + mControlIds.pop_front(); + } + break; case PDFExtOutDevDataSync::CreateLink : { rWriter.Push( PushFlags::MAPMODE ); @@ -426,10 +436,12 @@ bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAc { sal_Int32 const n = rWriter.CreateControl(*pControl); // resolve AnnotIds structural attribute - ::std::vector<sal_Int32> const annotIds{ sal_Int32(mpGlobalData->mParaIds.size()) }; + ::std::vector<sal_Int32> const annotIds{ sal_Int32(mpGlobalData->mCurId) }; rWriter.SetStructureAnnotIds(annotIds); - rWriter.SetLinkPropertyID(n, sal_Int32(mpGlobalData->mParaIds.size())); - mpGlobalData->mParaIds.push_back(n); + // tdf#157397: this must be called *in order* with CreateLink etc. + mpGlobalData->mActions.push_back(PDFExtOutDevDataSync::CreateControlLink); + mpGlobalData->mControlIds.push_back(n); + mpGlobalData->mCurId++; } mControls.pop_front(); } @@ -534,6 +546,7 @@ bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, sal_uInt32& rCurGDIMtfAc break; case PDFExtOutDevDataSync::CreateNamedDest: case PDFExtOutDevDataSync::CreateDest: + case PDFExtOutDevDataSync::CreateControlLink: case PDFExtOutDevDataSync::CreateLink: case PDFExtOutDevDataSync::CreateScreen: case PDFExtOutDevDataSync::SetLinkDest: @@ -926,7 +939,6 @@ void PDFExtOutDevData::CreateControl( const PDFWriter::AnyWidget& rControlType ) std::shared_ptr< PDFWriter::AnyWidget > pClone( rControlType.Clone() ); mpPageSyncData->mControls.push_back( pClone ); - mpGlobalSyncData->mCurId++; } void PDFExtOutDevData::BeginGroup()