/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
class localTextBreakupHelper : public TextBreakupHelper
{ private:
SvgTextPosition& mrSvgTextPosition;
protected: /// allow user callback to allow changes to the new TextTransformation. Default /// does nothing. virtualbool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength) override;
// #i122324# if the FontFamily name ends on ' embedded' it is probably a re-import // of a SVG export with font embedding. Remove this to make font matching work. This // is pretty safe since there should be no font family names ending on ' embedded'. // Remove again when FontEmbedding is implemented in SVG import if(aFontFamily.endsWith(" embedded"))
{
aFontFamily = aFontFamily.copy(0, aFontFamily.getLength() - 9);
}
// prepare TextLayouterDevice; use a larger font size for more linear size // calculations. Similar to nTextSizeFactor in sd/source/ui/view/sdview.cxx // (ViewRedirector::createRedirectedPrimitive2DSequence). constdouble sizeFactor = fFontHeight < 50000 ? 50000 / fFontHeight : 1.0;
TextLayouterDevice aTextLayouterDevice;
aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth * sizeFactor, fFontHeight * sizeFactor, aLocale);
// Do nothing when X and Dx arrays are empty if((!aTextArray.empty() || !aDxArray.empty()) && aTextArray.size() < nLength)
{ const sal_uInt32 nArray(aTextArray.size());
aTextArray.reserve(nLength); for(size_t a = 0; a < aExtendArray.size(); ++a)
{ if (a < aDxArray.size())
{
fComulativeDx += aDxArray[a];
}
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) / sizeFactor);
if(rSvgTextPosition.getLengthAdjust())
{ // spacing, need to create and expand TextArray if(aTextArray.empty())
{ auto aExtendArray(aTextLayouterDevice.getTextArray(getText(), nIndex, nLength));
aTextArray.reserve(aExtendArray.size()); for (auto n : aExtendArray)
aTextArray.push_back(n / sizeFactor);
}
for(auto &a : aTextArray)
{
a *= fFactor;
}
} else
{ // spacing and glyphs, just apply to FontWidth
fFontWidth *= fFactor;
}
fTextWidth = rSvgTextPosition.getTextLength();
}
// get TextAlign
TextAlign aTextAlign(rSvgStyleAttributes.getTextAlign());
// map TextAnchor to TextAlign, there seems not to be a difference if(TextAnchor::notset != rSvgStyleAttributes.getTextAnchor())
{ switch(rSvgStyleAttributes.getTextAnchor())
{ case TextAnchor::start:
{ if (bRTL)
aTextAlign = TextAlign::right; else
aTextAlign = TextAlign::left; break;
} case TextAnchor::middle:
{
aTextAlign = TextAlign::center; break;
} case TextAnchor::end:
{ if (bRTL)
aTextAlign = TextAlign::left; else
aTextAlign = TextAlign::right; break;
} default:
{ break;
}
}
}
// apply TextAlign switch(aTextAlign)
{ case TextAlign::right:
{
aPosition.setX(aPosition.getX() - mpParentLine->getTextLineWidth()); break;
} case TextAlign::center:
{
aPosition.setX(aPosition.getX() - (mpParentLine->getTextLineWidth() * 0.5)); break;
} case TextAlign::notset: case TextAlign::left: case TextAlign::justify:
{ // TextAlign::notset, TextAlign::left: nothing to do // TextAlign::justify is not clear currently; handle as TextAlign::left break;
}
}
// get DominantBaseline const DominantBaseline aDominantBaseline(rSvgStyleAttributes.getDominantBaseline());
basegfx::B2DRange aRange(aTextLayouterDevice.getTextBoundRect(getText(), nIndex, nLength)); // apply DominantBaseline switch(aDominantBaseline)
{ case DominantBaseline::Middle: case DominantBaseline::Central:
{
aPosition.setY(aPosition.getY() - aRange.getCenterY() / sizeFactor); break;
} case DominantBaseline::Hanging:
{
aPosition.setY(aPosition.getY() - aRange.getMinY() / sizeFactor); break;
} default: // DominantBaseline::Auto
{ // nothing to do break;
}
}
// get BaselineShift const BaselineShift aBaselineShift(rSvgStyleAttributes.getBaselineShift());
// apply BaselineShift switch(aBaselineShift)
{ case BaselineShift::Sub:
{
aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset() / sizeFactor); break;
} case BaselineShift::Super:
{
aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset() / sizeFactor); break;
} case BaselineShift::Percentage: case BaselineShift::Length:
{ const SvgNumber aNumber(rSvgStyleAttributes.getBaselineShiftNumber()); constdouble mfBaselineShift(aNumber.solve(*this));
aPosition.setY(aPosition.getY() - mfBaselineShift); break;
} default: // BaselineShift::Baseline
{ // nothing to do break;
}
}
// get fill color
basegfx::BColor aFill(0, 0, 0); if(rSvgStyleAttributes.getFill())
aFill = *rSvgStyleAttributes.getFill();
// get fill opacity double fFillOpacity = 1.0; if (rSvgStyleAttributes.getFillOpacity().isSet())
{
fFillOpacity = rSvgStyleAttributes.getFillOpacity().getNumber();
}
// check TextDecoration and if TextDecoratedPortionPrimitive2D is needed const TextDecoration aDeco(rSvgStyleAttributes.getTextDecoration());
if(TextDecoration::underline == aDeco
|| TextDecoration::overline == aDeco
|| TextDecoration::line_through == aDeco)
{ // get the fill for decoration as described by SVG. We cannot // have different stroke colors/definitions for those, though const SvgStyleAttributes* pDecoDef = rSvgStyleAttributes.getTextDecorationDefiningSvgStyleAttributes();
if(!rSvgTextPosition.isRotated())
{
rTarget.push_back(xRef);
} else
{ // need to apply rotations to each character as given const TextSimplePortionPrimitive2D* pCandidate = dynamic_cast< const TextSimplePortionPrimitive2D* >(xRef.get());
// also consume for the implied single space
rSvgTextPosition.consumeRotation();
} else
{
OSL_ENSURE(false, "Used primitive is not a text primitive (!)");
}
}
}
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 = maText.replaceAll(u"\n", bIsDefault ? u"" : u" ").replaceAll(u"\t", u" ");
if (!bIsDefault)
{ if (maText.isEmpty())
{ // Ignore this empty node for the purpose of whitespace handling return pPreviousCharacterNode;
}
if (pPreviousCharacterNode && pPreviousCharacterNode->mbHadTrailingSpace)
{ // 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 += " ";
}
returnthis;
}
bool bHadLeadingSpace = maText.startsWith(" ");
mbHadTrailingSpace = maText.endsWith(" "); // Only set for xml:space="default"
// strip of all leading and trailing spaces // and consolidate contiguous space
maText = consolidateContiguousSpace(maText.trim());
if (pPreviousCharacterNode)
{ if (pPreviousCharacterNode->mbHadTrailingSpace)
{ // pPreviousCharacterNode->mbHadTrailingSpace implies its xml:space="default". // The previous node already has a pending trailing space. if (maText.isEmpty())
{ // 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 += " "; returnthis;
}
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 returnthis;
}
}
// 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.