drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx |   19 +
 include/vcl/pdfwriter.hxx                                  |    4 
 vcl/qa/cppunit/pdfexport/data/formcontrol.fodt             |  192 +++++++++++++
 vcl/qa/cppunit/pdfexport/pdfexport.cxx                     |  132 ++++++++
 vcl/source/gdi/pdfwriter_impl.cxx                          |   72 ++--
 5 files changed, 387 insertions(+), 32 deletions(-)

New commits:
commit 2a87a59a52ae26db5106f7d1f4346225d032b550
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Mon Mar 27 12:51:24 2023 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Tue Apr 4 15:01:15 2023 +0200

    tdf#152234 vcl,drawinglayer: PDF/UA export: produce Role for form controls
    
    veraPDF complains:
    
      Specification: ISO 14289-1:2014, Clause: 7.18.4, Test number: 2
      If the Form element omits a Role attribute (Table 348), it shall have
      only one child: an object reference (14.7.4.3) identifying the widget
      annotation per ISO 32000-1:2008, 14.8.4.5, Table 340.
    
    LO forms produce both page content in an MCID and an /Annot, so Role is
    needed.
    
    Change-Id: Ic231931a7c35d8da37ca76e02d97501edb43347c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149626
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 56675ff113ef..58396ba0c73b 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -1158,6 +1158,25 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
 
             mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
             OUString const& rAltText(rControlPrimitive.GetAltText());
+            vcl::PDFWriter::StructAttributeValue role;
+            switch (pPDFControl->Type)
+            {
+                case vcl::PDFWriter::PushButton:
+                    role = vcl::PDFWriter::Pb;
+                    break;
+                case vcl::PDFWriter::RadioButton:
+                    role = vcl::PDFWriter::Rb;
+                    break;
+                case vcl::PDFWriter::CheckBox:
+                    role = vcl::PDFWriter::Cb;
+                    break;
+                default: // there is a paucity of roles, tv is the catch-all 
one
+                    role = vcl::PDFWriter::Tv;
+                    break;
+            }
+            // ISO 14289-1:2014, Clause: 7.18.4
+            mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Role, 
role);
+            // ISO 14289-1:2014, Clause: 7.18.1
             if (!rAltText.isEmpty())
             {
                 mpPDFExtOutDevData->SetAlternateText(rAltText);
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index b16e7120b398..0d959f9add34 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -141,7 +141,7 @@ public:
         Placement, WritingMode, SpaceBefore, SpaceAfter, StartIndent, 
EndIndent,
         TextIndent, TextAlign, Width, Height, BlockAlign, InlineAlign,
         LineHeight, BaselineShift, TextDecorationType, ListNumbering,
-        RowSpan, ColSpan, Scope,
+        RowSpan, ColSpan, Scope, Role,
 
         // link destination is an artificial attribute that sets
         // the link annotation ID of a Link element
@@ -180,6 +180,8 @@ public:
         Underline, Overline, LineThrough,
         // Scope
         Row, Column, Both,
+        // Role
+        Rb, Cb, Pb, Tv,
         // ListNumbering
         Disc, Circle, Square, Decimal, UpperRoman, LowerRoman, UpperAlpha, 
LowerAlpha
     };
diff --git a/vcl/qa/cppunit/pdfexport/data/formcontrol.fodt 
b/vcl/qa/cppunit/pdfexport/data/formcontrol.fodt
new file mode 100644
index 000000000000..f6ec84585217
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/formcontrol.fodt
@@ -0,0 +1,192 @@
+<?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:creation-date>2023-04-03T17:28:30.031698782</meta:creation-date><dc:date>2023-04-03T17:48:01.654889994</dc:date><meta:editing-duration>PT19M34S</meta:editing-duration><meta:editing-cycles>3</meta:editing-cycles><meta:generator>LibreOfficeDev/7.6.0.0.alpha0$Linux_X86_64
 
LibreOffice_project/581bc338cb60e9511c2f870acfbb7ec3593a582d</meta:generator><dc:title>dummy</dc:title><meta:document-statistic
 meta:table-count="0" meta:image-count="0" meta:object-count="0" 
