sw/qa/core/text/data/placeholder.fodt | 9 ++++ sw/qa/core/text/text.cxx | 28 +++++++++++++ sw/source/core/text/itrform2.cxx | 2 sw/source/core/text/porfld.cxx | 69 +++++++++++++++++++++++++++++++--- sw/source/core/text/porfld.hxx | 24 ++++++++++- sw/source/core/text/txtfld.cxx | 3 - 6 files changed, 125 insertions(+), 10 deletions(-)
New commits: commit 696f664b3e901077d62d0dc6fd1878d7ea29821a Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Tue Oct 24 23:20:30 2023 +0300 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Fri Oct 27 11:32:14 2023 +0200 Export text placeholder fields as PDF form fields Inspired by commit 82d90529dc2b3cb8359dec78852cbd910a66d275 (sw content controls, rich text: add initial PDF export, 2022-09-12). Change-Id: I16cc45b6f2e070ab9dc83ba15e3c66ca0caa5e53 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158407 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158480 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/sw/qa/core/text/data/placeholder.fodt b/sw/qa/core/text/data/placeholder.fodt new file mode 100644 index 000000000000..01cb60437618 --- /dev/null +++ b/sw/qa/core/text/data/placeholder.fodt @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:body> + <office:text> + <text:p><text:placeholder text:placeholder-type="text" text:description="reference text"><placeholder text></text:placeholder></text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index 790f2f32415f..c68a0fa99cbe 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -1174,6 +1174,34 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testRichContentControlPDF) CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationCount()); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testPlaceholderFieldPDF) +{ + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + + // Given a file with a text-type placeholder field: + createSwDoc("placeholder.fodt"); + + // When exporting to PDF (default setting is "create a PDF form"): + save("writer_pdf_Export"); + + // Then make sure that a fillable form widget is emitted: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); + 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 placeholder field was just exported as normal text. + CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationCount()); + std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnotation = pPage->getAnnotation(0); + CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget, pAnnotation->getSubType()); + + // Also verify that the widget description is correct: + CPPUNIT_ASSERT_EQUAL(OUString("reference text"), + pAnnotation->getFormFieldAlternateName(pPdfDocument.get())); +} + CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testNumberPortionFormat) { // Given a document with a single paragraph, direct formatting asks 24pt font size for the diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 51c2d7a9423f..8d52962bb7f7 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -1182,7 +1182,7 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const } } assert(2 <= sal_Int32(nFieldLen)); - pPor = new SwFieldPortion(aFieldName, nullptr, false, nFieldLen); + pPor = new SwFieldPortion(aFieldName, nullptr, nFieldLen); } else { diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx index 66f39644ffed..580b4a2635a7 100644 --- a/sw/source/core/text/porfld.cxx +++ b/sw/source/core/text/porfld.cxx @@ -22,10 +22,14 @@ #include <com/sun/star/i18n/ScriptType.hpp> #include <com/sun/star/i18n/XBreakIterator.hpp> #include <utility> + +#include <comphelper/string.hxx> #include <vcl/graph.hxx> #include <editeng/brushitem.hxx> #include <vcl/metric.hxx> #include <vcl/outdev.hxx> +#include <vcl/pdfextoutdevdata.hxx> +#include <vcl/pdfwriter.hxx> #include <viewopt.hxx> #include <SwPortionHandler.hxx> #include "porlay.hxx" @@ -44,6 +48,7 @@ #include <editeng/lrspitem.hxx> #include <unicode/ubidi.h> #include <bookmark.hxx> +#include <docufld.hxx> using namespace ::com::sun::star; @@ -59,7 +64,7 @@ SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const } // #i107143# // pass placeholder property to created <SwFieldPortion> instance. - SwFieldPortion* pClone = new SwFieldPortion( rExpand, std::move(pNewFnt), m_bPlaceHolder ); + SwFieldPortion* pClone = new SwFieldPortion(rExpand, std::move(pNewFnt)); pClone->SetNextOffset( m_nNextOffset ); pClone->m_bNoLength = m_bNoLength; return pClone; @@ -73,13 +78,13 @@ void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField ) m_bFollow = true; } -SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, bool bPlaceHold, TextFrameIndex const nFieldLen) +SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, TextFrameIndex const nFieldLen) : m_aExpand(std::move(aExpand)), m_pFont(std::move(pFont)), m_nNextOffset(0) , m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0) , m_bFollow( false ), m_bLeft( false), m_bHide( false) , m_bCenter (false), m_bHasFollow( false ) , m_bAnimated( false), m_bNoPaint( false) - , m_bReplace( false), m_bPlaceHolder( bPlaceHold ) + , m_bReplace(false) , m_bNoLength( false ) { SetWhichPor( PortionType::Field ); @@ -100,7 +105,6 @@ SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField ) , m_bAnimated ( rField.m_bAnimated ) , m_bNoPaint( rField.m_bNoPaint) , m_bReplace( rField.m_bReplace ) - , m_bPlaceHolder( rField.m_bPlaceHolder ) , m_bNoLength( rField.m_bNoLength ) { if ( rField.HasFont() ) @@ -458,7 +462,7 @@ void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const SwFontSave aSave( rInf, m_pFont.get() ); // OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?"); - if( Width() && ( !m_bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) && !m_bContentControl ) + if (Width() && !m_bContentControl) { // A very liberal use of the background rInf.DrawViewOpt( *this, PortionType::Field ); @@ -536,7 +540,7 @@ SwNumberPortion::SwNumberPortion( const OUString &rExpand, const bool bCntr, const sal_uInt16 nMinDst, const bool bLabelAlignmentPosAndSpaceModeActive ) - : SwFieldPortion(rExpand, std::move(pFont), false, TextFrameIndex(0)) + : SwFieldPortion(rExpand, std::move(pFont), TextFrameIndex(0)) , m_nFixWidth(0) , m_nMinDist(nMinDst) , mbLabelAlignmentPosAndSpaceModeActive(bLabelAlignmentPosAndSpaceModeActive) @@ -1392,4 +1396,57 @@ void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const } } +SwFieldPortion* SwJumpFieldPortion::Clone(const OUString& rExpand) const +{ + auto pRet = new SwJumpFieldPortion(*this); + pRet->m_aExpand = rExpand; + return pRet; +} + +bool SwJumpFieldPortion::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_nFormat != SwJumpEditFormat::JE_FMT_TEXT) + return false; + + vcl::PDFWriter::EditWidget aDescriptor; + + aDescriptor.Border = true; + aDescriptor.BorderColor = COL_BLACK; + + SwRect aLocation; + rInf.CalcRect(*this, &aLocation); + aDescriptor.Location = aLocation.SVRect(); + + // Map the text of the field to the descriptor's text. + static sal_Unicode constexpr aForbidden[] = { CH_TXTATR_BREAKWORD, 0 }; + aDescriptor.Text = comphelper::string::removeAny(GetExp(), aForbidden); + + // Description for accessibility purposes. + if (!m_sHelp.isEmpty()) + aDescriptor.Description = m_sHelp; + + pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form); + pPDFExtOutDevData->CreateControl(aDescriptor); + pPDFExtOutDevData->EndStructureElement(); + + return true; +} + +void SwJumpFieldPortion::Paint(const SwTextPaintInfo& rInf) const +{ + if (Width() && DescribePDFControl(rInf)) + return; + + if (rInf.GetOpt().IsShowPlaceHolderFields()) + SwFieldPortion::Paint(rInf); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/porfld.hxx b/sw/source/core/text/porfld.hxx index e1c18fc9bd18..b92372942425 100644 --- a/sw/source/core/text/porfld.hxx +++ b/sw/source/core/text/porfld.hxx @@ -50,7 +50,6 @@ protected: bool m_bAnimated : 1; // Used by SwGrfNumPortion bool m_bNoPaint : 1; // Used by SwGrfNumPortion bool m_bReplace : 1; // Used by SwGrfNumPortion - const bool m_bPlaceHolder : 1; bool m_bNoLength : 1; // HACK for meta suffix (no CH_TXTATR) bool m_bContentControl = false; @@ -60,7 +59,7 @@ protected: public: SwFieldPortion( const SwFieldPortion& rField ); - SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFnt = nullptr, bool bPlaceHolder = false, TextFrameIndex nLen = TextFrameIndex(1)); + SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFnt = nullptr, TextFrameIndex nLen = TextFrameIndex(1)); virtual ~SwFieldPortion() override; void TakeNextOffset( const SwFieldPortion* pField ); @@ -262,4 +261,25 @@ private: bool m_bStart; }; +class SwJumpFieldPortion final : public SwFieldPortion +{ +public: + explicit SwJumpFieldPortion(OUString aExpand, OUString aHelp, std::unique_ptr<SwFont> pFont, + sal_uInt32 nFormat) + : SwFieldPortion(std::move(aExpand), std::move(pFont)) + , m_nFormat(nFormat) + , m_sHelp(std::move(aHelp)) + { + } + virtual SwFieldPortion* Clone(const OUString& rExpand) const override; + + virtual void Paint(const SwTextPaintInfo& rInf) const override; + +private: + sal_uInt32 m_nFormat; // SwJumpEditFormat from SwField::GetFormat() + OUString m_sHelp; + + bool DescribePDFControl(const SwTextPaintInfo& rInf) const; +}; + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx index 7ca9b9e34fd6..7de5583cd1a9 100644 --- a/sw/source/core/text/txtfld.cxx +++ b/sw/source/core/text/txtfld.cxx @@ -184,7 +184,8 @@ SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf, &static_cast<SwJumpEditField*>(pField)->GetCharFormat()->GetAttrSet(), &m_pFrame->GetDoc().getIDocumentSettingAccess()); } - return new SwFieldPortion(ExpandField(*pField, *this, rInf), std::move(pFont), true); + return new SwJumpFieldPortion(ExpandField(*pField, *this, rInf), pField->GetPar2(), + std::move(pFont), pField->GetFormat()); } case SwFieldIds::GetRef: break;