sw/qa/extras/htmlexport/data/tdf131728.docx |binary sw/qa/extras/htmlexport/htmlexport.cxx | 18 ++++++++++++++++++ sw/source/filter/html/htmlatr.cxx | 16 ++++++++++++++++ sw/source/filter/html/htmlfly.hxx | 3 ++- sw/source/filter/html/htmlflywriter.cxx | 27 ++++++++++++++++++++++----- sw/source/filter/html/wrthtml.hxx | 2 ++ 6 files changed, 60 insertions(+), 6 deletions(-)
New commits: commit bcee366d82392b02745b0070a312624e7baa29d3 Author: László Németh <nem...@numbertext.org> AuthorDate: Tue Nov 12 23:07:16 2024 +0100 Commit: László Németh <nem...@numbertext.org> CommitDate: Wed Nov 13 13:07:52 2024 +0100 tdf#163873 sw inline heading: use HTML text, not lo-res bitmap export Text frames formatted with Inline Heading style and anchored as characters were converted to unacceptably low resolution images in the HTML export. Now the HTML export contains normal h1–h6 elements with display:inline; CSS setting to get text-based/searchable inline headings, fixing also the rendering quality. Follow-up to commit 7a35f3dc7419d833b8f47069c4df63e900ccb880 "tdf#48459 sw inline heading: apply it on the selected words", commit d87cf67f8f3346a1e380383917a3a4552fd9248e "tdf#131728 sw inline heading: fix missing/broken DOCX export", commit a1dcbd1d1ce6071d48bb5df26d7839aeb21b75a8 "tdf48459 sw inline heading: add Inline Heading frame style" and commit 984f0e49d35ba87c105310f27d945147a23d1198 "tdf#163874 sw inline heading: fix XHTML export". Change-Id: I02f7af8a39314e78d670b4db9a76897e931b3a47 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176512 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/sw/qa/extras/htmlexport/data/tdf131728.docx b/sw/qa/extras/htmlexport/data/tdf131728.docx new file mode 100644 index 000000000000..4f3399919db8 Binary files /dev/null and b/sw/qa/extras/htmlexport/data/tdf131728.docx differ diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx index 7a6b4b15542c..44eca01a2b13 100644 --- a/sw/qa/extras/htmlexport/htmlexport.cxx +++ b/sw/qa/extras/htmlexport/htmlexport.cxx @@ -3374,6 +3374,24 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_162426) assertXPath(pDoc, "/html/body/p/img", "border", u"0"); } +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_163873) +{ + // Given a document with an image with style:wrap="none": + createSwDoc("tdf131728.docx"); + // Before the fix, an assertion failed in HtmlWriter::attribute when exporting to HTML : + ExportToHTML(); + + xmlDocUniquePtr pDoc = parseXml(maTempFile); + CPPUNIT_ASSERT(pDoc); + + // Before the fix, inline headings weren't inline + assertXPath(pDoc, "/html/body/p[5]/span/h2", "style", u"display:inline;"); + assertXPath(pDoc, "/html/body/p[6]/span/h2", "style", u"display:inline;"); + assertXPath(pDoc, "/html/body/p[7]/span/h2", "style", u"display:inline;"); + assertXPath(pDoc, "/html/body/p[11]/span/h2", "style", u"display:inline;"); + assertXPath(pDoc, "/html/body/p[14]/span/h2", "style", u"display:inline;"); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index a3e1c8817728..68ca031e4220 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -436,6 +436,7 @@ static void OutHTML_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat, rInfo.bInNumberBulletList = false; // Are we in a list? bool bNumbered = false; // The current paragraph is numbered bool bPara = false; // the current token is <P> + bool bHeading = false; // the current token is <H1> .. <H6> rInfo.bParaPossible = false; // a <P> may be additionally output bool bNoEndTag = false; // don't output an end tag @@ -554,6 +555,15 @@ static void OutHTML_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat, rWrt.m_bNoAlign = true; bForceDL = true; } + else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_head1 || + rInfo.aToken == OOO_STRING_SVTOOLS_HTML_head2 || + rInfo.aToken == OOO_STRING_SVTOOLS_HTML_head3 || + rInfo.aToken == OOO_STRING_SVTOOLS_HTML_head4 || + rInfo.aToken == OOO_STRING_SVTOOLS_HTML_head5 || + rInfo.aToken == OOO_STRING_SVTOOLS_HTML_head6) + { + bHeading = true; + } } else { @@ -925,6 +935,12 @@ static void OutHTML_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat, HTMLOutFuncs::Out_String( rWrt.Strm(), aClass ); sOut += "\""; } + + // set inline heading (heading in a text frame anchored as character and + // formatted with frame style "Inline Heading") + if( bHeading && rWrt.IsInlineHeading() ) + sOut += " " OOO_STRING_SVTOOLS_HTML_O_style "=\"display:inline;\""; + rWrt.Strm().WriteOString( sOut ); sOut = ""_ostr; diff --git a/sw/source/filter/html/htmlfly.hxx b/sw/source/filter/html/htmlfly.hxx index 4deb1ae4696d..bd7f1c599cc2 100644 --- a/sw/source/filter/html/htmlfly.hxx +++ b/sw/source/filter/html/htmlfly.hxx @@ -60,7 +60,8 @@ enum class HtmlOut { Marquee, GraphicFrame, OleGraphic, - Span + Span, + InlineHeading }; enum class HtmlPosition { diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx index 4269e675e895..69a3d56a2ed0 100644 --- a/sw/source/filter/html/htmlflywriter.cxx +++ b/sw/source/filter/html/htmlflywriter.cxx @@ -43,6 +43,7 @@ #include <osl/diagnose.h> #include <svx/svdograf.hxx> #include <comphelper/xmlencode.hxx> +#include <poolfmt.hxx> #include <fmtanchr.hxx> #include <fmtornt.hxx> @@ -289,11 +290,13 @@ SwHTMLFrameType SwHTMLWriter::GuessFrameType( const SwFrameFormat& rFrameFormat, void SwHTMLWriter::CollectFlyFrames() { SwPosFlyFrames aFlyPos( - m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), true)); + m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), + /*bDrawAlso=*/true, /*bAsCharAlso=*/true)); for(const SwPosFlyFrame& rItem : aFlyPos) { const SwFrameFormat& rFrameFormat = rItem.GetFormat(); + const SwFormat* pParent = rFrameFormat.DerivedFrom(); const SdrObject *pSdrObj = nullptr; const SwNode *pAnchorNode; const SwContentNode *pACNd; @@ -308,7 +311,6 @@ void SwHTMLWriter::CollectFlyFrames() case RndStdIds::FLY_AT_FLY: nMode = getHTMLOutFramePageFlyTable(eType, m_nExportMode); break; - case RndStdIds::FLY_AT_PARA: // frames that are anchored to a paragraph are only placed // before the paragraph, if the paragraph has a @@ -329,12 +331,18 @@ void SwHTMLWriter::CollectFlyFrames() } nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode); break; - + case RndStdIds::FLY_AS_CHAR: + // keep only Inline Heading frames from the frames anchored as characters + if ( !(pParent && pParent->GetPoolFormatId() == RES_POOLFRM_INLINE_HEADING) ) + continue; + [[fallthrough]]; case RndStdIds::FLY_AT_CHAR: if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel ) nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode); else nMode = getHTMLOutFrameParaOtherTable(eType, m_nExportMode); + if ( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR ) + nMode.nOut = HtmlOut::InlineHeading; break; default: @@ -389,6 +397,7 @@ bool SwHTMLWriter::OutFlyFrame( SwNodeOffset nNdIdx, sal_Int32 nContentIdx, Html { case HtmlOut::Div: case HtmlOut::Span: + case HtmlOut::InlineHeading: case HtmlOut::MultiCol: case HtmlOut::TableNode: bRestart = true; // It could become recursive here @@ -464,8 +473,13 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra break; case HtmlOut::Div: case HtmlOut::Span: + case HtmlOut::InlineHeading: + if( nOutMode == HtmlOut::InlineHeading ) + m_bInlineHeading = true; OSL_ENSURE( aContainerStr.isEmpty(), "Div: Container is not supposed to be here" ); - OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Span==nOutMode ); + OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Div!=nOutMode ); + if (nOutMode == HtmlOut::InlineHeading) + m_bInlineHeading = false; break; case HtmlOut::MultiCol: // OK OutHTML_FrameFormatAsMulticol( *this, rFrameFormat, !aContainerStr.isEmpty() ); @@ -488,7 +502,10 @@ void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFra static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject ); break; case HtmlOut::GraphicFrame: - OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true ); + // skip already exported inline headings + const SwFormat* pParent = rFrameFormat.DerivedFrom(); + if ( !(pParent && pParent->GetPoolFormatId() == RES_POOLFRM_INLINE_HEADING) ) + OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true ); break; } diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx index 734a86e55566..3b8e9616b359 100644 --- a/sw/source/filter/html/wrthtml.hxx +++ b/sw/source/filter/html/wrthtml.hxx @@ -282,6 +282,7 @@ class SwHTMLWriter : public Writer bool m_bLFPossible = false; // a line break can be inserted bool m_bSpacePreserve = false; // Using xml::space="preserve", or "white-space: pre-wrap" style bool m_bPreserveSpacesOnWrite = false; // If export should use m_bSpacePreserve + bool m_bInlineHeading = false; // If export should use display:inline for inline heading // If "Save URLs relative to *" is ignored for self-generated images / objects bool m_bRelativeURLsForOwnObjects = false; @@ -633,6 +634,7 @@ public: bool IsSpacePreserve() const { return m_bSpacePreserve; } void SetSpacePreserve(bool val) { m_bSpacePreserve = val; } bool IsPreserveSpacesOnWritePrefSet() const { return m_bPreserveSpacesOnWrite; } + bool IsInlineHeading() const { return m_bInlineHeading; } OUString normalizeURL(const OUString& url, bool own) const; };