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)