diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2024-01-30 16:51:49 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2024-02-15 13:05:15 +0100 |
commit | 1502f4dcd29c3dfa0f473c85329953943e24df8e (patch) | |
tree | 7ba7b8a3379f65efa4eaf4f97639d4e7aa98c1f4 | |
parent | 7fb34ef1845b7d793b168db805ebbb64969c74b7 (diff) |
tdf#159452 sw content control, PDF export: fix checked checkboxes
Regression from commit 9bad5be0ffdcdee92d40162b598ed2ab2815e5d5 (sw
content controls, checkbox: add PDF export, 2022-09-13), we used to
export checkbox content controls as plain text, but once checkbox
content controls are exported as forms, the state of the checkboxes are
lost.
Writer content control checkboxes support custom values for the checked
and unchecked states, but the PDF export does not. On one hand,
PDFWriterImpl::createDefaultCheckBoxAppearance() assumes that the
checked state should be a checkmark, not the Writer default 'BALLOT BOX
WITH X' (U+2612). On the other hand, the PDF spec section 12.7.4.2.3
"Check Boxes" says that the checked state should be "Yes", which
explains why our checked state is not recognized by PDF readers.
Fix the problem by making the export of checked/unchecked states
conditional in SwContentControlPortion::DescribePDFControl(): the
checked state then shows up as expected.
Leave the unchecked case unchanged, the current markup there doesn't
cause problems.
(cherry picked from commit 256e2c679bcbb3ea446884d0ff4e3f8687b82ede)
Conflicts:
sw/qa/core/text/itrform2.cxx
Change-Id: I9063d8607c8cccfa080921af38b3cbfe40905115
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163424
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
-rw-r--r-- | include/vcl/filter/PDFiumLibrary.hxx | 1 | ||||
-rw-r--r-- | sw/qa/core/text/text.cxx | 35 | ||||
-rw-r--r-- | sw/source/core/text/itrform2.cxx | 9 | ||||
-rw-r--r-- | vcl/source/pdf/PDFiumLibrary.cxx | 31 |
4 files changed, 74 insertions, 2 deletions
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx index a0da94ab49f2..305c7f3f9d2f 100644 --- a/include/vcl/filter/PDFiumLibrary.hxx +++ b/include/vcl/filter/PDFiumLibrary.hxx @@ -106,6 +106,7 @@ public: virtual float getFontSize(PDFiumDocument* pDoc) = 0; virtual OUString getFormFieldAlternateName(PDFiumDocument* pDoc) = 0; virtual int getFormFieldFlags(PDFiumDocument* pDoc) = 0; + virtual OUString getFormFieldValue(PDFiumDocument* pDoc) = 0; }; class PDFiumTextPage; diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index de15cadd4c36..8cc59e8e3a2a 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -770,6 +770,41 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testNumberPortionNoformat) getXPath(pXmlDoc, "//Special[@nType='PortionType::Number']/SwFont", "color")); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testCheckedCheckboxContentControlPDF) +{ + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + + // Given a file with a checked checkbox content control: + SwDoc* pDoc = createSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->InsertContentControl(SwContentControlType::CHECKBOX); + // Toggle it, so we get a checked one: + SwTextContentControl* pTextContentControl = pWrtShell->CursorInsideContentControl(); + const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl(); + pWrtShell->GotoContentControl(rFormatContentControl); + + // When exporting to PDF: + StoreToTempFile("writer_pdf_Export"); + + // Then make sure that a checked checkbox form widget is emitted: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = LoadPdfFromTempFile(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationCount()); + std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnotation = pPage->getAnnotation(0); + CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget, pAnnotation->getSubType()); + CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::CheckBox, + pAnnotation->getFormFieldType(pPdfDocument.get())); + OUString aActual = pAnnotation->getFormFieldValue(pPdfDocument.get()); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: Yes + // - Actual : Off + // i.e. the /AP -> /N key of the checkbox widget annotation object didn't have a sub-key that + // would match /V, leading to not showing the checked state. + CPPUNIT_ASSERT_EQUAL(OUString("Yes"), aActual); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index fad85e12337e..fdbd8ef28050 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -951,8 +951,13 @@ bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) co pDescriptor = std::make_unique<vcl::PDFWriter::CheckBoxWidget>(); auto pCheckBoxWidget = static_cast<vcl::PDFWriter::CheckBoxWidget*>(pDescriptor.get()); pCheckBoxWidget->Checked = pContentControl->GetChecked(); - pCheckBoxWidget->OnValue = pContentControl->GetCheckedState(); - pCheckBoxWidget->OffValue = pContentControl->GetUncheckedState(); + // If it is checked already, then leave the default "Yes" OnValue unchanged, so the + // appropriate appearance is found by PDF readers. + if (!pCheckBoxWidget->Checked) + { + pCheckBoxWidget->OnValue = pContentControl->GetCheckedState(); + pCheckBoxWidget->OffValue = pContentControl->GetUncheckedState(); + } break; } case SwContentControlType::DROP_DOWN_LIST: diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx index c6781bca2f26..5db5805b2250 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -253,6 +253,7 @@ public: float getFontSize(PDFiumDocument* pDoc) override; OUString getFormFieldAlternateName(PDFiumDocument* pDoc) override; int getFormFieldFlags(PDFiumDocument* pDoc) override; + OUString getFormFieldValue(PDFiumDocument* pDoc) override; }; class PDFiumPageObjectImpl final : public PDFiumPageObject @@ -1230,6 +1231,36 @@ OUString PDFiumAnnotationImpl::getFormFieldAlternateName(PDFiumDocument* pDoc) return aString; } +OUString PDFiumAnnotationImpl::getFormFieldValue(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + OUString aString; + unsigned long nSize + = FPDFAnnot_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, nullptr, 0); + assert(nSize % 2 == 0); + nSize /= 2; + if (nSize > 1) + { + std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nSize]); + unsigned long nStringSize + = FPDFAnnot_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, + reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize * 2); + assert(nStringSize % 2 == 0); + nStringSize /= 2; + if (nStringSize > 0) + { +#if defined OSL_BIGENDIAN + for (unsigned long i = 0; i != nStringSize; ++i) + { + pText[i] = OSL_SWAPWORD(pText[i]); + } +#endif + aString = OUString(pText.get()); + } + } + return aString; +} + namespace { bool getBorderProperties(FPDF_ANNOTATION mpAnnotation, float& rHorizontalCornerRadius, |