include/vcl/filter/PDFiumLibrary.hxx    |    1 
 sw/qa/core/text/itrform2.cxx            |   56 ++++++++++++++++++++++++++++++++
 sw/source/core/text/itrform2.cxx        |   19 ++++++++++
 vcl/qa/cppunit/pdfexport/pdfexport2.cxx |   10 ++---
 vcl/source/pdf/PDFiumLibrary.cxx        |    6 +++
 5 files changed, 87 insertions(+), 5 deletions(-)

New commits:
commit b3e01bd3bdd37961057026ab7f2b57cbfab35d4a
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Jun 28 08:42:37 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Jun 28 09:40:34 2024 +0200

    Related: tdf#161708 sw content controls: fix custom default of dropdown to 
PDF
    
    Export the bugdoc to PDF without noticing that there is a content
    control around some of the content and notice how the PDF export lacks
    some words from the body text.
    
    What happens is that content controls are exported to PDF as PDF forms
    by default, and the selected option of a dropdown has to be an index, so
    in case the text of the dropdown content control is not one of the
    options, then the PDF will miss those words.
    
    Fix the problem by inserting the text of the dropdown at the start if
    there would be no valid index for it.
    
    Also add a bit of padding around the rectangle of the content controls,
    it turns out there is a default 1pt border in PDF, and this would lead
    to a cut-off text at the end if we don't compensate for that border.
    
    Change-Id: I99447894b320b42ad9ffe0d54d0190000621616b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169694
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/include/vcl/filter/PDFiumLibrary.hxx 
b/include/vcl/filter/PDFiumLibrary.hxx
index a5d757414429..6d2a65a8fa38 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -117,6 +117,7 @@ public:
                                                        PDFAnnotAActionType 
eEvent)
         = 0;
     virtual OUString getFormFieldValue(PDFiumDocument* pDoc) = 0;
+    virtual int getOptionCount(PDFiumDocument* pDoc) = 0;
 };
 
 class PDFiumTextPage;
diff --git a/sw/qa/core/text/itrform2.cxx b/sw/qa/core/text/itrform2.cxx
index 8753a797e9f8..5612863a0ddf 100644
--- a/sw/qa/core/text/itrform2.cxx
+++ b/sw/qa/core/text/itrform2.cxx
@@ -11,6 +11,9 @@
 
 #include <memory>
 
+#include <com/sun/star/text/XTextDocument.hpp>
+
+#include <comphelper/propertyvalue.hxx>
 #include <editeng/colritem.hxx>
 
 #include <IDocumentLayoutAccess.hxx>
@@ -258,6 +261,59 @@ CPPUNIT_TEST_FIXTURE(Test, testContentControlPDFFontColor)
     // i.e. the custom color was lost, the font color was black, not orange.
     CPPUNIT_ASSERT_EQUAL(nOrange, 
pAnnotation->getFontColor(pPdfDocument.get()));
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testContentControlPDFDropDownText)
+{
+    std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+    if (!pPDFium)
+        return;
+
+    // Given a document with a dropdown: custom default text and 3 items:
+    createSwDoc();
+    uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<text::XTextDocument> xTextDocument(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<text::XText> xText = xTextDocument->getText();
+    uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+    xText->insertString(xCursor, u"test"_ustr, /*bAbsorb=*/false);
+    xCursor->gotoStart(/*bExpand=*/false);
+    xCursor->gotoEnd(/*bExpand=*/true);
+    uno::Reference<text::XTextContent> xContentControl(
+        xMSF->createInstance(u"com.sun.star.text.ContentControl"_ustr), 
uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, 
uno::UNO_QUERY);
+    {
+        uno::Sequence<beans::PropertyValues> aListItems = {
+            {
+                comphelper::makePropertyValue(u"DisplayText"_ustr, 
uno::Any(u"red"_ustr)),
+                comphelper::makePropertyValue(u"Value"_ustr, 
uno::Any(u"R"_ustr)),
+            },
+            {
+                comphelper::makePropertyValue(u"DisplayText"_ustr, 
uno::Any(u"green"_ustr)),
+                comphelper::makePropertyValue(u"Value"_ustr, 
uno::Any(u"G"_ustr)),
+            },
+            {
+                comphelper::makePropertyValue(u"DisplayText"_ustr, 
uno::Any(u"blue"_ustr)),
+                comphelper::makePropertyValue(u"Value"_ustr, 
uno::Any(u"B"_ustr)),
+            },
+        };
+        xContentControlProps->setPropertyValue(u"ListItems"_ustr, 
uno::Any(aListItems));
+    }
+    xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+    // When exporting that to PDF:
+    save(u"writer_pdf_Export"_ustr);
+
+    // Then make sure that the custom default is not lost:
+    std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+    std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0);
+    pPage->onAfterLoadPage(pPdfDocument.get());
+    CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationCount());
+    std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnotation = 
pPage->getAnnotation(0);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 4
+    // - Actual  : 3
+    // i.e. only the 3 colors were exported, the default "test" text was not.
+    CPPUNIT_ASSERT_EQUAL(4, pAnnotation->getOptionCount(pPdfDocument.get()));
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 621294528767..bfb7c6ed7a2c 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1038,13 +1038,24 @@ bool SwContentControlPortion::DescribePDFControl(const 
SwTextPaintInfo& rInf) co
             auto pListWidget = 
