sw/qa/core/text/text.cxx | 32 ++++++++++++++++++++++++++++++++ sw/source/core/text/itrform2.cxx | 31 ++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 5 deletions(-)
New commits: commit a70f5f141c1e6013abb4c1b3219e017b2eea40a8 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Oct 7 13:51:55 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Oct 7 14:40: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. Change-Id: Ie8627faa4257d2660a733e2a28546a894548edd3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141053 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index c776dd20609e..d5b1761476c3 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> @@ -778,6 +779,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(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false); + sal_Int32 nPlaceHolderLen = SwResId(STR_CONTENT_CONTROL_PLACEHOLDER).getLength(); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/true, nPlaceHolderLen, + /*bBasicCall=*/false); + pWrtShell->Insert("xxxyyy"); + pWrtShell->Left(SwCursorSkipMode::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 700669f3d2e2..5d1d160d27bb 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -930,6 +930,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()) { @@ -996,10 +1009,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[] = { @@ -1008,8 +1019,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);