svgio/inc/svgnode.hxx | 2 svgio/inc/svgstyleattributes.hxx | 13 ++++ svgio/inc/svgtoken.hxx | 3 svgio/qa/cppunit/SvgImportTest.cxx | 47 +++++++++++++++ svgio/qa/cppunit/data/tdf129356.svg | 34 +++++++++++ svgio/qa/cppunit/data/tdf156834.svg | 7 ++ svgio/source/svgreader/svgcharacternode.cxx | 24 +++++++ svgio/source/svgreader/svgnode.cxx | 68 +++++++--------------- svgio/source/svgreader/svgstyleattributes.cxx | 43 +++++++++++++ svgio/source/svgreader/svgtoken.cxx | 6 + sw/qa/core/layout/paintfrm.cxx | 10 +-- sw/source/core/layout/paintfrm.cxx | 80 +++++++++++++++++++++++++- 12 files changed, 281 insertions(+), 56 deletions(-)
New commits: commit e7c02ea29d92bed2d713b9a3180bfb1674add908 Author: Miklos Vajna <[email protected]> AuthorDate: Mon Aug 21 08:33:14 2023 +0200 Commit: Xisco Fauli <[email protected]> CommitDate: Tue Aug 22 21:00:20 2023 +0200 tdf#156351 sw floattable: fix missing bottom border in master table The problem was that the bugdoc has a split floating table, and the bottom of the table on the first page was missing its border. The borders on the second page were correct. This happens because the cell itself has no bottom border and the layout did not extra effort to mirror the top table margin at the buttom, like Word does. Fix the problem similar to commit 53798fef2cc0b5b0b9706081a4af5ceca964a41b (Related: tdf#156351 sw floattable: fix missing top border in follow table, 2023-08-18), so we add a bottom border for master tables at a layout level, similar how a top border was already added for follow tables. Given that this kind of worked already in the past (just top borders, just the 1 row case), do this unconditionally; but if needed this could be limited to the "Word table cell" case. Change-Id: Iafdcd90226fdc425be597d36ad97fb69dca5a89a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155884 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins (cherry picked from commit 08aea5526c75ff4c5385e960bd940f10ffa19cd5) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155914 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sw/qa/core/layout/paintfrm.cxx b/sw/qa/core/layout/paintfrm.cxx index 15b9df6a4fb4..2416c6b95f8c 100644 --- a/sw/qa/core/layout/paintfrm.cxx +++ b/sw/qa/core/layout/paintfrm.cxx @@ -36,7 +36,8 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitTableBorder) // When rendering that document: std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile(); - // Then make sure that the follow table has a top border: + // Then make sure that the master table has a bottom border and the follow table has a top + // border: MetafileXmlDump aDumper; xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile); xmlXPathObjectPtr pXmlObj = getXPathNode(pXmlDoc, "//polyline[@style='solid']/point"); @@ -61,10 +62,11 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitTableBorder) } xmlXPathFreeObject(pXmlObj); // Without the accompanying fix in place, this test would have failed with: - // - Expected: 3 + // - Expected: 4 // - Actual : 2 - // i.e. the top border in the follow table was missing. - CPPUNIT_ASSERT_EQUAL(3, nHorizontalBorders); + // i.e. the bottom border in the master table and the top border in the follow table were + // missing. + CPPUNIT_ASSERT_EQUAL(4, nHorizontalBorders); } } diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx index fdc6c6140a57..361e750570e9 100644 --- a/sw/source/core/layout/paintfrm.cxx +++ b/sw/source/core/layout/paintfrm.cxx @@ -2399,6 +2399,9 @@ class SwTabFramePainter void InsertFollowTopBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, bool bWordTableCell, SwTwips nTop, SwTwips nLeft, SwTwips nRight, bool bTopIsOuter); + void InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, + bool bWordTableCell, SwTwips nBottom, SwTwips nLeft, SwTwips nRight, + bool bBottomIsOuter); void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea); void FindStylesForLine( Point&, @@ -2893,7 +2896,7 @@ void SwTabFramePainter::InsertFollowTopBorder(const SwFrame& rFrame, const SvxBo // This is then a first row in a follow table, without repeated headlines. auto pLastRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLastLower()); - if (!pLastRow && pLastRow == pThisRow) + if (!pLastRow || pLastRow == pThisRow) { return; } @@ -2929,6 +2932,79 @@ void SwTabFramePainter::InsertFollowTopBorder(const SwFrame& rFrame, const SvxBo Insert(aFollowTop, true); } +void SwTabFramePainter::InsertMasterBottomBorder(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, + bool bWordTableCell, SwTwips nBottom, SwTwips nLeft, + SwTwips nRight, bool bBottomIsOuter) +{ + // Figure out which cell to copy. + int nCol = 0; + const SwFrame* pCell = &rFrame; + while (pCell) + { + if (!pCell->GetPrev()) + { + break; + } + + ++nCol; + pCell = pCell->GetPrev(); + } + + auto pThisRow = dynamic_cast<const SwRowFrame*>(rFrame.GetUpper()); + if (!pThisRow || pThisRow->GetUpper() != &mrTabFrame) + { + return; + } + + if (mrTabFrame.IsFollow() || !mrTabFrame.GetFollow()) + { + return; + } + + // This is a master table that is split. + if (pThisRow->GetNext() || rBoxItem.GetTop() || rBoxItem.GetBottom()) + { + return; + } + + // This is then a last row in a master table. + auto pFirstRow = dynamic_cast<const SwRowFrame*>(mrTabFrame.GetLower()); + if (!pFirstRow || pFirstRow == pThisRow) + { + return; + } + + const SwFrame* pFirstCell = pFirstRow->GetLower(); + for (int i = 0; i < nCol; ++i) + { + if (!pFirstCell) + { + break; + } + + pFirstCell = pFirstCell->GetNext(); + } + if (!pFirstCell) + { + return; + } + + SwBorderAttrAccess aAccess(SwFrame::GetCache(), pFirstCell); + const SwBorderAttrs& rAttrs = *aAccess.Get(); + const SvxBoxItem& rFirstBoxItem = rAttrs.GetBox(); + if (!rFirstBoxItem.GetTop()) + { + return; + } + + // The matching (same column) row in the first row has a top border for us. + svx::frame::Style aMasterT(rFirstBoxItem.GetTop(), 1.0); + aMasterT.SetWordTableCell(bWordTableCell); + SwLineEntry aMasterBottom(nBottom, nLeft, nRight, bBottomIsOuter, aMasterT); + aMasterT.SetRefMode(svx::frame::RefMode::Begin); + Insert(aMasterBottom, true); +} + void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea) { // build 4 line entries for the 4 borders: @@ -3019,6 +3095,8 @@ void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem Insert( aTop, true ); Insert( aBottom, true ); + InsertMasterBottomBorder(rFrame, rBoxItem, bWordTableCell, nBottom, nLeft, nRight, + bBottomIsOuter); InsertFollowTopBorder(rFrame, rBoxItem, bWordTableCell, nTop, nLeft, nRight, bTopIsOuter); } commit 603e1a7563a8ab8900fc2d7b99fbc0522e815056 Author: Xisco Fauli <[email protected]> AuthorDate: Tue Aug 22 12:46:02 2023 +0200 Commit: Xisco Fauli <[email protected]> CommitDate: Tue Aug 22 21:00:13 2023 +0200 tdf#129356: handle css combinator when the element name is combined... ... with the ID or the class While at it, simplify the code a bit Change-Id: I9e36f334b884d31229568835a346d4427a47c760 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155945 Tested-by: Jenkins Reviewed-by: Xisco Fauli <[email protected]> (cherry picked from commit 0bf0871f2dabb6c11f4976a40dcc12f6f5cb90c8) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155919 diff --git a/svgio/inc/svgnode.hxx b/svgio/inc/svgnode.hxx index 0d8008d41c90..96c6ade01efa 100644 --- a/svgio/inc/svgnode.hxx +++ b/svgio/inc/svgnode.hxx @@ -125,7 +125,7 @@ namespace svgio::svgreader const OUString& aConcatenated); void fillCssStyleVectorUsingHierarchyAndSelectors( const SvgNode& rCurrent, - const OUString& aConcatenated); + std::u16string_view aConcatenated); void fillCssStyleVectorUsingParent( const SvgNode& rCurrent); diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index fde5c7ee22dc..06a5cfb63328 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -441,6 +441,29 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156168) assertXPath(pDocument, "/primitive2D/transform/polypolygonstroke[4]/line", "color", "#00ff00"); } +CPPUNIT_TEST_FIXTURE(Test, testTdf129356) +{ + Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf129356.svg"); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength())); + + drawinglayer::Primitive2dXmlDump dumper; + xmlDocUniquePtr pDocument = dumper.dumpAndParse(aSequence); + + CPPUNIT_ASSERT (pDocument); + + // Without the fix in place, this test would have failed with + // - Expected: #008000 + // - Actual : #0000ff + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[1]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[2]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[3]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[4]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[5]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[6]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[7]", "color", "#008000"); + assertXPath(pDocument, "/primitive2D/transform/polypolygoncolor[8]", "color", "#008000"); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf156034) { Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf156034.svg"); diff --git a/svgio/qa/cppunit/data/tdf129356.svg b/svgio/qa/cppunit/data/tdf129356.svg new file mode 100644 index 000000000000..46bd6935daa4 --- /dev/null +++ b/svgio/qa/cppunit/data/tdf129356.svg @@ -0,0 +1,34 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-0 0 800 800"> + + <style> + g.g1 rect {fill:green;} + g#g3 rect {fill:green;} + g.g4 #r1 {fill:green;} + g#g3 .r5 {fill:green;} + </style> + + <g class="g4 g1"> + <g class="g2"> + <rect x="0" y="0" height="50" width="50" fill="blue"></rect> + </g> + <rect x="60" y="0" height="50" width="50" fill="blue"></rect> + </g> + <g id="g3"> + <g id="g4"> + <rect x="120" y="0" height="50" width="50" fill="blue"></rect> + </g> + <rect x="180" y="0" height="50" width="50" fill="blue"></rect> + </g> + <g class="g4 g1"> + <g> + <rect id="r1" x="240" y="0" height="50" width="50" fill="blue"></rect> + </g> + <rect id="r1" x="300" y="0" height="50" width="50" fill="blue"></rect> + </g> + <g id="g3"> + <g id="g4"> + <rect class="r5" x="360" y="0" height="50" width="50" fill="blue"></rect> + </g> + <rect class="r5" x="420" y="0" height="50" width="50" fill="blue"></rect> + </g> +</svg> diff --git a/svgio/source/svgreader/svgnode.cxx b/svgio/source/svgreader/svgnode.cxx index 4f82a16ebf1e..fd80337eb5b6 100644 --- a/svgio/source/svgreader/svgnode.cxx +++ b/svgio/source/svgreader/svgnode.cxx @@ -94,7 +94,7 @@ namespace { void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors( const SvgNode& rCurrent, - const OUString& aConcatenated) + std::u16string_view aConcatenated) { const SvgDocument& rDocument = getDocument(); @@ -102,6 +102,7 @@ namespace { return; const SvgNode* pParent = rCurrent.getParent(); + OUString sCurrentType(SVGTokenToStr(rCurrent.getType())); // check for ID (highest priority) if(rCurrent.getId()) @@ -110,21 +111,17 @@ namespace { if(rId.getLength()) { - const OUString aNewConcatenated( - "#" + rId + aConcatenated); + const OUString aNewConcatenated("#" + rId + aConcatenated); + addCssStyle(rDocument, aNewConcatenated); + + if(!sCurrentType.isEmpty()) + addCssStyle(rDocument, sCurrentType + aNewConcatenated); + if(pParent) { // check for combined selectors at parent first so that higher specificity will be in front fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, aNewConcatenated); } - addCssStyle(rDocument, aNewConcatenated); - - // look further up in the hierarchy - if(!aConcatenated.isEmpty() && pParent && pParent->getId()) - { - const OUString& rParentId = pParent->getId().value(); - addCssStyle(rDocument, "#" + rParentId + aConcatenated); - } } } @@ -132,55 +129,34 @@ namespace { for(const auto &aClass : aClasses) { const OUString aNewConcatenated("." + aClass + aConcatenated); + addCssStyle(rDocument, aNewConcatenated); + + if(!sCurrentType.isEmpty()) + addCssStyle(rDocument, sCurrentType + aNewConcatenated); + if(pParent) { // check for combined selectors at parent first so that higher specificity will be in front fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, aNewConcatenated); } - addCssStyle(rDocument, aNewConcatenated); + } - // look further up in the hierarchy - if(!aConcatenated.isEmpty() && pParent) - { - std::vector <OUString> aParentClasses = parseClass(*pParent); - for(const auto &aParentClass : aParentClasses) - { - addCssStyle(rDocument, "." + aParentClass + aConcatenated); - } - } + if(!sCurrentType.isEmpty()) + { + const OUString aNewConcatenated(sCurrentType + aConcatenated); + addCssStyle(rDocument, aNewConcatenated); } - OUString sCurrentType(SVGTokenToStr(getType())); + OUString sType(SVGTokenToStr(getType())); // check for class-dependent references to CssStyles - if(sCurrentType.isEmpty()) + if(sType.isEmpty()) return; - OUString aNewConcatenated(aConcatenated); - - if(!rCurrent.getId() && !rCurrent.getClass() && 0 == aConcatenated.indexOf(sCurrentType)) - { - // no new CssStyle Selector and already starts with sCurrentType, do not concatenate; - // we pass an 'empty' node (in the sense of CssStyle Selector) - } - else - { - aNewConcatenated = sCurrentType + aConcatenated; - } - if(pParent) { // check for combined selectors at parent first so that higher specificity will be in front - fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, aNewConcatenated); - } - - addCssStyle(rDocument, aNewConcatenated); - - // check if there is a css style with element inside element - if(pParent) - { - OUString sParentType(SVGTokenToStr(pParent->getType())); - addCssStyle(rDocument, sParentType + sCurrentType); + fillCssStyleVectorUsingHierarchyAndSelectors(*pParent, sType); } } @@ -309,7 +285,7 @@ namespace { fillCssStyleVectorUsingParent(*this); // check the hierarchy for concatenated patterns of Selectors - fillCssStyleVectorUsingHierarchyAndSelectors(*this, OUString()); + fillCssStyleVectorUsingHierarchyAndSelectors(*this, std::u16string_view()); // tdf#99115, Add css selector '*' style only if the element is on top of the hierarchy commit 7ecfaad43a41f2e20f178d5693d37ce1128e2dea Author: Xisco Fauli <[email protected]> AuthorDate: Mon Aug 21 12:35:49 2023 +0200 Commit: Xisco Fauli <[email protected]> CommitDate: Tue Aug 22 21:00:06 2023 +0200 tdf#156834: Add basic support for dominant-baseline attribute Change-Id: I005d6ca6bc340d73cae639ccd09321a0a00bc4b7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155892 Tested-by: Jenkins Reviewed-by: Xisco Fauli <[email protected]> (cherry picked from commit 3b357ecb7bca4ab3844d1900eced55f46e6f8e1c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155913 diff --git a/svgio/inc/svgstyleattributes.hxx b/svgio/inc/svgstyleattributes.hxx index 0db313243a8c..acd310adbf6d 100644 --- a/svgio/inc/svgstyleattributes.hxx +++ b/svgio/inc/svgstyleattributes.hxx @@ -162,6 +162,13 @@ namespace svgio::svgreader Length }; + enum class DominantBaseline + { + Auto, + Middle, + Hanging + }; + enum class Visibility { notset, @@ -229,6 +236,8 @@ namespace svgio::svgreader BaselineShift maBaselineShift; SvgNumber maBaselineShiftNumber; + DominantBaseline maDominantBaseline; + mutable std::vector<sal_uInt16> maResolvingParent; // defines if this attributes are part of a ClipPath. If yes, @@ -444,6 +453,10 @@ namespace svgio::svgreader void setBaselineShift(const BaselineShift aBaselineShift) { maBaselineShift = aBaselineShift; } BaselineShift getBaselineShift() const; SvgNumber getBaselineShiftNumber() const; + + // DominantBaseline + void setDominantBaseline(const DominantBaseline aDominantBaseline) { maDominantBaseline = aDominantBaseline; } + DominantBaseline getDominantBaseline() const; }; } // end of namespace svgio::svgreader diff --git a/svgio/inc/svgtoken.hxx b/svgio/inc/svgtoken.hxx index 6c1a17692f22..3a4a89f285e2 100644 --- a/svgio/inc/svgtoken.hxx +++ b/svgio/inc/svgtoken.hxx @@ -188,7 +188,8 @@ namespace svgio::svgreader // text tokens Text, - BaselineShift + BaselineShift, + DominantBaseline }; SVGToken StrToSVGToken(const OUString& rStr, bool bCaseIndependent); diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index c7bf11ba32c5..fde5c7ee22dc 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -727,6 +727,30 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156777) assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", "23"); } +CPPUNIT_TEST_FIXTURE(Test, testTdf156834) +{ + Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf156834.svg"); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength())); + + drawinglayer::Primitive2dXmlDump dumper; + xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence)); + + CPPUNIT_ASSERT (pDocument); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion", 3); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "text", "Auto"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "x", "30"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", "20"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", "Middle"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "x", "30"); + //assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "y", "57"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "text", "Hanging"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "x", "30"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "y", "94"); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf104339) { Primitive2DSequence aSequenceTdf104339 = parseSvg(u"/svgio/qa/cppunit/data/tdf104339.svg"); diff --git a/svgio/qa/cppunit/data/tdf156834.svg b/svgio/qa/cppunit/data/tdf156834.svg new file mode 100644 index 000000000000..74dc1548186e --- /dev/null +++ b/svgio/qa/cppunit/data/tdf156834.svg @@ -0,0 +1,7 @@ +<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg"> + <path d="M20,20 L180,20 M20,50 L180,50 M20,80 L180,80" stroke="grey" /> + + <text dominant-baseline="auto" x="30" y="20" font-size="20">Auto</text> + <text dominant-baseline="middle" x="30" y="50" font-size="20">Middle</text> + <text dominant-baseline="Hanging" x="30" y="80" font-size="20">Hanging</text> +</svg> diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index 91ec98ae9b68..cb39d6dc542f 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -279,6 +279,30 @@ namespace svgio::svgreader } } + // get DominantBaseline + const DominantBaseline aDominantBaseline(rSvgStyleAttributes.getDominantBaseline()); + + basegfx::B2DRange aRange(aTextLayouterDevice.getTextBoundRect(getText(), nIndex, nLength)); + // apply DominantBaseline + switch(aDominantBaseline) + { + case DominantBaseline::Middle: + { + aPosition.setY(aPosition.getY() - aRange.getCenterY()); + break; + } + case DominantBaseline::Hanging: + { + aPosition.setY(aPosition.getY() - aRange.getMinY()); + break; + } + default: // DominantBaseline::Auto + { + // nothing to do + break; + } + } + // get BaselineShift const BaselineShift aBaselineShift(rSvgStyleAttributes.getBaselineShift()); diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx index 95c83c2f2c33..94209c32ac25 100644 --- a/svgio/source/svgreader/svgstyleattributes.cxx +++ b/svgio/source/svgreader/svgstyleattributes.cxx @@ -1292,7 +1292,8 @@ namespace svgio::svgreader maClipRule(FillRule::notset), maBaselineShift(BaselineShift::Baseline), maBaselineShiftNumber(0), - maResolvingParent(30, 0), + maDominantBaseline(DominantBaseline::Auto), + maResolvingParent(31, 0), mbIsClipPathContent(SVGToken::ClipPathNode == mrOwner.getType()), mbStrokeDasharraySet(false) { @@ -1964,6 +1965,26 @@ namespace svgio::svgreader } break; } + case SVGToken::DominantBaseline: + { + if(!aContent.isEmpty()) + { + if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"middle")) + { + setDominantBaseline(DominantBaseline::Middle); + } + else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"hanging")) + { + setDominantBaseline(DominantBaseline::Hanging); + } + else + { + // no DominantBaseline + setDominantBaseline(DominantBaseline::Auto); + } + } + break; + } default: { break; @@ -3113,6 +3134,26 @@ namespace svgio::svgreader return BaselineShift::Baseline; } + + DominantBaseline SvgStyleAttributes::getDominantBaseline() const + { + if(maDominantBaseline != DominantBaseline::Auto) + { + return maDominantBaseline; + } + + const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); + + if (pSvgStyleAttributes && maResolvingParent[30] < nStyleDepthLimit) + { + ++maResolvingParent[30]; + auto ret = pSvgStyleAttributes->getDominantBaseline(); + --maResolvingParent[30]; + return ret; + } + + return DominantBaseline::Auto; + } } // end of namespace svgio /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svgio/source/svgreader/svgtoken.cxx b/svgio/source/svgreader/svgtoken.cxx index 961c4ec3b5a4..492c78623f14 100644 --- a/svgio/source/svgreader/svgtoken.cxx +++ b/svgio/source/svgreader/svgtoken.cxx @@ -177,7 +177,8 @@ constexpr auto aSVGTokenMapperList = frozen::make_unordered_map<std::u16string_v { u"stroke-width", SVGToken::StrokeWidth }, { u"text", SVGToken::Text }, - { u"baseline-shift", SVGToken::BaselineShift } + { u"baseline-shift", SVGToken::BaselineShift }, + { u"dominant-baseline", SVGToken::DominantBaseline } }); // The same elements as the map above but lowercase. CSS is case insensitive @@ -334,7 +335,8 @@ constexpr auto aSVGLowerCaseTokenMapperList = frozen::make_unordered_map<std::u { u"stroke-width", SVGToken::StrokeWidth }, { u"text", SVGToken::Text }, - { u"baseline-shift", SVGToken::BaselineShift } + { u"baseline-shift", SVGToken::BaselineShift }, + { u"dominant-baseline", SVGToken::DominantBaseline } }); static_assert(sizeof(aSVGTokenMapperList) == sizeof(aSVGLowerCaseTokenMapperList),
