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;
 };

Reply via email to