diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-09-12 09:30:38 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-09-12 10:16:11 +0200 |
commit | 82d90529dc2b3cb8359dec78852cbd910a66d275 (patch) | |
tree | 427c2458c4c039aaced2c38310881bb19891645c | |
parent | 7458cb8d13b2893ccbfb648198319cc3a73d7e50 (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.hxx | 2 | ||||
-rw-r--r-- | sw/qa/core/text/text.cxx | 33 | ||||
-rw-r--r-- | sw/source/core/text/itrform2.cxx | 89 | ||||
-rw-r--r-- | sw/source/core/txtnode/attrcontentcontrol.cxx | 30 |
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")); |