src/lib/VSDStyles.h          |    8 +-
 src/lib/VSDXTheme.cpp        |  131 +++++++++++++++++++++++++++++++++++++++++--
 src/lib/VSDXTheme.h          |    6 +
 src/test/Makefile.am         |    3 
 src/test/data/testfile6.vsdx |binary
 src/test/importtest.cpp      |   12 +++
 6 files changed, 152 insertions(+), 8 deletions(-)

New commits:
commit 4e1e04ef85e460dd529c7ee1aba8fd37fa0046de
Author:     Balazs Varga <[email protected]>
AuthorDate: Mon Sep 22 14:27:29 2025 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Tue Sep 23 08:49:37 2025 +0200

    Related tdf#168475 libvisio: parse gradient fill styles
    
    as well from theme and use the first SchemeClr for the shape
    (if it is referenced/used by the shape) until we support gradient
    fill completely in libvisio.
    
    Change-Id: I1d5f731a48b3c66aa3bb0536e59b4e31b4c4e4fc
    Reviewed-on: https://gerrit.libreoffice.org/c/libvisio/+/191347
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Miklos Vajna <[email protected]>

diff --git a/src/lib/VSDStyles.h b/src/lib/VSDStyles.h
index 31f7e9d..9f72b94 100644
--- a/src/lib/VSDStyles.h
+++ b/src/lib/VSDStyles.h
@@ -193,9 +193,13 @@ struct VSDFillStyle
       ASSIGN_OPTIONAL(theme->getThemeColour(qsFillColour, 
variationColorIndex), bgColour);
       ASSIGN_OPTIONAL(theme->getThemeColour(qsShadowColour, 
variationColorIndex), shadowFgColour);
       if (!!style.qsFillMatrix && style.qsFillMatrix.value() >= 0)
+      {
         ASSIGN_OPTIONAL(theme->getFillStyleColour(style.qsFillMatrix.value()), 
fgColour);
-      // Check fill style color from variationStyleScheme --> varStyle --> 
fillIdx
-      ASSIGN_OPTIONAL(theme->getStyleColour(qsFillColour, 
variationStyleIndex), fgColour);
+        if (style.qsFillMatrix.value() > 
static_cast<long>(theme->getFillStyleLstSize()))
+          ASSIGN_OPTIONAL(theme->getStyleColour(qsFillColour, 
variationStyleIndex), fgColour);
+      }
+      else
+        ASSIGN_OPTIONAL(theme->getStyleColour(qsFillColour, 
variationStyleIndex), fgColour);
     }
     ASSIGN_OPTIONAL(style.fgColour, fgColour);
     ASSIGN_OPTIONAL(style.bgColour, bgColour);
diff --git a/src/lib/VSDXTheme.cpp b/src/lib/VSDXTheme.cpp
index 69fd4ec..c16c170 100644
--- a/src/lib/VSDXTheme.cpp
+++ b/src/lib/VSDXTheme.cpp
@@ -69,7 +69,7 @@ libvisio::VSDXVariationStyleScheme::VSDXVariationStyleScheme()
 libvisio::VSDXTheme::VSDXTheme()
   : m_clrScheme(),
     m_fontScheme(),
-    m_fillStyleLst(std::vector<std::optional<libvisio::Colour>>(6)),
+    m_fillStyleLst(),
     m_variationStyleSchemeLst()
 {
 }
@@ -168,6 +168,51 @@ std::optional<libvisio::Colour> 
libvisio::VSDXTheme::readSysClr(xmlTextReaderPtr
   return retVal;
 }
 