meta:page-count="1" meta:paragraph-count="0" meta:word-count="0" 
meta:character-count="0" meta:non-whitespace-character-count="0"/></office:meta>
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation 
Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
+  <style:font-face style:name="Lohit Devanagari1" svg:font-family="'Lohit 
Devanagari'" style:font-family-generic="system" style:font-pitch="variable"/>
+  <style:font-face style:name="Source Han Serif CN" svg:font-family="'Source 
Han Serif CN'" 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.3cm" 
draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" 
draw:start-line-spacing-vertical="0.283cm" 
draw:end-line-spacing-horizontal="0.283cm" 
draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" 
style:line-break="strict" loext:tab-stop-distance="0cm" 
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%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="de" fo:country="DE" style:letter-kerning="true" 
style:font-name-asian="Source Han Serif CN" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Lohit Devanagari1" style:font-size-complex="12pt" 
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="1.251cm" style:writing-mode="page"/>
+   <style:text-properties style:use-window-font-color="true" 
loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" 
fo:language="de" fo:country="DE" style:letter-kerning="true" 
style:font-name-asian="Source Han Serif CN" style:font-size-asian="10.5pt" 
style:language-asian="zh" style:country-asian="CN" 
style:font-name-complex="Lohit Devanagari1" style:font-size-complex="12pt" 
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="5" 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"/>
+  <text:outline-style style:name="Outline">
+   <text:outline-level-style text:level="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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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.499cm" style:num-format="1" text:number-position="left" 
text:increment="5"/>
+  <loext:theme loext:name="Office">
+   <loext:color-table loext:name="LibreOffice">
+    <loext:color loext:name="dk1" loext:color="#000000"/>
+    <loext:color loext:name="lt1" loext:color="#ffffff"/>
+    <loext:color loext:name="dk2" loext:color="#000000"/>
+    <loext:color loext:name="lt2" loext:color="#ffffff"/>
+    <loext:color loext:name="accent1" loext:color="#18a303"/>
+    <loext:color loext:name="accent2" loext:color="#0369a3"/>
+    <loext:color loext:name="accent3" loext:color="#a33e03"/>
+    <loext:color loext:name="accent4" loext:color="#8e03a3"/>
+    <loext:color loext:name="accent5" loext:color="#c99c00"/>
+    <loext:color loext:name="accent6" loext:color="#c9211e"/>
+    <loext:color loext:name="hlink" loext:color="#0000ee"/>
+    <loext:color loext:name="folHlink" loext:color="#551a8b"/>
+   </loext:color-table>
+  </loext:theme>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph">
+   <style:paragraph-properties fo:text-align="start"/>
+  </style:style>
+  <style:style style:name="gr1" style:family="graphic">
+   <style:graphic-properties draw:textarea-vertical-align="middle" 
style:wrap="run-through" style:number-wrapped-paragraphs="no-limit" 
style:vertical-pos="from-top" style:vertical-rel="paragraph" 
style:horizontal-pos="from-left" style:horizontal-rel="paragraph"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="21.001cm" 
fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" 
fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
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:page-layout style:name="pm2" style:page-usage="left">
+   <style:page-layout-properties fo:page-width="21.001cm" 
fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" 
fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
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:page-layout style:name="pm3" style:page-usage="right">
+   <style:page-layout-properties fo:page-width="21.001cm" 
fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" 
fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
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:page-layout style:name="pm4">
+   <style:page-layout-properties fo:page-width="22.901cm" 
fo:page-height="11.4cm" style:num-format="1" 
style:print-orientation="landscape" fo:margin-top="0cm" fo:margin-bottom="0cm" 
fo:margin-left="0cm" fo:margin-right="0cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
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:page-layout style:name="pm5">
+   <style:page-layout-properties fo:page-width="21.001cm" 
fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="1cm" fo:margin-bottom="1cm" fo:margin-left="2cm" 
fo:margin-right="1cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
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:page-layout style:name="pm6">
+   <style:page-layout-properties fo:page-width="21.001cm" 
fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" 
fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep 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:page-layout style:name="pm7">
+   <style:page-layout-properties fo:page-width="29.7cm" 
fo:page-height="21.001cm" style:num-format="1" 
style:print-orientation="landscape" fo:margin-top="2cm" fo:margin-bottom="2cm" 
fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm" loext:margin-gutter="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
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>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+  </office:master-styles>
+ <office:body>
+  <office:text>
+   <office:forms form:automatic-focus="false" form:apply-design-mode="false">
+    <form:form form:name="Form" 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:checkbox form:name="Check Box 1" 
form:control-implementation="ooo:com.sun.star.form.component.CheckBox" 
xml:id="control1" form:id="control1" form:label="Check Box" 
form:input-required="false" form:image-position="center">
+      <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.CheckBox"/>
+       <form:property form:property-name="HelpText" office:value-type="string" 
office:string-value="helpful text"/>
+       <form:property form:property-name="ObjIDinMSO" 
office:value-type="float" office:value="65535"/>
+       <form:property form:property-name="SecondaryRefValue" 
office:value-type="string" office:string-value=""/>
+      </form:properties>
+     </form:checkbox>
+    </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="Standard"><draw:control 
text:anchor-type="paragraph" draw:z-index="0" draw:name="Control 1" 
draw:style-name="gr1" draw:text-style-name="P1" svg:width="3.003cm" 
svg:height="1.04cm" svg:x="1.381cm" svg:y="0.651cm" draw:control="control1">
+     <svg:title>textuelle alternative</svg:title>
+     <svg:desc>a box to check</svg:desc>
+    </draw:control></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 448ba4ba24ed..4f24545a0829 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -3703,6 +3703,138 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testMediaShapeAnnot)
     CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
 }
 