static_cast<vcl::PDFWriter::ListBoxWidget*>(pDescriptor.get());
             pListWidget->DropDown = true;
             sal_Int32 nIndex = 0;
+            bool bTextFound = false;
             for (const auto& rItem : pContentControl->GetListItems())
             {
                 pListWidget->Entries.push_back(rItem.m_aDisplayText);
                 if (rItem.m_aDisplayText == aText)
+                {
                     pListWidget->SelectedEntries.push_back(nIndex);
+                    bTextFound = true;
+                }
                 ++nIndex;
             }
+            if (!aText.isEmpty() && !bTextFound)
+            {
+                // The selected entry has to be an index, if there is no index 
for it, insert one at
+                // the start.
+                pListWidget->Entries.insert(pListWidget->Entries.begin(), 
aText);
+                pListWidget->SelectedEntries.push_back(0);
+            }
             break;
         }
         case SwContentControlType::COMBO_BOX:
@@ -1116,6 +1127,14 @@ bool SwContentControlPortion::DescribePDFControl(const 
SwTextPaintInfo& rInf) co
 
     aLocation = aStartRect;
     aLocation.Union(aEndRect);
+
+    // PDF spec 12.5.2 Annotation Dictionaries says the default border with is 
1pt wide, increase
+    // the rectangle to compensate for that, otherwise the text will be cut 
off at the end.
+    aLocation.AddTop(-20);
+    aLocation.AddBottom(20);
+    aLocation.AddLeft(-20);
+    aLocation.AddRight(20);
+
     pDescriptor->Location = aLocation.SVRect();
 
     pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
index 6c0f39e977bd..fb7ad3f97d85 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -4713,11 +4713,11 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152246)
     CPPUNIT_ASSERT_EQUAL(size_t(1), aPages.size());
 
     // Position array
-    constexpr double aPos[5][4] = { { 56.699, 707.701, 131.401, 721.499 },
-                                    { 198.499, 707.701, 273.201, 721.499 },
-                                    { 303.349, 680.101, 378.051, 693.899 },
-                                    { 480.599, 680.101, 555.301, 693.899 },
-                                    { 56.699, 652.501, 131.401, 666.299 } };
+    constexpr double aPos[5][4] = { { 55.699, 706.701, 132.401, 722.499 },
+                                    { 197.499, 706.701, 274.201, 722.499 },
+                                    { 302.349, 679.101, 379.051, 694.899 },
+                                    { 479.599, 679.101, 556.301, 694.899 },
+                                    { 55.699, 651.501, 132.401, 667.299 } };
 
     // Get page annotations.
     auto pAnnots = 
dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"_ostr));
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 0c1ee041a223..29d95338b004 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -303,6 +303,7 @@ public:
     OUString getFormAdditionalActionJavaScript(PDFiumDocument* pDoc,
                                                PDFAnnotAActionType eEvent) 
override;
     OUString getFormFieldValue(PDFiumDocument* pDoc) override;
+    int getOptionCount(PDFiumDocument* pDoc) override;
 };
 
 class PDFiumPageObjectImpl final : public PDFiumPageObject
@@ -1382,6 +1383,11 @@ OUString 
PDFiumAnnotationImpl::getFormFieldValue(PDFiumDocument* pDoc)
     }
     return aString;
 }
+int PDFiumAnnotationImpl::getOptionCount(PDFiumDocument* pDoc)
+{
+    auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
+    return FPDFAnnot_GetOptionCount(pDocImpl->getFormHandlePointer(), 
mpAnnotation);
+}
 
 OUString 
PDFiumAnnotationImpl::getFormAdditionalActionJavaScript(PDFiumDocument* pDoc,
                                                                  
PDFAnnotAActionType eEvent)

Reply via email to