tdf#154989 filter: XHTML export: avoid duplicated frames

Various updates on XSLT ODF2HTML triggered by TDF issue 154989 - esp. image/frame positioning by CSS position

(regression from commit f680b6d74209fd78c547201b2f14c6547e55c81b)

Change-Id: I4d08a67ebca7ae3808db07c828488bb9284623a3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151094
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/filter/source/xslt/odf2xhtml/export/common/body.xsl b/filter/source/xslt/odf2xhtml/export/common/body.xsl
index bcad3ab..8dfb782 100644
--- a/filter/source/xslt/odf2xhtml/export/common/body.xsl
+++ b/filter/source/xslt/odf2xhtml/export/common/body.xsl
@@ -81,7 +81,7 @@
    <!-- *************** -->

    <!-- ID / NAME of text-box -->
    <xsl:template match="@draw:name">
    <xsl:template match="@draw:name | @text:id | @xml:id">
        <xsl:attribute name="id">
            <xsl:choose>
                <xsl:when test="number(substring(.,1,1))">
diff --git a/filter/source/xslt/odf2xhtml/export/common/styles/style_mapping_css.xsl b/filter/source/xslt/odf2xhtml/export/common/styles/style_mapping_css.xsl
index c81cc9a..06e22d7 100644
--- a/filter/source/xslt/odf2xhtml/export/common/styles/style_mapping_css.xsl
+++ b/filter/source/xslt/odf2xhtml/export/common/styles/style_mapping_css.xsl
@@ -23,7 +23,7 @@


    <!-- *** Properties with a 'fo:' prefix *** -->
    <xsl:template match="@fo:background-color">
    <xsl:template match="@fo:background-color | @draw:fill-color">
        <xsl:text>background-color:</xsl:text>
        <xsl:value-of select="."/>
        <xsl:text>; </xsl:text>
diff --git a/filter/source/xslt/odf2xhtml/export/xhtml/body.xsl b/filter/source/xslt/odf2xhtml/export/xhtml/body.xsl
index fca8d9a..6ad03e2 100644
--- a/filter/source/xslt/odf2xhtml/export/xhtml/body.xsl
+++ b/filter/source/xslt/odf2xhtml/export/xhtml/body.xsl
@@ -141,15 +141,21 @@
            </xsl:choose>
            <!-- adapt page size -->
            <xsl:variable name="pageWidth" select="$pageProperties/style:page-layout-properties/@fo:page-width"/>
            <xsl:variable name="pageHeight" select="$pageProperties/style:page-layout-properties/@fo:height"/>

            <!-- multiple backgroundimages for different page styles (never used in html) -->
            <xsl:variable name="backgroundImage" select="$pageProperties/style:page-layout-properties/style:background-image"/>
            <!-- page margins & background image  -->
            <xsl:if test="$pageWidth or $pageProperties/style:page-layout-properties/@fo:* or $backgroundImage/@xlink:href">
            <xsl:if test="$pageWidth or $pageHeight or $pageProperties/style:page-layout-properties/@fo:* or $backgroundImage/@xlink:href">
                <xsl:attribute name="style">
                    <xsl:if test="$pageWidth">
                        <xsl:text>max-width:</xsl:text>
                        <xsl:value-of select="$pageWidth"/>
                        <xsl:value-of select="$pageWidth" />
                        <xsl:text>;</xsl:text>
                    </xsl:if>
                    <xsl:if test="$pageHeight">
                        <xsl:text>max-height:</xsl:text>
                        <xsl:value-of select="$pageHeight" />
                        <xsl:text>;</xsl:text>
                    </xsl:if>
                    <xsl:if test="$pageProperties/style:page-layout-properties/@fo:* or $backgroundImage/@xlink:href">
