summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2022-09-12 09:30:38 +0200
committerMiklos Vajna <vmiklos@collabora.com>2022-09-12 10:16:11 +0200
commit82d90529dc2b3cb8359dec78852cbd910a66d275 (patch)
tree427c2458c4c039aaced2c38310881bb19891645c
parent7458cb8d13b2893ccbfb648198319cc3a73d7e50 (diff)
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 <vmiklos@collabora.com> Tested-by: Jenkins
-rw-r--r--sw/inc/formatcontentcontrol.hxx2
-rw-r--r--sw/qa/core/text/text.cxx33
-rw-r--r--sw/source/core/text/itrform2.cxx89
-rw-r--r--sw/source/core/txtnode/attrcontentcontrol.cxx30
4 files changed, 152 insertions, 2 deletions
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"));