+std::optional<libvisio::Colour> 
libvisio::VSDXTheme::readSchemeClr(xmlTextReaderPtr reader)
+{
+  std::optional<libvisio::Colour> retVal;
+  if (XML_A_SCHEMECLR == getElementToken(reader))
+  {
+    const shared_ptr<xmlChar> val(xmlTextReaderGetAttribute(reader, 
BAD_CAST("val")), xmlFree);
+    if (val)
+    {
+      std::string aSchameName((const char *)val.get());
+      if (aSchameName == "dk1")
+        retVal = m_clrScheme.m_dk1;
+      else if (aSchameName == "lt1")
+        retVal = m_clrScheme.m_lt1;
+      else if (aSchameName == "dk2")
+        retVal = m_clrScheme.m_dk2;
+      else if (aSchameName == "lt2")
+        retVal = m_clrScheme.m_lt2;
+      else if (aSchameName == "accent1")
+        retVal = m_clrScheme.m_accent1;
+      else if (aSchameName == "accent2")
+        retVal = m_clrScheme.m_accent2;
+      else if (aSchameName == "accent3")
+        retVal = m_clrScheme.m_accent3;
+      else if (aSchameName == "accent4")
+        retVal = m_clrScheme.m_accent4;
+      else if (aSchameName == "accent5")
+        retVal = m_clrScheme.m_accent5;
+      else if (aSchameName == "accent6")
+        retVal = m_clrScheme.m_accent6;
+      else if (aSchameName == "hlink")
+        retVal = m_clrScheme.m_hlink;
+      else if (aSchameName == "folHlink")
+        retVal = m_clrScheme.m_folHlink;
+      else if (aSchameName == "bkgnd")
+        retVal = m_clrScheme.m_bkgnd;
+      else
+      {
+        //TODO: handle more color schames like phClr
+        VSD_DEBUG_MSG(("VSDXTheme::readSchemeClr: unknown name %s
", val.get()));
+      }
+    }
+  }
+  return retVal;
+}
+
 void libvisio::VSDXTheme::readFontScheme(xmlTextReaderPtr reader)
 {
   VSD_DEBUG_MSG(("VSDXTheme::readFontScheme
"));
@@ -356,6 +401,9 @@ bool libvisio::VSDXTheme::readThemeColour(xmlTextReaderPtr 
reader, int idToken,
     case XML_A_SYSCLR:
       colour = readSysClr(reader);
       break;
+    case XML_A_SCHEMECLR:
+      colour = readSchemeClr(reader);
+      break;
     default:
       break;
     }
@@ -688,6 +736,7 @@ void libvisio::VSDXTheme::readFillStyleLst(xmlTextReaderPtr 
reader)
   VSD_DEBUG_MSG(("VSDXTheme::readFillStyleLst
"));
   int ret = xmlTextReaderRead(reader);
   int tokenId = getElementToken(reader);
+  m_fillStyleLst.fill(std::vector<std::optional<libvisio::Colour>>());
   if (XML_TOKEN_INVALID == tokenId)
   {
     VSD_DEBUG_MSG(("VSDXTheme::readFillStyleLst: unknown token %s
", xmlTextReaderConstName(reader)));
@@ -705,7 +754,7 @@ void libvisio::VSDXTheme::readFillStyleLst(xmlTextReaderPtr 
reader)
       {
         if (i < m_fillStyleLst.size())
         {
-          m_fillStyleLst[i] = colour;
+          m_fillStyleLst[i].push_back(colour);
         }
         else
         {
@@ -714,6 +763,11 @@ void 
libvisio::VSDXTheme::readFillStyleLst(xmlTextReaderPtr reader)
       }
       break;
     }
+    case XML_A_GRADFILL:
+    {
+      readGradFill(reader, i);
+      break;
+    }
     default:
       // Skip unimplemented fill type
       skipUnimplemented(reader, tokenId);
@@ -730,11 +784,80 @@ void 
libvisio::VSDXTheme::readFillStyleLst(xmlTextReaderPtr reader)
   }
 }
 
+void libvisio::VSDXTheme::readGradFill(xmlTextReaderPtr reader, std::size_t 
nPos)
+{
+  VSD_DEBUG_MSG(("VSDXTheme::readGradFill
"));
+  int ret = 1;
+  int tokenId = XML_TOKEN_INVALID;
+  int tokenType = -1;
+  do
+  {
+    ret = xmlTextReaderRead(reader);
+    tokenId = getElementToken(reader);
+    if (XML_TOKEN_INVALID == tokenId)
+    {
+      VSD_DEBUG_MSG(("VSDXTheme::readGradFill: unknown token %s
", xmlTextReaderConstName(reader)));
+    }
+    tokenType = xmlTextReaderNodeType(reader);
+    switch (tokenId)
+    {
+    case XML_A_GSLST:
+    {
+      readGradFillLst(reader, nPos);
+      break;
+    }
+    default:
+      break;
+    }
+  }
+  while ((XML_A_GRADFILL != tokenId || XML_READER_TYPE_END_ELEMENT != 
tokenType) && 1 == ret);
+}
+
+void libvisio::VSDXTheme::readGradFillLst(xmlTextReaderPtr reader, std::size_t 
nPos)
+{
+  VSD_DEBUG_MSG(("VSDXTheme::readGradFillLst
"));
+  int ret = xmlTextReaderRead(reader);
+  int tokenId = getElementToken(reader);
+  if (XML_TOKEN_INVALID == tokenId)
+  {
+    VSD_DEBUG_MSG(("VSDXTheme::readGradFillLst: unknown token %s
", xmlTextReaderConstName(reader)));
+  }
+  int tokenType = xmlTextReaderNodeType(reader);
+  while ((XML_A_GSLST != tokenId || XML_READER_TYPE_END_ELEMENT != tokenType) 
&& 1 == ret)
+  {
+    switch (tokenId)
+    {
+    case XML_A_GS:
+    {
+      Colour colour;
+      if (readThemeColour(reader, tokenId, colour))
+      {
+        if (nPos < m_fillStyleLst.size())
+          m_fillStyleLst[nPos].push_back(colour);
+        else
+          VSD_DEBUG_MSG(("VSDXTheme::readGradFillLst Error: Unable to add 
colour #%02x%02x%02x
", colour.r, colour.g, colour.b));
+      }
+      break;
+    }
+    default:
+      break;
+    }
+    ret = xmlTextReaderRead(reader);
+    tokenId = getElementToken(reader);
+    if (XML_TOKEN_INVALID == tokenId)
+    {
+      VSD_DEBUG_MSG(("VSDXTheme::readGradFillLst: unknown token %s
", xmlTextReaderConstName(reader)));
+    }
+    tokenType = xmlTextReaderNodeType(reader);
+  }
+}
+
 std::optional<libvisio::Colour> 
libvisio::VSDXTheme::getFillStyleColour(unsigned value) const
 {
-  if (value == 0 || value > m_fillStyleLst.size())
+  if (value == 0 || value > m_fillStyleLst.size() || m_fillStyleLst[value - 
1].empty())
     return std::optional<libvisio::Colour>();
-  return m_fillStyleLst[value - 1];
+  // TODO: only the first colour is used until we support gradient fills
+  return m_fillStyleLst[value - 1].front();
 }
 
 /* vim:set shiftwidth=2 softtabstop=2 expandtab: */
diff --git a/src/lib/VSDXTheme.h b/src/lib/VSDXTheme.h
index 728d1fb..32c8a17 100644
--- a/src/lib/VSDXTheme.h
+++ b/src/lib/VSDXTheme.h
@@ -90,6 +90,7 @@ public:
   std::optional<Colour> getThemeColour(unsigned value, unsigned variationIndex 
= 0) const;
   std::optional<Colour> getStyleColour(unsigned value, unsigned variationIndex 
= 0) const;
   std::optional<Colour> getFillStyleColour(unsigned value) const;
+  size_t getFillStyleLstSize() const { return m_fillStyleLst.size(); }
 
 private:
   VSDXTheme(const VSDXTheme &);
@@ -97,6 +98,7 @@ private:
 
   std::optional<Colour> readSrgbClr(xmlTextReaderPtr reader);
   std::optional<Colour> readSysClr(xmlTextReaderPtr reader);
+  std::optional<Colour> readSchemeClr(xmlTextReaderPtr reader);
 
   void readClrScheme(xmlTextReaderPtr reader);
   bool readThemeColour(xmlTextReaderPtr reader, int idToken, Colour &clr);
@@ -111,13 +113,15 @@ private:
   bool readTypeFace(xmlTextReaderPtr reader, int &script, 
librevenge::RVNGString &typeFace);
   void readFmtScheme(xmlTextReaderPtr reader);
   void readFillStyleLst(xmlTextReaderPtr reader);
+  void readGradFill(xmlTextReaderPtr reader, std::size_t nPos);
+  void readGradFillLst(xmlTextReaderPtr reader, std::size_t nPos);
 
   int getElementToken(xmlTextReaderPtr reader);
   void skipUnimplemented(xmlTextReaderPtr reader, int idToken);
 
   VSDXClrScheme m_clrScheme;
   VSDXFontScheme m_fontScheme;
-  std::vector<std::optional<Colour>> m_fillStyleLst;
+  std::array<std::vector<std::optional<Colour>>, 6> m_fillStyleLst;
   std::vector<VSDXVariationStyleScheme> m_variationStyleSchemeLst;
 };
 
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index cded77e..97b7e83 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -85,7 +85,8 @@ EXTRA_DIST = \
        data/testfile1.vsdx \
        data/testfile3.vsdx \
        data/testfile4.vsdx \
-       data/testfile5.vsdx
+       data/testfile5.vsdx \
+       data/testfile6.vsdx
 
 # ImportTest::testVsdMetadataTitleUtf8 checks formatted date string
 AM_TESTS_ENVIRONMENT = TZ=UTC; export TZ;
diff --git a/src/test/data/testfile6.vsdx b/src/test/data/testfile6.vsdx
new file mode 100644
index 0000000..7c443b7
Binary files /dev/null and b/src/test/data/testfile6.vsdx differ
diff --git a/src/test/importtest.cpp b/src/test/importtest.cpp
index 1fb4981..2d44159 100644
--- a/src/test/importtest.cpp
+++ b/src/test/importtest.cpp
@@ -233,6 +233,7 @@ class ImportTest : public CPPUNIT_NS::TestFixture
   CPPUNIT_TEST(testVsdxFillStylesFromTheme3);
   CPPUNIT_TEST(testVsdxFillStylesFromTheme4);
   CPPUNIT_TEST(testVsdxFillStylesFromTheme5);
+  CPPUNIT_TEST(testVsdxFillStylesFromTheme6);
 
   CPPUNIT_TEST_SUITE_END();
 
@@ -264,6 +265,7 @@ class ImportTest : public CPPUNIT_NS::TestFixture
   void testVsdxFillStylesFromTheme3();
   void testVsdxFillStylesFromTheme4();
   void testVsdxFillStylesFromTheme5();
+  void testVsdxFillStylesFromTheme6();
 
   xmlBufferPtr m_buffer;
   xmlDocPtr m_doc;
@@ -641,6 +643,16 @@ void ImportTest::testVsdxFillStylesFromTheme5()
   assertXPath(m_doc, "/document/page/layer[1]//setStyle[2]", "fill-color", 
"#4372c4");
 }
 
+void ImportTest::testVsdxFillStylesFromTheme6()
+{
+  m_doc = parse("testfile6.vsdx", m_buffer);
+  // The first shape still has a wrong fill.
+  // The expected value would be #4672c4, but we get #bdd0e9 instead.
+  //assertXPath(m_doc, "/document/page/layer[1]//setStyle[2]", "fill-color", 
"#4672c4");
+  assertXPath(m_doc, "/document/page/layer[2]//setStyle[2]", "fill-color", 
"#000000");
+  assertXPath(m_doc, "/document/page/layer[3]//setStyle[2]", "fill-color", 
"#feffff");
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(ImportTest);
 
 /* vim:set shiftwidth=2 softtabstop=2 expandtab: */

Reply via email to