@@ -253,20 +259,20 @@
        <xsl:text>&#xa;</xsl:text>
        <xsl:element name="div">
            <xsl:variable name="dimension">
                <xsl:apply-templates select="@fo:min-width"/>
                <xsl:apply-templates select="@fo:max-width"/>
                <xsl:apply-templates select="@fo:min-height"/>
                <xsl:apply-templates select="@fo:max-height"/>
                <xsl:apply-templates select="@fo:min-width" />
                <xsl:apply-templates select="@fo:max-width" />
                <xsl:apply-templates select="@fo:min-height" />
                <xsl:apply-templates select="@fo:max-height" />
            </xsl:variable>
            <xsl:if test="$dimension">
            <xsl:if test="normalize-space($dimension)!=''">
                <xsl:attribute name="style">
                    <xsl:value-of select="$dimension"/>
                    <xsl:value-of select="$dimension" />
                </xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="@draw:name">
                <xsl:with-param name="globalData" select="$globalData"/>
            </xsl:apply-templates>

            <xsl:apply-templates select="@xml:id">
                <xsl:with-param name="globalData" select="$globalData" />
            </xsl:apply-templates>
            <xsl:apply-templates select="node()">
                <xsl:with-param name="globalData" select="$globalData"/>
            </xsl:apply-templates>
@@ -361,11 +367,12 @@
                    <xsl:call-template name="create-paragraph">
                        <xsl:with-param name="globalData" select="$globalData" />
                        <xsl:with-param name="footnotePrefix" select="$footnotePrefix" />
                        <xsl:with-param name="frameFloating" select="true()"/>
                    </xsl:call-template>
                </xsl:if>

            </xsl:when>
            <xsl:when test="draw:frame and ((normalize-space($childText) != '')  or (((count(*) - count(text:soft-page-break)) &gt; 1)))">
            <xsl:when test="name() = 'draw:frame' and ((normalize-space($childText) != '')  or (((count(*) - count(text:soft-page-break)) &gt; 1)))">
                <!-- If there is a 'draw:frame' child with text (not being whitespace alone) and more than the draw:frame alone and
                    not the draw:frame and a soft-page-break alone (which is quite often) -->

@@ -421,7 +428,7 @@
                    &#160; is an unbreakable whitespace to give content to the element and force a browser not to ignore the element -->
                <div style="clear:both; line-height:0; width:0; height:0; margin:0; padding:0;">&#160;</div>
            </xsl:when>
            <xsl:when test="text:tab and not(ancestor::text:index-body)">
            <xsl:when test="text:tab and not(ancestor::text:index-body)">                
                <!-- If there is a tabulator (ie. text:tab) within a paragraph, a heuristic for ODF tabulators creates a
                    span for every text:tab embracing the following text nodes aligning them according to the tabulator.
                    A line break or another text:tab starts a new text:span, line break even the tab counter for the line.
@@ -440,7 +447,7 @@
                            <xsl:variable name="paragraphName" select="@text:style-name" />
                            <xsl:variable name="imageParagraphStyle" select="$globalData/all-styles/style[@style:name = $paragraphName]/final-properties"/>
                            <!-- Only the left margin of the first paragraph of a list item will be added to the margin of the complete list (all levels)-->
<!-- TODO: left-margin in order with bidirectional -->
                            <!-- TODO: left-margin in order with bidirectional -->
                            <xsl:choose>
                                <xsl:when test="contains($imageParagraphStyle, 'margin-left:')">
                                    <xsl:call-template name="convert2cm">
@@ -695,6 +702,7 @@
        <xsl:param name="globalData"/>
        <!-- the footnote symbol is the prefix for a footnote in the footer -->
        <xsl:param name="footnotePrefix"/>
        <xsl:param name="frameFloating"/>

        <!-- xhtml:p may only contain inline elements.
             If there is one frame beyond, div must be used! -->
@@ -704,7 +712,10 @@
                <xsl:otherwise>p</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <xsl:if test="name() = 'text:p' and $elementName = 'div'">
            <xsl:comment>Next 'div' was a 'text:p'.</xsl:comment>
            <xsl:text>&#xa;</xsl:text>
        </xsl:if>
        <xsl:element name="{$elementName}">
            <xsl:choose>
                <!-- in ODF borders of paragraphs will be merged by default. Merging means the adjacent paragraphs are building a unit,
