sw/qa/core/text/text.cxx | 32 ++++++++++++++++++++++++++++++++ sw/source/core/text/itrform2.cxx | 31 ++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 5 deletions(-)
New commits: commit d5135d4eb2b10e6ceb78c64c298076827f1863eb Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Oct 7 13:51:55 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Mon Oct 10 09:21:36 2022 +0200 sw content controls: emit only one PDF widget for multiple text portions If the content control's text is like "XXX<b>YYY</b>", then we used to create two PDF form widgets, while only one is wanted. This is especially annoying since the widget's width is defined by the Writer text, so tab portions would be a natural tool to nicely increase the width if wanted. The problem is that in case the content is rich text, then we break up that content into multiple portions, and each portion will be handled in SwContentControlPortion::DescribePDFControl() separately. Fix the problem by checking if the current text portion is the first one for a content control, handling the entire content control there and then silently not doing anything (no form widget, no plain text fallback) for the rest of the content. This also helps in case the content control has a linebreak to extend its height. Note that this can't be perfect in case the content control has multiple lines but the starting/ending line has text outside the content control. This is a restriction on the PDF side, since the form widget's area must be a rectangle. (cherry picked from commit a70f5f141c1e6013abb4c1b3219e017b2eea40a8) Change-Id: Ie8627faa4257d2660a733e2a28546a894548edd3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141075 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index b5b9476cd236..7c3077c8cdae 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -19,6 +19,7 @@ #include <comphelper/propertyvalue.hxx> #include <unotools/mediadescriptor.hxx> #include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> #include <docsh.hxx> #include <unotxdoc.hxx> @@ -688,6 +689,37 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testComboContentControlPDF) CPPUNIT_ASSERT(pAnnotation->getFormFieldFlags(pPdfDocument.get()) & 0x00040000); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testRichContentControlPDF) +{ + // Given a file with a rich content control, its value set to "xxx<b>yyy</b>": + SwDoc* pDoc = createSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->InsertContentControl(SwContentControlType::RICH_TEXT); + pWrtShell->SttEndDoc(/*bStt=*/true); + pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + sal_Int32 nPlaceHolderLen = SwResId(STR_CONTENT_CONTROL_PLACEHOLDER).getLength(); + pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/true, nPlaceHolderLen, + /*bBasicCall=*/false); + pWrtShell->Insert("xxxyyy"); + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/true, 3, /*bBasicCall=*/false); + SfxItemSetFixed<RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT> aSet(pWrtShell->GetAttrPool()); + SvxWeightItem aItem(WEIGHT_BOLD, RES_CHRATR_WEIGHT); + aSet.Put(aItem); + pWrtShell->SetAttrSet(aSet); + + // When exporting to PDF: + StoreToTempFile("writer_pdf_Export"); + + // Then make sure that a single fillable form widget is emitted: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = LoadPdfFromTempFile(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 2 + // i.e. "xxx<b>yyy</b>" was exported as 2 widgets, not 1. + CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationCount()); +} + 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 35548fe00217..3d9ed4edebfe 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -924,6 +924,19 @@ bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) co return false; } + // Check if this is the first content control portion of this content control. + SwTextNode* pTextNode = pContentControl->GetTextNode(); + sal_Int32 nStart = m_pTextContentControl->GetStart(); + sal_Int32 nEnd = *m_pTextContentControl->GetEnd(); + TextFrameIndex nViewStart = rInf.GetTextFrame()->MapModelToView(pTextNode, nStart); + TextFrameIndex nViewEnd = rInf.GetTextFrame()->MapModelToView(pTextNode, nEnd); + // The content control portion starts 1 char after the starting dummy character. + if (rInf.GetIdx() != nViewStart + TextFrameIndex(1)) + { + // Ignore: don't process and also don't emit plain text fallback. + return true; + } + std::unique_ptr<vcl::PDFWriter::AnyWidget> pDescriptor; switch (pContentControl->GetType()) { @@ -990,10 +1003,8 @@ bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) co } // Description for accessibility purposes. - SwTextContentControl* pTextAttr = pContentControl->GetTextAttr(); - SwTextNode* pTextNode = pContentControl->GetTextNode(); - SwPosition aPoint(*pTextNode, pTextAttr->GetStart()); - SwPosition aMark(*pTextNode, *pTextAttr->GetEnd()); + SwPosition aPoint(*pTextNode, nStart); + SwPosition aMark(*pTextNode, nEnd); SwPaM aPam(aMark, aPoint); OUString aDescription = aPam.GetText(); static sal_Unicode const aForbidden[] = { @@ -1002,8 +1013,18 @@ bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) co }; pDescriptor->Description = comphelper::string::removeAny(aDescription, aForbidden); + // Calculate the bounding rectangle of this content control, which can be one or more layout + // portions in one or more lines. SwRect aLocation; - rInf.CalcRect(*this, &aLocation); + auto pTextFrame = const_cast<SwTextFrame*>(rInf.GetTextFrame()); + SwTextSizeInfo aInf(pTextFrame); + SwTextCursor aLine(pTextFrame, &aInf); + SwRect aStartRect; + aLine.GetCharRect(&aStartRect, nViewStart); + aLocation = aStartRect; + SwRect aEndRect; + aLine.GetCharRect(&aEndRect, nViewEnd); + aLocation.Union(aEndRect); pDescriptor->Location = aLocation.SVRect(); pPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);