include/vcl/filter/PDFiumLibrary.hxx | 1 sw/qa/core/text/text.cxx | 24 +++++++++++++++++++++ sw/qa/filter/html/data/centered-table.xhtml | 9 ++++++++ sw/qa/filter/html/html.cxx | 19 +++++++++++++++++ sw/source/core/text/itrform2.cxx | 22 +++++++++---------- sw/source/filter/html/htmltab.cxx | 29 ++++++++++++++++++++++++++ sw/source/filter/html/svxcss1.cxx | 16 ++++++++++++++ sw/source/filter/html/svxcss1.hxx | 2 + vcl/source/pdf/PDFiumLibrary.cxx | 31 ++++++++++++++++++++++++++++ 9 files changed, 141 insertions(+), 12 deletions(-)
New commits: commit e80593e219dffd85ab16ebd2954cba8fa8c84637 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu Jan 19 19:53:12 2023 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Tue Jan 24 11:47:23 2023 +0000 tdf#153047 sw: fix PDF export of content controls in placeholder mode Importing an inline content control from DOCX used to be just plain text in Writer, so the PDF export of that was also just plain text. Now that content controls are actually supported, we used to not emit them as plain text in the PDF export, since 82d90529dc2b3cb8359dec78852cbd910a66d275 (sw content controls, rich text: add initial PDF export, 2022-09-12). Part of this was to write the string value of the content control as the /V (value) key of the form, when it's not in placeholder mode. This made sure that once the form is filled in, no overlap between the plain text and the filled in text happens. Try to support both use-cases at the same time by also mapping the value of the content control to /V, even if it's in placeholder mode. This keeps avoiding the unwanted overlap, but this way the placeholder text is no longer lost on PDF export. An alternative would have been to map the placeholder text to description when the alias/title is empty, but that would show up only as a mouse tooltip, so won't change the behavior when the PDF is printed. Change-Id: I9408b5abe36af28cd00845a74a3dfff13973b83a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145828 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> (cherry picked from commit 14da39fcfffe8006a79971ac0b670e12d0d7a0ea) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146017 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx index 347b64619045..9c278d281a1c 100644 --- a/include/vcl/filter/PDFiumLibrary.hxx +++ b/include/vcl/filter/PDFiumLibrary.hxx @@ -110,6 +110,7 @@ public: virtual OUString getFormAdditionalActionJavaScript(PDFiumDocument* pDoc, PDFAnnotAActionType eEvent) = 0; + virtual OUString getFormFieldValue(PDFiumDocument* pDoc) = 0; }; class PDFiumTextPage; diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index c4821f5d6eae..9ffae51f175f 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -679,6 +679,30 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testContentControlPDF) pAnnotation->getFormFieldAlternateName(pPdfDocument.get())); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testContentControlPlaceholderPDF) +{ + // Given a file with a content control, in placeholder mode: + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->InsertContentControl(SwContentControlType::RICH_TEXT); + + // When exporting to PDF: + save("writer_pdf_Export"); + + // Then make sure that a fillable form widget is emitted with the expected value: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport(); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getAnnotationCount()); + std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnotation = pPage->getAnnotation(0); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: Click here to enter text + // - Actual : + // i.e. the value of the content control was empty, the placeholder value was lost. + CPPUNIT_ASSERT_EQUAL(SwResId(STR_CONTENT_CONTROL_PLACEHOLDER), + pAnnotation->getFormFieldValue(pPdfDocument.get())); +} + CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testCheckboxContentControlPDF) { std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 9f075014fd9a..91f73ed87825 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -1047,18 +1047,16 @@ bool SwContentControlPortion::DescribePDFControl(const SwTextPaintInfo& rInf) co pDescriptor->Description = pContentControl->GetAlias(); } - if (!pContentControl->GetShowingPlaceHolder()) - { - SwPosition aPoint(*pTextNode, nStart); - SwPosition aMark(*pTextNode, nEnd); - SwPaM aPam(aMark, aPoint); - OUString aText = aPam.GetText(); - static sal_Unicode const aForbidden[] = { - CH_TXTATR_BREAKWORD, - 0 - }; - pDescriptor->Text = comphelper::string::removeAny(aText, aForbidden); - } + // Map the text of the content control to the descriptor's text. + SwPosition aPoint(*pTextNode, nStart); + SwPosition aMark(*pTextNode, nEnd); + SwPaM aPam(aMark, aPoint); + OUString aText = aPam.GetText(); + static sal_Unicode const aForbidden[] = { + CH_TXTATR_BREAKWORD, + 0 + }; + pDescriptor->Text = comphelper::string::removeAny(aText, aForbidden); // Calculate the bounding rectangle of this content control, which can be one or more layout // portions in one or more lines. diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx index 9fa291898f8c..e0562c4dbf9b 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -268,6 +268,7 @@ public: int getFormFieldFlags(PDFiumDocument* pDoc) override; OUString getFormAdditionalActionJavaScript(PDFiumDocument* pDoc, PDFAnnotAActionType eEvent) override; + OUString getFormFieldValue(PDFiumDocument* pDoc) override; }; class PDFiumPageObjectImpl final : public PDFiumPageObject @@ -1233,6 +1234,36 @@ OUString PDFiumAnnotationImpl::getFormFieldAlternateName(PDFiumDocument* pDoc) return aString; } +OUString PDFiumAnnotationImpl::getFormFieldValue(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + OUString aString; + unsigned long nSize + = FPDFAnnot_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, nullptr, 0); + assert(nSize % 2 == 0); + nSize /= 2; + if (nSize > 1) + { + std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nSize]); + unsigned long nStringSize + = FPDFAnnot_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, + reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize * 2); + assert(nStringSize % 2 == 0); + nStringSize /= 2; + if (nStringSize > 0) + { +#if defined OSL_BIGENDIAN + for (unsigned long i = 0; i != nStringSize; ++i) + { + pText[i] = OSL_SWAPWORD(pText[i]); + } +#endif + aString = OUString(pText.get()); + } + } + return aString; +} + OUString PDFiumAnnotationImpl::getFormAdditionalActionJavaScript(PDFiumDocument* pDoc, PDFAnnotAActionType eEvent) { commit 68c003154e7eb0dcf0473b41ce112283659a1cac Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Jan 20 12:15:48 2023 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Tue Jan 24 11:47:15 2023 +0000 sw HTML import: initial CSS support on tables This is the import side of commit 6ce374140f3bd1cede959ccc8da69fdacecff191 (sw XHTML export: use CSS instead of <center> for tables, 2023-01-19), otherwise the import would not handle the new markup of the export. Change-Id: I7d18b06e10adff877dbbf1745861c65a07f807cb Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145863 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins (cherry picked from commit 44c75d56cd441ad3d174db75a6e5ae672b7d2a08) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145946 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/filter/html/data/centered-table.xhtml b/sw/qa/filter/html/data/centered-table.xhtml new file mode 100644 index 000000000000..4ec88792f119 --- /dev/null +++ b/sw/qa/filter/html/data/centered-table.xhtml @@ -0,0 +1,9 @@ +<reqif-xhtml:div> + <reqif-xhtml:table width="400" cellpadding="0" cellspacing="0" style="margin-left: auto; margin-right: auto"> + <reqif-xhtml:tr> + <reqif-xhtml:td><reqif-xhtml:p>A1</reqif-xhtml:p> + </reqif-xhtml:td> + </reqif-xhtml:tr> + </reqif-xhtml:table> +<reqif-xhtml:p></reqif-xhtml:p> +</reqif-xhtml:div> diff --git a/sw/qa/filter/html/html.cxx b/sw/qa/filter/html/html.cxx index 65f67b7378d1..2b0c36508378 100644 --- a/sw/qa/filter/html/html.cxx +++ b/sw/qa/filter/html/html.cxx @@ -226,6 +226,25 @@ CPPUNIT_TEST_FIXTURE(Test, testCenteredTableCSSExport) assertXPath(pXmlDoc, "//reqif-xhtml:center", 0); assertXPath(pXmlDoc, "//reqif-xhtml:table", "style", "margin-left: auto; margin-right: auto"); } + +CPPUNIT_TEST_FIXTURE(Test, testCenteredTableCSSImport) +{ + // Given an XHTML file with a centered (with inline CSS) table, when importing that document: + setImportFilterOptions("xhtmlns=reqif-xhtml"); + setImportFilterName("HTML (StarWriter)"); + createSwDoc("centered-table.xhtml"); + + // Then make sure that the table is centered: + SwDoc* pDoc = getSwDoc(); + const SwFrameFormats& rTableFormats = *pDoc->GetTableFrameFormats(); + const SwFrameFormat* pTableFormat = rTableFormats[0]; + sal_Int16 eHoriOrient = pTableFormat->GetHoriOrient().GetHoriOrient(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 (CENTER) + // - Actual : 3 (LEFT) + // i.e. the table alignment was lost on import. + CPPUNIT_ASSERT_EQUAL(text::HoriOrientation::CENTER, eHoriOrient); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/html/htmltab.cxx b/sw/source/filter/html/htmltab.cxx index fe55cec381ac..7913cd9e85fa 100644 --- a/sw/source/filter/html/htmltab.cxx +++ b/sw/source/filter/html/htmltab.cxx @@ -4917,6 +4917,35 @@ std::shared_ptr<HTMLTable> SwHTMLParser::BuildTable(SvxAdjust eParentAdjust, else { m_xTable.reset(); + + // Parse CSS on the table. + OUString aStyle; + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i;) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + if (rOption.GetToken() == HtmlOptionId::STYLE) + { + aStyle = rOption.GetString(); + } + } + if (!aStyle.isEmpty()) + { + // Have inline CSS. + SfxItemSet aItemSet(m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap()); + SvxCSS1PropertyInfo aPropInfo; + if (ParseStyleOptions(aStyle, /*aId=*/OUString(), /*aClass=*/OUString(), aItemSet, + aPropInfo)) + { + if (aPropInfo.m_eLeftMarginType == SVX_CSS1_LTYPE_AUTO + && aPropInfo.m_eRightMarginType == SVX_CSS1_LTYPE_AUTO) + { + // Both left & right is set to auto: that's our center. + eParentAdjust = SvxAdjust::Center; + } + } + } + HTMLTableOptions aTableOptions(GetOptions(), eParentAdjust); if (!aTableOptions.aId.isEmpty()) diff --git a/sw/source/filter/html/svxcss1.cxx b/sw/source/filter/html/svxcss1.cxx index 256656a6df1e..9b312e0b3239 100644 --- a/sw/source/filter/html/svxcss1.cxx +++ b/sw/source/filter/html/svxcss1.cxx @@ -399,6 +399,8 @@ SvxCSS1PropertyInfo::SvxCSS1PropertyInfo( const SvxCSS1PropertyInfo& rProp ) : m_eTopType( rProp.m_eTopType ), m_eWidthType( rProp.m_eWidthType ), m_eHeightType( rProp.m_eHeightType ), + m_eLeftMarginType( rProp.m_eLeftMarginType ), + m_eRightMarginType( rProp.m_eRightMarginType ), m_eSizeType( rProp.m_eSizeType ), m_ePageBreakBefore( rProp.m_ePageBreakBefore ), m_ePageBreakAfter( rProp.m_ePageBreakAfter ) @@ -438,6 +440,8 @@ void SvxCSS1PropertyInfo::Clear() m_nLeft = m_nTop = m_nWidth = m_nHeight = 0; m_eLeftType = m_eTopType = m_eWidthType = m_eHeightType = SVX_CSS1_LTYPE_NONE; + m_eLeftMarginType = SVX_CSS1_LTYPE_NONE; + m_eRightMarginType = SVX_CSS1_LTYPE_NONE; // Feature: PrintExt m_eSizeType = SVX_CSS1_STYPE_NONE; @@ -2042,6 +2046,12 @@ static void ParseCSS1_margin_left( const CSS1Expression *pExpr, ; } + if (pExpr->GetString() == "auto") + { + rPropInfo.m_bLeftMargin = true; + rPropInfo.m_eLeftMarginType = SVX_CSS1_LTYPE_AUTO; + } + if( !bSet ) return; @@ -2099,6 +2109,12 @@ static void ParseCSS1_margin_right( const CSS1Expression *pExpr, ; } + if (pExpr->GetString() == "auto") + { + rPropInfo.m_bRightMargin = true; + rPropInfo.m_eRightMarginType = SVX_CSS1_LTYPE_AUTO; + } + if( !bSet ) return; diff --git a/sw/source/filter/html/svxcss1.hxx b/sw/source/filter/html/svxcss1.hxx index 0ca92f314ea9..30c5a7b4accb 100644 --- a/sw/source/filter/html/svxcss1.hxx +++ b/sw/source/filter/html/svxcss1.hxx @@ -136,6 +136,8 @@ public: SvxCSS1LengthType m_eLeftType, m_eTopType; SvxCSS1LengthType m_eWidthType, m_eHeightType; + SvxCSS1LengthType m_eLeftMarginType; + SvxCSS1LengthType m_eRightMarginType; SvxCSS1SizeType m_eSizeType;