filter/qa/unit/data/custom-bullet.fodp |   36 +++++++++++++++++++++++++++++++++
 filter/qa/unit/svg.cxx                 |   27 ++++++++++++++++++++++++
 filter/source/svg/svgexport.cxx        |   12 +++++++++++
 filter/source/svg/svgfilter.hxx        |    4 +++
 filter/source/svg/svgwriter.cxx        |   24 ++++++++++++++++++----
 filter/source/svg/svgwriter.hxx        |    2 -
 6 files changed, 100 insertions(+), 5 deletions(-)

New commits:
commit 38da870731123d8ad97b62f95c61ec8c1257d9a9
Author:     Miklos Vajna <[email protected]>
AuthorDate: Mon Jan 17 11:47:14 2022 +0100
Commit:     Mike Kaganski <[email protected]>
CommitDate: Tue Jan 18 08:20:36 2022 +0100

    SVG export: fix missing custom bullets
    
    It seems this was broken since b76628acb1ae4fc06f8c1b70ec2e0cf39356deef
    (text export support for bullets and hyperlinks, 2012-08-11), the
    problem is that SVGFilter::implEmbedBulletGlyphs() has a fixed list of
    characters that are typically used as bullets, but e.g. "-" is missing
    from that list.
    
    Fix this by improving SVGTextWriter::implWriteBulletChars() to continue
    working from those shared glyph paths when the glyph is in the fixed
    list, but otherwise call GetTextOutline() to look up the path for the
    custom bullet.
    
    (cherry picked from commit bbc4360b5beb012adf1e2652328d3e18d66224aa)
    
    Change-Id: I3de8fab8dc6c78e273629d13566d1f9f289eb752
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128501
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/filter/qa/unit/data/custom-bullet.fodp 
b/filter/qa/unit/data/custom-bullet.fodp
new file mode 100644
index 000000000000..4139260f9780
--- /dev/null
+++ b/filter/qa/unit/data/custom-bullet.fodp
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document 
xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
office:version="1.3" 
office:mimetype="application/vnd.oasis.opendocument.presentation">
+  <office:automatic-styles>
+    <style:page-layout style:name="PM1">
+      <style:page-layout-properties fo:margin-top="0cm" fo:margin-bottom="0cm" 
fo:margin-left="0cm" fo:margin-right="0cm" fo:page-width="28cm" 
fo:page-height="15.75cm" style:print-orientation="landscape"/>
+    </style:page-layout>
+    <style:style style:name="gr1" style:family="graphic" 
style:parent-style-name="standard">
+      <style:graphic-properties draw:stroke="none" svg:stroke-color="#000000" 
draw:fill="none" draw:fill-color="#ffffff" draw:auto-grow-height="true" 
draw:auto-grow-width="false" fo:max-height="0cm" fo:min-height="0cm"/>
+    </style:style>
+    <text:list-style style:name="L1">
+      <text:list-level-style-bullet text:level="1" text:bullet-char="-">
+        <style:list-level-properties text:min-label-width="0.6cm"/>
+        <style:text-properties fo:font-family="OpenSymbol" 
style:font-style-name="Regular" style:font-charset="x-symbol" 
style:use-window-font-color="true" fo:font-size="45%"/>
+      </text:list-level-style-bullet>
+    </text:list-style>
+  </office:automatic-styles>
+  <office:master-styles>
+    <style:master-page style:name="Default" style:page-layout-name="PM1">
+    </style:master-page>
+  </office:master-styles>
+  <office:body>
+    <office:presentation>
+      <draw:page draw:name="page1" draw:master-page-name="Default">
+        <draw:frame draw:style-name="gr1" draw:text-style-name="P8" 
draw:layer="layout" svg:width="9.525cm" svg:height="0.962cm" svg:x="3.175cm" 
svg:y="2.54cm">
+          <draw:text-box>
+            <text:list text:style-name="L1">
+              <text:list-item>
+                <text:p>hello</text:p>
+              </text:list-item>
+            </text:list>
+          </draw:text-box>
+        </draw:frame>
+      </draw:page>
+    </office:presentation>
+  </office:body>
+</office:document>
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index bb7600d71626..8f03e7120d75 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -179,6 +179,33 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testShapeNographic)
     xStorable->storeToURL("private:stream", 
aMediaDescriptor.getAsConstPropertyValueList());
 }
 
