sw/inc/formatcontentcontrol.hxx               |    2 
 sw/qa/core/text/text.cxx                      |   33 +++++++++
 sw/source/core/text/itrform2.cxx              |   89 +++++++++++++++++++++++++-
 sw/source/core/txtnode/attrcontentcontrol.cxx |   30 ++++++++
 4 files changed, 152 insertions(+), 2 deletions(-)

New commits:
commit 82d90529dc2b3cb8359dec78852cbd910a66d275
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Sep 12 09:30:38 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Sep 12 10:16:11 2022 +0200

    sw content controls, rich text: add initial PDF export
    
    Similar to how such form widgets are emitted for form shapes in
    
drawinglayer::processor2d::VclMetafileProcessor2D::processControlPrimitive2D().
    
    This is just initial support for rich and plain text, the other types
    from SwContentControlType are not yet handled.
    
    Change-Id: I765fec134f1959fe03f01ecaa128e830d86ab610
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139787
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index d265d2f098e9..fa7c237acaf7 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -317,6 +317,8 @@ public:
     void SetReadWrite(bool bReadWrite) { m_bReadWrite = bReadWrite; }
 
     bool GetReadWrite() const { return m_bReadWrite; }
+
+    SwContentControlType GetType() const;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx
index 50739240ad70..0e832f074cd9 100644
--- a/sw/qa/core/text/text.cxx
+++ b/sw/qa/core/text/text.cxx
@@ -33,6 +33,7 @@
 #include <fmtcntnt.hxx>
 #include <fmtfsize.hxx>
 #include <IDocumentRedlineAccess.hxx>
+#include <formatcontentcontrol.hxx>
 
 constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/core/text/data/";
 
@@ -623,6 +624,38 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, 
testTdf43100_CursorMoveToSpacesOverMargin)
     }
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testContentControlPDF)
+{
+    // Given a file with a content control:
+    SwDoc* pDoc = createSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->InsertContentControl(SwContentControlType::RICH_TEXT);
+
+    // When exporting to PDF:
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    utl::MediaDescriptor aMediaDescriptor;
+    aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+    xStorable->storeToURL(maTempFile.GetURL(), 
aMediaDescriptor.getAsConstPropertyValueList());
+
+    // Then make sure that a fillable form widget is emitted:
+    SvFileStream aFile(maTempFile.GetURL(), StreamMode::READ);
+    SvMemoryStream aMemory;
+    aMemory.WriteStream(aFile);
+    std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+    if (!pPDFium)
+    {
+        return;
+    }
+    std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+        = pPDFium->openDocument(aMemory.GetData(), aMemory.GetSize(), 
OString());
+    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  : 0
+    // i.e. the content control was just exported as normal text.
+    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 6c38bdbe3586..5073e826457b 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -55,8 +55,10 @@
 #include <IMark.hxx>
 #include <IDocumentMarkAccess.hxx>
 #include <comphelper/processfactory.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
 #include <docsh.hxx>
 #include <unocrsrhelper.hxx>
+#include <textcontentcontrol.hxx>
 #include <com/sun/star/rdf/Statement.hpp>
 #include <com/sun/star/rdf/URI.hpp>
 #include <com/sun/star/rdf/URIs.hpp>
@@ -872,9 +874,13 @@ public:
 /// A content control portion is a text portion that is inside 
RES_TXTATR_CONTENTCONTROL.
 class SwContentControlPortion : public SwTextPortion
 {
+    SwTextContentControl* m_pTextContentControl;
 public:
-    SwContentControlPortion() { SetWhichPor(PortionType::ContentControl); }
+    SwContentControlPortion(SwTextContentControl* pTextContentControl);
     virtual void Paint(const SwTextPaintInfo& rInf) const override;
+
+    /// Emits a PDF form widget for this portion on success, does nothing on 
failure.
+    bool DescribePDFControl(const SwTextPaintInfo& rInf) const;
 };
 }
 
@@ -892,11 +898,78 @@ void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) 
const
     }
 }
 