@@ -804,7 +815,7 @@
        <xsl:text>;</xsl:text>
    </xsl:template>

    <!-- As soon a frame is within a paragraph (text:p) or page:frame, every child element is  floating (CSS) and worked out in sequence.
    <!-- As soon a frame is within a paragraph (text:p) or page:frame, every child element is floating (CSS) and worked out in sequence.
    Accumulating prior frame width and adding parent's left margin -->
    <!-- Matching all elements and text beyond a paragraph/text:page which are sibling of a draw:frame -->
    <xsl:template match="* | text()" mode="frameFloating">
@@ -814,17 +825,17 @@
        <xsl:param name="leftPosition" select="0" />
        <xsl:param name="parentMarginLeft" />
        <xsl:param name="stopAtFirstFrame" select="false()" />
        <xsl:param name="tdf146264hack" select="false()" />
        <xsl:param name="frameFloating" select="false()" />

        <xsl:choose>
            <xsl:when test="name() = 'draw:frame' and not($stopAtFirstFrame)">

                <!-- if the first node is a draw:frame create a div -->
                <xsl:call-template name="createDrawFrame">
                    <xsl:with-param name="globalData" select="$globalData"/>
                    <xsl:with-param name="previousFrameWidths" select="$previousFrameWidths"/>
                    <xsl:with-param name="previousFrameHeights" select="$previousFrameHeights"/>
                    <xsl:with-param name="parentMarginLeft" select="$parentMarginLeft"/>
                    <xsl:with-param name="frameFloating" select="true()"/>
                </xsl:call-template>
                <!-- next elements will be called after the creation with the new indent (plus width of frame) -->
            </xsl:when>
@@ -847,7 +858,7 @@
                            <!-- This xsl:if is the meat of the extremely ugly "fix" to tdf#146264. It probably has unintended
                                 bad side-effects.
                            -->
                            <xsl:if test="not($tdf146264hack)">
                            <xsl:if test="not($frameFloating)">
                                <xsl:apply-templates select=".">
                                    <xsl:with-param name="globalData" select="$globalData"/>
                                </xsl:apply-templates>
@@ -922,13 +933,13 @@
        <xsl:param name="globalData"/>
        <xsl:param name="previousFrameWidths" select="0"/>
        <xsl:param name="previousFrameHeights" select="0" />
        <xsl:param name="tdf146264hack" select="false()" />
        <xsl:param name="frameFloating" select="false()" />

        <xsl:call-template name="createDrawFrame">
            <xsl:with-param name="globalData" select="$globalData" />
            <xsl:with-param name="previousFrameWidths" select="$previousFrameWidths"/>
            <xsl:with-param name="previousFrameHeights" select="$previousFrameHeights"/>
            <xsl:with-param name="tdf146264hack" select="$tdf146264hack"/>
            <xsl:with-param name="frameFloating" select="$frameFloating"/>
        </xsl:call-template>
        <!-- after the last draw:frame sibling the CSS float is disabled -->
        <xsl:if test="@text:anchor-type!='as-char'">
@@ -972,7 +983,7 @@
        <xsl:param name="previousFrameHeights" select="0" />
        <xsl:param name="parentMarginLeft"/>
        <xsl:param name="stopAtFirstFrame" select="false()" />
        <xsl:param name="tdf146264hack" select="false()" />
        <xsl:param name="frameFloating" select="false()" />

        <xsl:variable name="parentMarginLeftNew">
            <xsl:choose>