+CPPUNIT_TEST_FIXTURE(PdfExportTest, testFormControlAnnot)
+{
+    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"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());
+
+    auto pAnnots = 
dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+    CPPUNIT_ASSERT(pAnnots);
+
+    // There should be one annotation
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
pAnnots->GetElements().size());
+    auto pAnnotReference
+        = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
+    CPPUNIT_ASSERT(pAnnotReference);
+    // check /Annot
+    vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+    CPPUNIT_ASSERT(pAnnot);
+    CPPUNIT_ASSERT_EQUAL(
+        OString("Annot"),
+        
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+    CPPUNIT_ASSERT_EQUAL(
+        OString("Widget"),
+        
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+    auto pT = 
dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAnnot->Lookup("T"));
+    CPPUNIT_ASSERT(pT);
+    CPPUNIT_ASSERT_EQUAL(OString("Check Box 1"), pT->GetValue());
+    auto pTU = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("TU"));
+    CPPUNIT_ASSERT(pTU);
+    CPPUNIT_ASSERT_EQUAL(OUString("helpful text"),
+                         
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pTU));
+
+    auto pStructParent
+        = 
dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
+    CPPUNIT_ASSERT(pStructParent);
+
+    vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
+
+    // check ParentTree to find StructElem
+    auto nRoots(0);
+    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() == "StructTreeRoot")
+        {
+            ++nRoots;
+            auto pParentTree
+                = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
+            CPPUNIT_ASSERT(pParentTree);
+            auto pNumTree = pParentTree->LookupObject();
+            CPPUNIT_ASSERT(pNumTree);
+            auto pNums = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
+            CPPUNIT_ASSERT(pNums);
+            auto nFound(0);
+            for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
+            {
+                auto pI = 
dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
+                if (pI->GetValue() == pStructParent->GetValue())
+                {
+                    ++nFound;
+                    CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
+                    pStructElemRef
+                        = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
+                    CPPUNIT_ASSERT(pStructElemRef);
+                }
+            }
+            CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
+
+    // check /StructElem
+    CPPUNIT_ASSERT(pStructElemRef);
+    auto pStructElem(pStructElemRef->LookupObject());
+    CPPUNIT_ASSERT(pStructElem);
+
+    auto pType = 
dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
+    CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
+    auto pS = 
dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
+    CPPUNIT_ASSERT_EQUAL(OString("Form"), pS->GetValue());
+    auto pAlt = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
+    CPPUNIT_ASSERT_EQUAL(OUString("textuelle alternative - a box to check"),
+                         
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
+    auto pA = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pStructElem->Lookup("A"));
+    CPPUNIT_ASSERT(pA);
+    auto pAObj = pA->LookupObject();
+    auto pO = dynamic_cast<vcl::filter::PDFNameElement*>(pAObj->Lookup("O"));
+    CPPUNIT_ASSERT(pO);
+    CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pO->GetValue());
+    auto pRole = 
dynamic_cast<vcl::filter::PDFNameElement*>(pAObj->Lookup("Role"));
+    CPPUNIT_ASSERT(pRole);
+    CPPUNIT_ASSERT_EQUAL(OString("Cb"), pRole->GetValue());
+    auto pKids = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->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"));
+            CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+}
+
 CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf142129)
 {
     loadFromURL(u"master.odm");
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 242172313940..6415ac76e90e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -1859,6 +1859,7 @@ const char* PDFWriterImpl::getAttributeTag( 
PDFWriter::StructAttribute eAttr )
         aAttributeStrings[ PDFWriter::RowSpan ]             = "RowSpan";
         aAttributeStrings[ PDFWriter::ColSpan ]             = "ColSpan";
         aAttributeStrings[ PDFWriter::Scope ]               = "Scope";
+        aAttributeStrings[ PDFWriter::Role ]                = "Role";
         aAttributeStrings[ PDFWriter::Type ]                = "Type";
         aAttributeStrings[ PDFWriter::Subtype ]             = "Subtype";
         aAttributeStrings[ PDFWriter::LinkAnnotation ]      = "LinkAnnotation";
@@ -1907,6 +1908,10 @@ const char* PDFWriterImpl::getAttributeValueTag( 
PDFWriter::StructAttributeValue
         aValueStrings[ PDFWriter::Header ]                  = "Header";
         aValueStrings[ PDFWriter::Footer ]                  = "Footer";
         aValueStrings[ PDFWriter::Watermark ]               = "Watermark";
+        aValueStrings[ PDFWriter::Rb ]                      = "Rb";
+        aValueStrings[ PDFWriter::Cb ]                      = "Cb";
+        aValueStrings[ PDFWriter::Pb ]                      = "Pb";
+        aValueStrings[ PDFWriter::Tv ]                      = "Tv";
         aValueStrings[ PDFWriter::Disc ]                    = "Disc";
         aValueStrings[ PDFWriter::Circle ]                  = "Circle";
         aValueStrings[ PDFWriter::Square ]                  = "Square";
@@ -1977,10 +1982,15 @@ OString PDFWriterImpl::emitStructureAttributes( 
PDFStructureElement& i_rEle )
 {
     // create layout, list and table attribute sets
     OStringBuffer aLayout(256), aList(64), aTable(64);
+    OStringBuffer aPrintField;
     for (auto const& attribute : i_rEle.m_aAttributes)
     {
         if( attribute.first == PDFWriter::ListNumbering )
             appendStructureAttributeLine( attribute.first, attribute.second, 
aList, true );
+        else if (attribute.first == PDFWriter::Role)
+        {
+            appendStructureAttributeLine(attribute.first, attribute.second, 
aPrintField, true);
+        }
         else if( attribute.first == PDFWriter::RowSpan ||
                  attribute.first == PDFWriter::ColSpan ||
                  attribute.first == PDFWriter::Scope)
@@ -2028,47 +2038,37 @@ OString PDFWriterImpl::emitStructureAttributes( 
PDFStructureElement& i_rEle )
     }
 
     std::vector< sal_Int32 > aAttribObjects;
-    if( !aLayout.isEmpty() )
+    auto const WriteAttrs = [&](char const*const pName, OStringBuffer & rBuf)
     {
         aAttribObjects.push_back( createObject() );
         if (updateObject( aAttribObjects.back() ))
         {
-            OString aObj =
-                OString::number(aAttribObjects.back())
-                + " 0 obj\n"
-                  "<</O/Layout\n";
-            aLayout.append( ">>\nendobj\n\n" );
-            writeBuffer( aObj );
-            writeBuffer( aLayout );
-        }
+            OStringBuffer aObj( 64 );
+            aObj.append( aAttribObjects.back() );
+            aObj.append( " 0 obj\n"
+                         "<</O");
+            aObj.append(pName);
+            aObj.append("\n");
+            rBuf.append(">>\nendobj\n\n");
+            writeBuffer(aObj);
+            writeBuffer(rBuf);
+        }
+    };
+    if( !aLayout.isEmpty() )
+    {
+        WriteAttrs("/Layout", aLayout);
     }
     if( !aList.isEmpty() )
     {
-        aAttribObjects.push_back( createObject() );
-        if (updateObject( aAttribObjects.back() ))
-        {
-            OString aObj =
-                OString::number( aAttribObjects.back() )
-                + " 0 obj\n"
-                  "<</O/List\n";
-            aList.append( ">>\nendobj\n\n" );
-            writeBuffer( aObj );
-            writeBuffer( aList );
-        }
+        WriteAttrs("/List", aList);
+    }
+    if (!aPrintField.isEmpty())
+    {
+        WriteAttrs("/PrintField", aPrintField);
     }
     if( !aTable.isEmpty() )
     {
-        aAttribObjects.push_back( createObject() );
-        if (updateObject( aAttribObjects.back() ))
-        {
-            OString aObj =
-                OString::number( aAttribObjects.back() )
-                + " 0 obj\n"
-                  "<</O/Table\n";
-            aTable.append( ">>\nendobj\n\n" );
-            writeBuffer( aObj );
-            writeBuffer( aTable );
-        }
+        WriteAttrs("/Table", aTable);
     }
 
     OStringBuffer aRet( 64 );
@@ -11170,6 +11170,16 @@ bool PDFWriterImpl::setStructureAttribute( enum 
PDFWriter::StructAttribute eAttr
                     }
                 }
                 break;
+            case PDFWriter::Role:
+                if (eVal == PDFWriter::Rb || eVal == PDFWriter::Cb || eVal == 
PDFWriter::Pb || eVal == PDFWriter::Tv)
+                {
+                    if (eType == PDFWriter::Form
+                        && PDFWriter::PDFVersion::PDF_1_7 <= 
m_aContext.Version)
+                    {
+                        bInsert = true;
+                    }
+                }
+                break;
             case PDFWriter::ListNumbering:
                 if( eVal == PDFWriter::NONE         ||
                     eVal == PDFWriter::Disc         ||

Reply via email to