+SwContentControlPortion::SwContentControlPortion(SwTextContentControl* 
pTextContentControl)
+    : m_pTextContentControl(pTextContentControl)
+{
+    SetWhichPor(PortionType::ContentControl);
+}
+
+bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) 
const
+{
+    auto pPDFExtOutDevData = 
dynamic_cast<vcl::PDFExtOutDevData*>(rInf.GetOut()->GetExtOutDevData());
+    if (!pPDFExtOutDevData)
+    {
+        return false;
+    }
+
+    if (!pPDFExtOutDevData->GetIsExportFormFields())
+    {
+        return false;
+    }
+
+    if (!m_pTextContentControl)
+    {
+        return false;
+    }
+
+    const SwFormatContentControl& rFormatContentControl = 
m_pTextContentControl->GetContentControl();
+    const std::shared_ptr<SwContentControl>& pContentControl = 
rFormatContentControl.GetContentControl();
+    if (!pContentControl)
+    {
+        return false;
+    }
+
+    std::unique_ptr<vcl::PDFWriter::AnyWidget> pDescriptor;
+    switch (pContentControl->GetType())
+    {
+        case SwContentControlType::RICH_TEXT:
+        case SwContentControlType::PLAIN_TEXT:
+            pDescriptor = std::make_unique<vcl::PDFWriter::EditWidget>();
+            break;
+        default:
+            break;
+    }
+
+    if (!pDescriptor)
+    {
+        return false;
+    }
+
+    pDescriptor->Border = true;
+    pDescriptor->BorderColor = COL_BLACK;
+
+    SwRect aLocation;
+    rInf.CalcRect(*this, &aLocation);
+    pDescriptor->Location = aLocation.SVRect();
+
+    pPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
+    pPDFExtOutDevData->CreateControl(*pDescriptor);
+    pPDFExtOutDevData->EndStructureElement();
+
+    return true;
+}
+
 void SwContentControlPortion::Paint(const SwTextPaintInfo& rInf) const
 {
     if (Width())
     {
         rInf.DrawViewOpt(*this, PortionType::ContentControl);
+
+        if (DescribePDFControl(rInf))
+        {
+            return;
+        }
+
         SwTextPortion::Paint(rInf);
     }
 }
@@ -1019,7 +1092,19 @@ SwTextPortion *SwTextFormatter::WhichTextPor( 
SwTextFormatInfo &rInf ) const
         }
         else if (GetFnt()->IsContentControl())
         {
-            pPor = new SwContentControlPortion;
+            SwTextFrame const*const pFrame(rInf.GetTextFrame());
+            SwPosition aPosition(pFrame->MapViewToModelPos(rInf.GetIdx()));
+            SwTextNode* pTextNode = aPosition.GetNode().GetTextNode();
+            SwTextContentControl* pTextContentControl = nullptr;
+            if (pTextNode)
+            {
+                sal_Int32 nIndex = aPosition.GetContentIndex();
+                if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, 
RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT))
+                {
+                    pTextContentControl = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+                }
+            }
+            pPor = new SwContentControlPortion(pTextContentControl);
         }
         else
         {
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx 
b/sw/source/core/txtnode/attrcontentcontrol.cxx
index b9992975f05a..5582b3409b30 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -334,6 +334,36 @@ bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& 
rKeyCode)
     return false;
 }
 
+SwContentControlType SwContentControl::GetType() const
+{
+    if (m_bCheckbox)
+    {
+        return SwContentControlType::CHECKBOX;
+    }
+
+    if (!m_aListItems.empty())
+    {
+        return SwContentControlType::DROP_DOWN_LIST;
+    }
+
+    if (m_bPicture)
+    {
+        return SwContentControlType::PICTURE;
+    }
+
+    if (m_bDate)
+    {
+        return SwContentControlType::DATE;
+    }
+
+    if (m_bPlainText)
+    {
+        return SwContentControlType::PLAIN_TEXT;
+    }
+
+    return SwContentControlType::RICH_TEXT;
+}
+
 void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
 {
     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl"));

Reply via email to