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);

Reply via email to