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: */