+CPPUNIT_TEST_FIXTURE(SvgFilterTest, testCustomBullet)
+{
+    // Given a presentation with a custom bullet:
+    load(u"custom-bullet.fodp");
+
+    // When exporting that to SVG:
+    uno::Reference<frame::XStorable> xStorable(getComponent(), 
uno::UNO_QUERY_THROW);
+    SvMemoryStream aStream;
+    uno::Reference<io::XOutputStream> xOut = new 
utl::OOutputStreamWrapper(aStream);
+    utl::MediaDescriptor aMediaDescriptor;
+    aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
+    aMediaDescriptor["OutputStream"] <<= xOut;
+    xStorable->storeToURL("private:stream", 
aMediaDescriptor.getAsConstPropertyValueList());
+
+    // Then make sure the bullet glyph is not lost:
+    aStream.Seek(STREAM_SEEK_TO_BEGIN);
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 1
+    // - Actual  : 0
+    // - XPath '//svg:g[@class='BulletChars']//svg:path' number of nodes is 
incorrect
+    // i.e. the custom bullet used '<use transform="scale(285,285)"
+    // xlink:href="#bullet-char-template-45"/>', but nobody produced a 
bullet-char-template-45,
+    // instead we need the path of the glyph inline.
+    CPPUNIT_ASSERT(!getXPath(pXmlDoc, 
"//svg:g[@class='BulletChars']//svg:path", "d").isEmpty());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index 2c8130667c6b..fc8de3b18f68 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -1531,6 +1531,7 @@ void SVGFilter::implEmbedBulletGlyph( sal_Unicode 
cBullet, const OUString & sPat
     mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "d", sPathData );
     SvXMLElementExport aPathElem( *mpSVGExport, XML_NAMESPACE_NONE, "path", 
true, true );
 
+    mpSVGExport->SetEmbeddedBulletGlyph(cBullet);
 }
 
 void SVGFilter::implExportBackgroundBitmaps()
@@ -2865,4 +2866,15 @@ void SVGExport::writeMtf( const GDIMetaFile& rMtf )
     }
 }
 
+void SVGExport::SetEmbeddedBulletGlyph(sal_Unicode cBullet)
+{
+    maEmbeddedBulletGlyphs.insert(cBullet);
+}
+
+bool SVGExport::IsEmbeddedBulletGlyph(sal_Unicode cBullet) const
+{
+    auto it = maEmbeddedBulletGlyphs.find(cBullet);
+    return it != maEmbeddedBulletGlyphs.end();
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx
index 7bdaa9d60f09..aab158971e0b 100644
--- a/filter/source/svg/svgfilter.hxx
+++ b/filter/source/svg/svgfilter.hxx
@@ -66,6 +66,7 @@ class SVGExport : public SvXMLExport
     bool    mbIsUseOpacity;
     bool    mbIsUseNativeTextDecoration;
     bool    mbIsUsePositionedCharacters;
+    std::set<sal_Unicode> maEmbeddedBulletGlyphs;
 
 public:
 
@@ -84,6 +85,9 @@ public:
 
     void writeMtf( const GDIMetaFile& rMtf );
 
+    void SetEmbeddedBulletGlyph(sal_Unicode cBullet);
+    bool IsEmbeddedBulletGlyph(sal_Unicode cBullet) const;
+
 protected:
 
     virtual void            ExportStyles_( bool /* bUsed */ ) override {}
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 57ccc67bf6ef..866594439abc 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1428,11 +1428,12 @@ void SVGTextWriter::implWriteBulletChars()
 
             SvXMLElementExport aPositioningElem( mrExport, XML_NAMESPACE_NONE, 
aXMLElemG, true, true );
 
-            // <use transform="scale(font-size)" xlink:ref="/" >
+            if (mrExport.IsEmbeddedBulletGlyph(rInfo.cBulletChar))
             {
+                // <use transform="scale(font-size)" xlink:ref="/" >
                 // Add size attribute through a scaling
-                sScaling = "scale(" + OUString::number( rInfo.nFontSize ) +
-                           "," + OUString::number( rInfo.nFontSize )+ ")";
+                sScaling = "scale(" + OUString::number( 
rInfo.aFont.GetFontHeight() ) +
+                           "," + OUString::number( rInfo.aFont.GetFontHeight() 
)+ ")";
                 mrExport.AddAttribute( XML_NAMESPACE_NONE, "transform", 
sScaling );
 
                 // Add ref attribute
@@ -1442,6 +1443,21 @@ void SVGTextWriter::implWriteBulletChars()
 
                 SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, 
"use", true, true );
             }
+            else
+            {
+                // <path d="...">
+                tools::PolyPolygon aPolyPolygon;
+                OUString aStr(rInfo.cBulletChar);
+                mpVDev->Push(PushFlags::FONT);
+                mpVDev->SetFont(rInfo.aFont);
+                if (mpVDev->GetTextOutline(aPolyPolygon, aStr))
+                {
+                    OUString 
aPathString(SVGActionWriter::GetPathString(aPolyPolygon, false));
+                    mrExport.AddAttribute(XML_NAMESPACE_NONE, "d", 
aPathString);
+                    SvXMLElementExport aPath(mrExport, XML_NAMESPACE_NONE, 
"path", true, true);
+                }
+                mpVDev->Pop();
+            }
         } // close aPositioningElem
     }
 
@@ -1696,7 +1712,7 @@ void SVGTextWriter::implWriteTextPortion( const Point& 
rPos,
             {
                 sId += ".bp";
                 BulletListItemInfo& aBulletListItemInfo = maBulletListItemMap[ 
sId ];
-                aBulletListItemInfo.nFontSize = rFont.GetFontHeight();
+                aBulletListItemInfo.aFont = rFont;
                 aBulletListItemInfo.aColor = aTextColor;
                 aBulletListItemInfo.aPos = maTextPos;
                 aBulletListItemInfo.cBulletChar = mcBulletChar;
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index 993d1162fcf3..a052fba16e87 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -189,7 +189,7 @@ class GDIMetaFile;
 
 struct BulletListItemInfo
 {
-    tools::Long nFontSize;
+    vcl::Font aFont;
     Color aColor;
     Point aPos;
     sal_Unicode cBulletChar;

Reply via email to