@@ -1052,15 +1063,8 @@
            <xsl:with-param name="parentMarginLeftNew" select="$parentMarginLeftNew"/>
            <xsl:with-param name="leftPosition" select="$leftPosition"/>
            <xsl:with-param name="svgY" select="$svgY"/>
            <xsl:with-param name="frameFloating" select="$frameFloating"/>
        </xsl:call-template>
        <xsl:apply-templates select="following-sibling::node()[1]" mode="frameFloating">
            <xsl:with-param name="globalData" select="$globalData"/>
            <xsl:with-param name="previousFrameWidths" select="$previousFrameWidths + $svgWidth"/>
            <xsl:with-param name="parentMarginLeft" select="$parentMarginLeftNew"/>
            <xsl:with-param name="leftPosition" select="$leftPosition"/>
            <xsl:with-param name="stopAtFirstFrame" select="$stopAtFirstFrame" />
            <xsl:with-param name="tdf146264hack" select="$tdf146264hack" />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template name="createDrawFrame2">
@@ -1094,18 +1098,33 @@
                    <xsl:attribute name="style">
                    <xsl:call-template name="widthAndHeight"/>
                    <xsl:text> padding:0; </xsl:text>
                    <xsl:if test="@text:anchor-type!='as-char'">
                        <!-- all images float (CSS float relative) with a left position calculated by svg:x - parentMarginLeft - previousFrameWidths -->
                        <xsl:text> float:left; position:relative; left:</xsl:text>
                        <xsl:value-of select="$leftPosition"/>
                        <xsl:text>cm; </xsl:text>
                        <!-- if the frame is anchored on a char -->
                        <xsl:if test="@text:anchor-type='char'">
                            <xsl:text>top:</xsl:text>
                            <xsl:value-of select="$svgY"/>
                    <xsl:choose>
                        <xsl:when test="@text:anchor-type='as-char'">
                            <!-- images being used as character are not floating and ar not positioned (CSS position:static being the default)-->
                            <!--<xsl:text> position:static;</xsl:text>-->
                        </xsl:when>
                        <xsl:when test="@text:anchor-type!='as-char'">
                            <!-- all images float (CSS float relative) with a left position calculated by svg:x - parentMarginLeft - previousFrameWidths -->
                            <xsl:text> float:left; position:relative; left:</xsl:text>
                                    <xsl:value-of select="$leftPosition" />
                            <xsl:text>cm; </xsl:text>
                        </xsl:if>
                    </xsl:if>
                            <!-- if the frame is anchored on a char -->
                            <xsl:if test="@text:anchor-type='char'">
                                <xsl:text>top:</xsl:text>
                                        <xsl:value-of select="$svgY" />
                                <xsl:text>cm; </xsl:text>
                            </xsl:if>
                        </xsl:when>
                        <!-- @text:anchor-type='' -->
                        <xsl:otherwise>
                                <xsl:text> position:absolute; left:</xsl:text>
                                    <xsl:value-of select="$leftPosition" />
                            <xsl:text>cm; </xsl:text>
                            <xsl:text>top:</xsl:text>
                                    <xsl:value-of select="$svgY" />
                            <xsl:text>cm; </xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:attribute>
                <xsl:apply-templates select="@*">
                    <xsl:with-param name="globalData" select="$globalData"/>
@@ -2582,7 +2601,7 @@
            <xsl:apply-templates>
                <xsl:with-param name="globalData" select="$globalData"/>
                <xsl:with-param name="listIndent" select="$minLabelWidth"/>
                <xsl:with-param name="tdf146264hack" select="true()"/>
                <xsl:with-param name="frameFloating" select="true()"/>
            </xsl:apply-templates>
            <!-- this span disables the float necessary to bring two block elements on one line. It contains a space as IE6 bug workaround -->
            <span class="odfLiEnd"></span>
@@ -2844,7 +2863,22 @@
        <xsl:param name="globalData"/>

        <xsl:attribute name="class">
            <xsl:value-of select="translate(., '.,;: %()[]/\+', '_____________')"/>
        	<xsl:choose>
        		<xsl:when test="name() = 'draw:text-style-name' or name() = 'draw:style-name'">
                    <xsl:choose>
                        <xsl:when test="parent::*/@draw:text-style-name and parent::*/@draw:style-name">
                            <xsl:value-of select="translate(parent::*/@draw:style-name, '.,;: %()[]/\+', '_____________')" /><xsl:text> </xsl:text>
                            <xsl:value-of select="translate(parent::*/@draw:text-style-name, '.,;: %()[]/\+', '_____________')" />
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="translate(., '.,;: %()[]/\+', '_____________')" />
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="translate(., '.,;: %()[]/\+', '_____________')"/>
                </xsl:otherwise>	            
	        </xsl:choose>    
        </xsl:attribute>
    </xsl:template>

