/* -*- 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 .
*/
if (aTokenName.isEmpty())
{ // if no TokenName advance one by force to avoid death loop, continue
OSL_ENSURE(false, "Could not interpret on current position, advancing one byte (!)");
nPos++; continue;
}
// check for '!important' CssStyle mark, currently not supported // but needs to be extracted for correct parsing
OUString aTokenImportant(u"!important"_ustr); const sal_Int32 nIndexTokenImportant(aOUTokenValue.indexOf(aTokenImportant));
if(-1 != nIndexTokenImportant)
{ // if there currently just remove it and remove spaces to have the value only
OUString aNewOUTokenValue;
// at this point the primitives in rSource are of type TextSimplePortionPrimitive2D // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill()) // set. When another fill is used and also evtl. stroke is set it gets necessary to // dismantle to geometry and add needed primitives const basegfx::BColor* pFill = getFill(); const SvgGradientNode* pFillGradient = getSvgGradientNodeFill(); const SvgPatternNode* pFillPattern = getSvgPatternNodeFill(); const basegfx::BColor* pStroke = getStroke(); const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke(); const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
basegfx::B2DPolyPolygon aMergedArea;
if(pFillGradient || pFillPattern || pStroke || pStrokeGradient || pStrokePattern)
{ // text geometry is needed, create // use neutral ViewInformation and create LineGeometryExtractor2D const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
// add fill. Use geometry even for simple color fill when stroke // is used, else text rendering and the geometry-based stroke will // normally not really match optically due to diverse system text // renderers if(aMergedArea.count() && (pFillGradient || pFillPattern || bStrokeUsed))
{ // create text fill content based on geometry
add_fill(aMergedArea, rTarget, aMergedArea.getB2DRange());
} elseif(pFill)
{ // add the already prepared primitives for single color fill
rTarget.append(std::move(rSource));
}
if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
{ // all possible units
aStart.setX(rFillGradient.getCx().solve(mrOwner, NumberType::xcoordinate));
aStart.setY(rFillGradient.getCy().solve(mrOwner, NumberType::ycoordinate));
fRadius = rFillGradient.getR().solve(mrOwner);
// build the reference range relative to the rGeoRange const basegfx::B2DRange aReferenceRange(fX, fY, fX + fW, fY + fH);
// find out how the content is mapped to the reference range
basegfx::B2DHomMatrix aMapPrimitivesToUnitRange; const basegfx::B2DRange* pViewBox = rFillPattern.getViewBox();
if (fStrokeOpacity <= 0.0 || basegfx::fTools::equalZero(fStrokeOpacity)) return;
// get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all constdouble fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0);
if (fStrokeWidth <= 0.0 || basegfx::fTools::equalZero(fStrokeWidth)) return;
// if we have a line with two identical points it is not really a line, // but used by SVG sometimes to paint a single dot.In that case, create // the geometry for a single dot if(1 == rPath.count())
{ const basegfx::B2DPolygon& aSingle(rPath.getB2DPolygon(0));
if(aMergedArea.count())
{ if(pStrokeGradient)
{ // create fill content with SVG gradient primitive. Use original GeoRange, // e.g. from circle without LineWidth
add_fillGradient(aMergedArea, aNewStroke, *pStrokeGradient, rGeoRange);
} else// if(pStrokePattern)
{ // create fill content with SVG pattern primitive. Use GeoRange // from the expanded data, e.g. circle with extended geo by half linewidth
add_fillPatternTransform(aMergedArea, aNewStroke, *pStrokePattern, aMergedArea.getB2DRange());
}
}
}
} else// if(pStroke)
{
aNewStroke.push_back(aNewLinePrimitive);
}
// Set the current style attributes to the marker before calling getMarkerPrimitives, // which calls decomposeSvgNode to decompose the children of the marker. // If any children uses 'context-fill' or 'context-stroke', // then these style attributes will be used in add_fill or add_stroke const_cast<SvgMarkerNode&>(rMarker).setContextStyleAttributes(this);
// get marker primitive representation
rMarkerPrimitives = rMarker.getMarkerPrimitives();
if(rRatio.isSet() && Overflow::visible != rMarker.getSvgStyleAttributes()->getOverflow())
{ // let mapping be created from SvgAspectRatio
rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange);
if(rRatio.isMeetOrSlice())
{ // need to clip
rClipRange = aPrimitiveRange;
}
} else
{ if(!pViewBox)
{ if(bStrokeWidth)
{ // adapt to strokewidth if needed
rMarkerTransform.scale(fStrokeWidth, fStrokeWidth);
}
} else
{ // choose default mapping
rMarkerTransform = SvgAspectRatio::createLinearMapping(aTargetRange, aPrimitiveRange);
}
}
// get and apply reference point. Initially it's in marker local coordinate system
basegfx::B2DPoint aRefPoint(
rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, NumberType::xcoordinate) : 0.0,
rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, NumberType::ycoordinate) : 0.0);
// apply MarkerTransform to have it in mapped coordinates
aRefPoint *= rMarkerTransform;
// apply by moving RepPoint to (0.0)
rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY());
// remember prepared marker; pStart, pMid and pEnd may all be equal when // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end', // see 'case SVGToken::Marker' in this file; thus in this case only one common // marker in primitive form will be prepared const SvgMarkerNode* pPrepared = nullptr;
// values for the prepared marker, results of prepare_singleMarker
drawinglayer::primitive2d::Primitive2DContainer aPreparedMarkerPrimitives;
basegfx::B2DHomMatrix aPreparedMarkerTransform;
basegfx::B2DRange aPreparedMarkerClipRange;
for (sal_uInt32 a(0); a < nSubPathCount; a++)
{ // iterate over sub-paths const basegfx::B2DPolygon& aSubPolygonPath(rPath.getB2DPolygon(a)); const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count()); constbool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed());
if(nSubPolygonPointCount)
{ // for each sub-path, create one marker per point (when closed, two markers // need to pe created for the 1st point) const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount);
for (sal_uInt32 b(0); b < nTargetMarkerCount; b++)
{ constbool bIsFirstMarker(!a && !b); constbool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b); const SvgMarkerNode* pNeeded = nullptr;
if(bIsFirstMarker)
{ // 1st point in 1st sub-polygon, use pStart
pNeeded = pStart;
} elseif(bIsLastMarker)
{ // last point in last sub-polygon, use pEnd
pNeeded = pEnd;
} else
{ // anything in-between, use pMid
pNeeded = pMid;
}
if(aFound != pHelpPointIndices->end())
{ // this point is a pure helper point; do not create a marker for it continue;
}
}
if(!pNeeded)
{ // no marker needs to be created for this point continue;
}
if(pPrepared != pNeeded)
{ // if needed marker is not yet prepared, do it now if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded))
{
pPrepared = pNeeded;
} else
{ // error: could not prepare given marker
OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)");
pPrepared = nullptr; continue;
}
}
// get entering and leaving tangents; this will search backward/forward // in the polygon to find tangents unequal to zero, skipping empty edges // see basegfx descriptions) // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker // and entering tangent for end marker. To achieve this (if wanted) it is possible // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker. // This is not done here, see comment 14 in task #1232379# // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
basegfx::B2DVector aEntering(
basegfx::utils::getTangentEnteringPoint(
aSubPolygonPath,
nPointIndex));
basegfx::B2DVector aLeaving(
basegfx::utils::getTangentLeavingPoint(
aSubPolygonPath,
nPointIndex)); constbool bEntering(!aEntering.equalZero()); constbool bLeaving(!aLeaving.equalZero());
// prepare marker
drawinglayer::primitive2d::Primitive2DReference xMarker( new drawinglayer::primitive2d::TransformPrimitive2D(
aCombinedTransform,
drawinglayer::primitive2d::Primitive2DContainer(aPreparedMarkerPrimitives)));
if(!aPreparedMarkerClipRange.isEmpty())
{ // marker needs to be clipped, it's bigger as the mapping
basegfx::B2DPolyPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aPreparedMarkerClipRange));
if(basegfx::fTools::equalZero(fOpacity))
{ // not visible return;
}
// check if it's a line constbool bNoWidth(basegfx::fTools::equalZero(aGeoRange.getWidth())); constbool bNoHeight(basegfx::fTools::equalZero(aGeoRange.getHeight())); constbool bIsTwoPointLine(1 == rPath.count()
&& !rPath.areControlPointsUsed()
&& 2 == rPath.getB2DPolygon(0).count()); constbool bIsLine(bIsTwoPointLine || bNoWidth || bNoHeight);
if(!bIsLine)
{ // create fill
basegfx::B2DPolyPolygon aPath(rPath);
if(SVGToken::Path == mrOwner.getType() || SVGToken::Polygon == mrOwner.getType())
{ if(FillRule::evenodd != getClipRule() && FillRule::evenodd != getFillRule())
{ if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill())
{ // nonzero is wanted, solve geometrically (see description on basegfx) // basegfx::utils::createNonzeroConform() is expensive for huge paths // and is only needed if path will be filled later on
aPath = basegfx::utils::createNonzeroConform(aPath);
}
}
}
if(pTransform)
{ // create embedding group element with transformation. This applies the given // transformation to the graphical content, but *not* to mask and/or clip (as needed) const drawinglayer::primitive2d::Primitive2DReference xRef( new drawinglayer::primitive2d::TransformPrimitive2D(
*pTransform,
std::move(aSource)));
const SvgClipPathNode* pClip = accessClipPathXLink(); while(pClip)
{ // #i124852# transform may be needed when SvgUnits::userSpaceOnUse
pClip->apply(aSource, pTransform);
pClip = pClip->getSvgStyleAttributes()->accessClipPathXLink();
}
if(!aSource.empty()) // test again, applied clipPath may have lead to empty geometry
{ const SvgFilterNode* pFilter = accessFilterXLink(); if(pFilter)
{
pFilter->apply(aSource, nullptr);
}
}
if(!aSource.empty()) // test again, applied filter may have lead to empty geometry
{
const SvgMaskNode* pMask = accessMaskXLink(); if(pMask)
{ // #i124852# transform may be needed when SvgUnits::userSpaceOnUse
pMask->apply(aSource, pTransform);
}
}
// This is part of the SVG import of self-written SVGs from // Draw/Impress containing multiple Slides/Pages. To be able // to later 'break' these to multiple Pages if wanted, embed // each Page-Content in an identifiable Primitive Grouping // Object. // This is the case when the current Node is a GroupNode, has // class="Page" set, has a parent that also is a GroupNode // at which class="Slide" is set. // Multiple Slides/Pages are possible for Draw and Impress. if(SVGToken::G == mrOwner.getType() && mrOwner.getClass())
{ const OUString aOwnerClass(*mrOwner.getClass());
if("Slide" == aParentClass)
{ // embed to grouping primitive to identify the // Slide/Page information const drawinglayer::primitive2d::Primitive2DReference xRef( new drawinglayer::primitive2d::PageHierarchyPrimitive2D(
std::move(aSource)));
if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"none"))
{ // #121221# The special value 'none' needs to be handled // in the sense that *when* it is set, the parent shall not // be used. Before this was only dependent on the array being // empty
mbStrokeDasharraySet = true;
} elseif(readSvgNumberVector(aContent, aVector))
{
maStrokeDasharray = std::move(aVector);
}
} break;
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.8 Sekunden
(vorverarbeitet)
¤
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.