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 c4b7722ee865f63566599b63d0bd3a484f6408e3 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 12:05:16 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> (cherry picked from commit b3e01bd3bdd37961057026ab7f2b57cbfab35d4a) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169670 diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx index 557eaff349f7..d6e57ec99d9b 100644 --- a/include/vcl/filter/PDFiumLibrary.hxx +++ b/include/vcl/filter/PDFiumLibrary.hxx @@ -112,6 +112,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 8502a6b5593a..e771c8ba6e2a 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx @@ -4703,11 +4703,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 4a8a40fb6764..f61307b5a44f 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -270,6 +270,7 @@ public: OUString getFormAdditionalActionJavaScript(PDFiumDocument* pDoc, PDFAnnotAActionType eEvent) override; OUString getFormFieldValue(PDFiumDocument* pDoc) override; + int getOptionCount(PDFiumDocument* pDoc) override; }; class PDFiumPageObjectImpl final : public PDFiumPageObject @@ -1288,6 +1289,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)