diff --git a/sd/qa/unit/HtmlExportTest.cxx b/sd/qa/unit/HtmlExportTest.cxx
index 812757b..93c1a21 100644
--- a/sd/qa/unit/HtmlExportTest.cxx
+++ b/sd/qa/unit/HtmlExportTest.cxx
@@ -40,8 +40,32 @@ public:
        assertXPath(htmlDoc, "/html/head/meta[3]", "content", "2014-04-09T17:05:41.987922038");
    }

    void testTdf154989()
    {
        loadFromURL(u"tdf154989.odg");
        save("XHTML Draw File");
        xmlDocUniquePtr pXmlDoc = parseXml(maTempFile);

        assertXPath(pXmlDoc, "/xhtml:html", 1);
        // 1 page
        assertXPath(pXmlDoc, "/xhtml:html/xhtml:body/xhtml:div", 1);
        // 4 shapes
        assertXPath(pXmlDoc, "/xhtml:html/xhtml:body/xhtml:div[1]/xhtml:div", 4);
        // 3 of them are text boxes
        assertXPath(pXmlDoc, "/xhtml:html/xhtml:body/xhtml:div[1]/xhtml:div/xhtml:div", 3);
        // not sure if the order of these is stable?
        assertXPathContent(pXmlDoc,
                           "/xhtml:html/xhtml:body/xhtml:div[1]/xhtml:div[2]/xhtml:div/xhtml:p",
                           "before");
        assertXPathContent(
            pXmlDoc, "/xhtml:html/xhtml:body/xhtml:div[1]/xhtml:div[3]/xhtml:div/xhtml:p", "above");
        assertXPathContent(
            pXmlDoc, "/xhtml:html/xhtml:body/xhtml:div[1]/xhtml:div[4]/xhtml:div/xhtml:p", "below");
    }

    CPPUNIT_TEST_SUITE(SdHTMLFilterTest);
    CPPUNIT_TEST(testHTMLExport);
    CPPUNIT_TEST(testTdf154989);
    CPPUNIT_TEST_SUITE_END();
};

diff --git a/sd/qa/unit/data/tdf154989.odg b/sd/qa/unit/data/tdf154989.odg
new file mode 100644
index 0000000..5bc7e83
--- /dev/null
+++ b/sd/qa/unit/data/tdf154989.odg
Binary files differ
diff --git a/sw/qa/extras/htmlexport/xhtmlexport.cxx b/sw/qa/extras/htmlexport/xhtmlexport.cxx
index 9f5b267..923c3fe 100644
--- a/sw/qa/extras/htmlexport/xhtmlexport.cxx
+++ b/sw/qa/extras/htmlexport/xhtmlexport.cxx
@@ -76,14 +76,8 @@ DECLARE_HTMLEXPORT_TEST(testTdf118637, "tdf118637.odt")
    CPPUNIT_ASSERT(pStream);
    sal_uInt64 nLength = pStream->TellEnd();
    OString aStream(read_uInt8s_ToOString(*pStream, nLength));
    CPPUNIT_ASSERT(
        aStream.indexOf(
            "<div style=\"display:inline; position:relative; left:0cm;\">The formula  </div>")
        != -1);
    CPPUNIT_ASSERT(
        aStream.indexOf(
            "<div style=\"display:inline; position:relative; left:0cm;\">should be inline.</div>")
        != -1);
    CPPUNIT_ASSERT(aStream.indexOf("The formula  ") != -1);
    CPPUNIT_ASSERT(aStream.indexOf("should be inline.</div>") != -1);
}

DECLARE_HTMLEXPORT_TEST(testTdf145361, "tdf145361.odt")