diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 11:48:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 11:48:25 +0000 |
commit | 9c0049cfae49c8e4ddef9125a69db2ad134c10c6 (patch) | |
tree | 812a86c0eee63dfc5ace12f2622ed3ce9cd3d680 /svgio/source/svgreader | |
parent | Releasing progress-linux version 4:24.2.3-2~progress7.99u1. (diff) | |
download | libreoffice-9c0049cfae49c8e4ddef9125a69db2ad134c10c6.tar.xz libreoffice-9c0049cfae49c8e4ddef9125a69db2ad134c10c6.zip |
Merging upstream version 4:24.2.4.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svgio/source/svgreader')
-rw-r--r-- | svgio/source/svgreader/SvgNumber.cxx | 5 | ||||
-rw-r--r-- | svgio/source/svgreader/svgcharacternode.cxx | 115 | ||||
-rw-r--r-- | svgio/source/svgreader/svgdocumenthandler.cxx | 83 | ||||
-rw-r--r-- | svgio/source/svgreader/svgmarkernode.cxx | 2 | ||||
-rw-r--r-- | svgio/source/svgreader/svgnode.cxx | 36 | ||||
-rw-r--r-- | svgio/source/svgreader/svgstyleattributes.cxx | 80 | ||||
-rw-r--r-- | svgio/source/svgreader/svgtspannode.cxx | 5 |
7 files changed, 208 insertions, 118 deletions
diff --git a/svgio/source/svgreader/SvgNumber.cxx b/svgio/source/svgreader/SvgNumber.cxx index c1558f3e64..72a44dbdd0 100644 --- a/svgio/source/svgreader/SvgNumber.cxx +++ b/svgio/source/svgreader/SvgNumber.cxx @@ -35,10 +35,11 @@ double SvgNumber::solveNonPercentage(const InfoProvider& rInfoProvider) const switch (meUnit) { + // See https://drafts.csswg.org/css-values-4/#font-relative-length case SvgUnit::em: - return mfNumber * rInfoProvider.getCurrentFontSizeInherited(); + return mfNumber * rInfoProvider.getCurrentFontSize(); case SvgUnit::ex: - return mfNumber * rInfoProvider.getCurrentXHeightInherited() * 0.5; + return mfNumber * rInfoProvider.getCurrentXHeight(); case SvgUnit::px: return mfNumber; case SvgUnit::pt: diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index 02bc3911a3..0aea1976c1 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -138,11 +138,11 @@ namespace svgio::svgreader { // prepare retval, index and length rtl::Reference<BasePrimitive2D> pRetval; - sal_uInt32 nLength(getText().getLength()); + const sal_uInt32 nLength(getText().getLength()); if(nLength) { - sal_uInt32 nIndex(0); + const sal_uInt32 nIndex(0); // prepare FontAttribute const drawinglayer::attribute::FontAttribute aFontAttribute(getFontAttribute(rSvgStyleAttributes)); @@ -154,9 +154,12 @@ namespace svgio::svgreader // prepare locale css::lang::Locale aLocale; - // prepare TextLayouterDevice + // prepare TextLayouterDevice; use a larger font size for more linear size + // calculations. Similar to nTextSizeFactor in sd/source/ui/view/sdview.cxx + // (ViewRedirector::createRedirectedPrimitive2DSequence). + const double sizeFactor = fFontHeight < 50000 ? 50000 / fFontHeight : 1.0; TextLayouterDevice aTextLayouterDevice; - aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); + aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth * sizeFactor, fFontHeight * sizeFactor, aLocale); // prepare TextArray ::std::vector< double > aTextArray(rSvgTextPosition.getX()); @@ -190,13 +193,13 @@ namespace svgio::svgreader { fComulativeDx += aDxArray[a]; } - aTextArray.push_back(aExtendArray[a] + fStartX + fComulativeDx); + aTextArray.push_back(aExtendArray[a] / sizeFactor + fStartX + fComulativeDx); } } // get current TextPosition and TextWidth in units basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition()); - double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength)); + double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength) / sizeFactor); // check for user-given TextLength if(0.0 != rSvgTextPosition.getTextLength() @@ -209,7 +212,10 @@ namespace svgio::svgreader // spacing, need to create and expand TextArray if(aTextArray.empty()) { - aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength); + auto aExtendArray(aTextLayouterDevice.getTextArray(getText(), nIndex, nLength)); + aTextArray.reserve(aExtendArray.size()); + for (auto n : aExtendArray) + aTextArray.push_back(n / sizeFactor); } for(auto &a : aTextArray) @@ -289,12 +295,12 @@ namespace svgio::svgreader case DominantBaseline::Middle: case DominantBaseline::Central: { - aPosition.setY(aPosition.getY() - aRange.getCenterY()); + aPosition.setY(aPosition.getY() - aRange.getCenterY() / sizeFactor); break; } case DominantBaseline::Hanging: { - aPosition.setY(aPosition.getY() - aRange.getMinY()); + aPosition.setY(aPosition.getY() - aRange.getMinY() / sizeFactor); break; } default: // DominantBaseline::Auto @@ -312,12 +318,12 @@ namespace svgio::svgreader { case BaselineShift::Sub: { - aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset()); + aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset() / sizeFactor); break; } case BaselineShift::Super: { - aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset()); + aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset() / sizeFactor); break; } case BaselineShift::Percentage: @@ -480,53 +486,80 @@ namespace svgio::svgreader } } - void SvgCharacterNode::whiteSpaceHandling() + SvgCharacterNode* + SvgCharacterNode::whiteSpaceHandling(SvgCharacterNode* pPreviousCharacterNode) { bool bIsDefault(XmlSpace::Default == getXmlSpace()); // if xml:space="default" then remove all newline characters, otherwise convert them to space // convert tab to space too - maText = maTextBeforeSpaceHandling = maText.replaceAll(u"\n", bIsDefault ? u"" : u" ").replaceAll(u"\t", u" "); + maText = maText.replaceAll(u"\n", bIsDefault ? u"" : u" ").replaceAll(u"\t", u" "); - if(bIsDefault) + if (!bIsDefault) { - // strip of all leading and trailing spaces - // and consolidate contiguous space - maText = consolidateContiguousSpace(maText.trim()); - } - } + if (maText.isEmpty()) + { + // Ignore this empty node for the purpose of whitespace handling + return pPreviousCharacterNode; + } - SvgCharacterNode* SvgCharacterNode::addGap(SvgCharacterNode* pPreviousCharacterNode) - { - // maText may have lost all text. If that's the case, ignore as invalid character node - // Also ignore if maTextBeforeSpaceHandling just have spaces - if(!maText.isEmpty() && !o3tl::trim(maTextBeforeSpaceHandling).empty()) - { - if(pPreviousCharacterNode) + if (pPreviousCharacterNode && pPreviousCharacterNode->mbHadTrailingSpace) { - bool bAddGap(true); + // pPreviousCharacterNode->mbHadTrailingSpace implies its xml:space="default". + // Even if this xml:space="preserve" node is whitespace-only, the trailing space + // of the previous node is significant - restore it + pPreviousCharacterNode->maText += " "; + } + + return this; + } - // Do not add a gap if last node doesn't end with a space and - // current note doesn't start with a space - const sal_uInt32 nLastLength(pPreviousCharacterNode->maTextBeforeSpaceHandling.getLength()); - if(pPreviousCharacterNode->maTextBeforeSpaceHandling[nLastLength - 1] != ' ' && maTextBeforeSpaceHandling[0] != ' ') - bAddGap = false; + bool bHadLeadingSpace = maText.startsWith(" "); + mbHadTrailingSpace = maText.endsWith(" "); // Only set for xml:space="default" - // Do not add a gap if this node and last node are in different lines - if(pPreviousCharacterNode->mpParentLine != mpParentLine) - bAddGap = false; + // strip of all leading and trailing spaces + // and consolidate contiguous space + maText = consolidateContiguousSpace(maText.trim()); - // add in-between whitespace (single space) to the beginning of the current character node - if(bAddGap) + if (pPreviousCharacterNode) + { + if (pPreviousCharacterNode->mbHadTrailingSpace) + { + // pPreviousCharacterNode->mbHadTrailingSpace implies its xml:space="default". + // The previous node already has a pending trailing space. + if (maText.isEmpty()) { - maText = " " + maText; + // Leading spaces in this empty node are insignificant. + // Ignore this empty node for the purpose of whitespace handling + return pPreviousCharacterNode; } + // The previous node's trailing space is significant - restore it. Note that + // it is incorrect to insert a space in this node instead: the spaces in + // different nodes may have different size + pPreviousCharacterNode->maText += " "; + return this; } - // this becomes the previous character node - return this; + if (bHadLeadingSpace) + { + // This possibly whitespace-only xml:space="default" node goes after another + // node either having xml:space="default", but without a trailing space; or + // having xml:space="preserve" (in that case, it's irrelevant if that node had + // any trailing spaces). + if (!maText.isEmpty()) + { + // The leading whitespace in this node is significant - restore it + maText = " " + maText; + } + // The trailing whitespace in this node may or may not be + // significant (it will be significant, if there will be more nodes). Keep it as + // it is (even empty), but return this, to participate in whitespace handling + return this; + } } - return pPreviousCharacterNode; + // No previous node, or no leading/trailing space on the previous node's boundary: if + // this is whitespace-only, its whitespace is never significant + return maText.isEmpty() ? pPreviousCharacterNode : this; } void SvgCharacterNode::concatenate(std::u16string_view rText) diff --git a/svgio/source/svgreader/svgdocumenthandler.cxx b/svgio/source/svgreader/svgdocumenthandler.cxx index 8d2cc8849c..5e6e0d4048 100644 --- a/svgio/source/svgreader/svgdocumenthandler.cxx +++ b/svgio/source/svgreader/svgdocumenthandler.cxx @@ -66,7 +66,46 @@ namespace svgio::svgreader namespace { - svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, svgio::svgreader::SvgTspanNode* pParentLine, svgio::svgreader::SvgCharacterNode* pLast) +using CharacterNodeHandlerFunc + = svgio::svgreader::SvgCharacterNode*(svgio::svgreader::SvgCharacterNode* pCharNode, + svgio::svgreader::SvgTspanNode* pParentLine, + svgio::svgreader::SvgCharacterNode* pLast); + // clean whitespace in text span + svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgCharacterNode* pCharNode, + svgio::svgreader::SvgTspanNode* pParentLine, + svgio::svgreader::SvgCharacterNode* pLast) + { + pCharNode->setParentLine(pParentLine); + return pCharNode->whiteSpaceHandling(pLast); + } + + // set correct widths of text lines + svgio::svgreader::SvgCharacterNode* calcTextLineWidths(svgio::svgreader::SvgCharacterNode* pCharNode, + svgio::svgreader::SvgTspanNode* pParentLine, + svgio::svgreader::SvgCharacterNode* /*pLast*/) + { + if (const SvgStyleAttributes* pSvgStyleAttributes = pCharNode->getSvgStyleAttributes()) + { + const drawinglayer::attribute::FontAttribute aFontAttribute( + svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes)); + + double fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode)); + double fFontHeight(fFontWidth); + + css::lang::Locale aLocale; + drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; + aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); + double fTextWidth = aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0, + pCharNode->getText().getLength()); + pParentLine->concatenateTextLineWidth(fTextWidth); + } + return nullptr; // no pLast handling + } + + svgio::svgreader::SvgCharacterNode* walkRecursive(svgio::svgreader::SvgNode const* pNode, + svgio::svgreader::SvgTspanNode* pParentLine, + svgio::svgreader::SvgCharacterNode* pLast, + CharacterNodeHandlerFunc* pHandlerFunc) { if(pNode) { @@ -83,34 +122,9 @@ namespace { case SVGToken::Character: { - // clean whitespace in text span svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate); - pCharNode->setParentLine(pParentLine); - - pCharNode->whiteSpaceHandling(); - pLast = pCharNode->addGap(pLast); - - double fTextWidth(0.0); - - const SvgStyleAttributes* pSvgStyleAttributes = pCharNode->getSvgStyleAttributes(); - - if(pSvgStyleAttributes) - { - const drawinglayer::attribute::FontAttribute aFontAttribute( - svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes)); - - double fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode)); - double fFontHeight(fFontWidth); - - css::lang::Locale aLocale; - - drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; - aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); - fTextWidth = aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0, pCharNode->getText().getLength()); - } - - pParentLine->concatenateTextLineWidth(fTextWidth); + pLast = pHandlerFunc(pCharNode, pParentLine, pLast); break; } case SVGToken::Tspan: @@ -121,15 +135,15 @@ namespace if(!pTspanNode->getX().empty() || !pTspanNode->getY().empty()) pParentLine = pTspanNode; - // recursively clean whitespaces in subhierarchy - pLast = whiteSpaceHandling(pCandidate, pParentLine, pLast); + // recursively handle subhierarchy + pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc); break; } case SVGToken::TextPath: case SVGToken::Tref: { - // recursively clean whitespaces in subhierarchy - pLast = whiteSpaceHandling(pCandidate, pParentLine, pLast); + // recursively handle subhierarchy + pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc); break; } default: @@ -516,7 +530,12 @@ namespace if(pTextNode) { // cleanup read strings - whiteSpaceHandling(pTextNode, static_cast< SvgTspanNode*>(pTextNode), nullptr); + // First pass: handle whitespace. This works in a way that handling a following + // node may append a space to a previous node; so correct line width calculation + // may only happen after this pass finishes + walkRecursive(pTextNode, static_cast<SvgTspanNode*>(pTextNode), nullptr, whiteSpaceHandling); + // Second pass: calculate line widths + walkRecursive(pTextNode, static_cast<SvgTspanNode*>(pTextNode), nullptr, calcTextLineWidths); } } diff --git a/svgio/source/svgreader/svgmarkernode.cxx b/svgio/source/svgreader/svgmarkernode.cxx index 083471b49c..2279920634 100644 --- a/svgio/source/svgreader/svgmarkernode.cxx +++ b/svgio/source/svgreader/svgmarkernode.cxx @@ -174,7 +174,7 @@ namespace svgio::svgreader const drawinglayer::primitive2d::Primitive2DContainer& SvgMarkerNode::getMarkerPrimitives() const { - if(aPrimitives.empty() && Display::None != getDisplay()) + if(Display::None != getDisplay()) { decomposeSvgNode(const_cast< SvgMarkerNode* >(this)->aPrimitives, true); } diff --git a/svgio/source/svgreader/svgnode.cxx b/svgio/source/svgreader/svgnode.cxx index 7a45c681f1..8fdc29eab7 100644 --- a/svgio/source/svgreader/svgnode.cxx +++ b/svgio/source/svgreader/svgnode.cxx @@ -691,45 +691,23 @@ namespace { } } - double SvgNode::getCurrentFontSizeInherited() const - { - if(getParent()) - { - return getParent()->getCurrentFontSize(); - } - else - { - return 0.0; - } - } - double SvgNode::getCurrentFontSize() const { if(getSvgStyleAttributes()) return getSvgStyleAttributes()->getFontSizeNumber().solve(*this, NumberType::xcoordinate); - return getCurrentFontSizeInherited(); - } - - double SvgNode::getCurrentXHeightInherited() const - { if(getParent()) - { - return getParent()->getCurrentXHeight(); - } - else - { - return 0.0; - } + return getParent()->getCurrentFontSize(); + + return 0.0; } double SvgNode::getCurrentXHeight() const { - if(getSvgStyleAttributes()) - // for XHeight, use FontSize currently - return getSvgStyleAttributes()->getFontSizeNumber().solve(*this, NumberType::ycoordinate); - - return getCurrentXHeightInherited(); + // https://drafts.csswg.org/css-values-4/#ex + // for XHeight, use 0.5em fallback currently + // FIXME: use "x-height of the first available font" + return getCurrentFontSize() * 0.5; } void SvgNode::setId(OUString const & rId) diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx index 58bdb9add8..42e19c51fc 100644 --- a/svgio/source/svgreader/svgstyleattributes.cxx +++ b/svgio/source/svgreader/svgstyleattributes.cxx @@ -817,6 +817,16 @@ namespace svgio::svgreader rMarkerTransform.identity(); rClipRange.reset(); + // Set the current fill to the marker before calling getMarkerPrimitives, + // which calls decomposeSvgNode to decompose the children of the marker. + // If any of the children uses 'fill="context-fill"', then it will use it + const_cast<SvgStyleAttributes*>(rMarker.getSvgStyleAttributes())->maContextFill = getFill(); + + // Set the current stroke to the marker before calling getMarkerPrimitives, + // which calls decomposeSvgNode to decompose the children of the marker. + // If any of the children uses 'stroke="context-stroke"', then it will use it + const_cast<SvgStyleAttributes*>(rMarker.getSvgStyleAttributes())->maContextStroke = getStroke(); + // get marker primitive representation rMarkerPrimitives = rMarker.getMarkerPrimitives(); @@ -1287,8 +1297,12 @@ namespace svgio::svgreader maBaselineShift(BaselineShift::Baseline), maBaselineShiftNumber(0), maDominantBaseline(DominantBaseline::Auto), - maResolvingParent(32, 0), - mbStrokeDasharraySet(false) + maResolvingParent(34, 0), + mbStrokeDasharraySet(false), + mbContextFill(false), + mbContextStroke(false), + maContextFill(nullptr), + maContextStroke(nullptr) { } @@ -1308,7 +1322,11 @@ namespace svgio::svgreader OUString aURL; SvgNumber aOpacity; - if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) + if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-fill")) + { + mbContextFill = true; + } + else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) { setFill(aSvgPaint); if(aOpacity.isSet()) @@ -1353,7 +1371,11 @@ namespace svgio::svgreader OUString aURL; SvgNumber aOpacity; - if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) + if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-stroke")) + { + mbContextStroke = true; + } + else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) { maStroke = aSvgPaint; if(aOpacity.isSet()) @@ -1996,6 +2018,40 @@ namespace svgio::svgreader } } + const basegfx::BColor* SvgStyleAttributes::getContextFill() const + { + if (SVGToken::Marker == mrOwner.getType()) + return maContextFill; + + const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); + if (pSvgStyleAttributes && maResolvingParent[33] < nStyleDepthLimit) + { + ++maResolvingParent[33]; + auto ret = pSvgStyleAttributes->getContextFill(); + --maResolvingParent[33]; + return ret; + } + + return nullptr; + } + + const basegfx::BColor* SvgStyleAttributes::getContextStroke() const + { + if (SVGToken::Marker == mrOwner.getType()) + return maContextStroke; + + const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); + if (pSvgStyleAttributes && maResolvingParent[32] < nStyleDepthLimit) + { + ++maResolvingParent[32]; + auto ret = pSvgStyleAttributes->getContextStroke(); + --maResolvingParent[32]; + return ret; + } + + return nullptr; + } + bool SvgStyleAttributes::isClipPathContent() const { if (SVGToken::ClipPathNode == mrOwner.getType()) @@ -2064,6 +2120,10 @@ namespace svgio::svgreader } } } + else if (mbContextFill) + { + return getContextFill(); + } else if (maNodeFillURL.isEmpty()) { const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); @@ -2109,6 +2169,10 @@ namespace svgio::svgreader return &maStroke.getBColor(); } } + else if (mbContextStroke) + { + return getContextStroke(); + } else if (maNodeStrokeURL.isEmpty()) { const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); @@ -2642,11 +2706,11 @@ namespace svgio::svgreader if(pSvgStyleAttributes) { const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber(); + double n = aParentNumber.getNumber() * maFontSizeNumber.getNumber(); + if (SvgUnit::ex == maFontSizeNumber.getUnit()) + n *= 0.5; // FIXME: use "x-height of the first available font" - return SvgNumber( - aParentNumber.getNumber() * maFontSizeNumber.getNumber(), - aParentNumber.getUnit(), - true); + return SvgNumber(n, aParentNumber.getUnit()); } } diff --git a/svgio/source/svgreader/svgtspannode.cxx b/svgio/source/svgreader/svgtspannode.cxx index 27d714e66a..4e97c2bfc4 100644 --- a/svgio/source/svgreader/svgtspannode.cxx +++ b/svgio/source/svgreader/svgtspannode.cxx @@ -141,11 +141,6 @@ namespace svgio::svgreader } } - double SvgTspanNode::getCurrentFontSize() const - { - return getCurrentFontSizeInherited(); - } - } // end of namespace svgio::svgreader /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |