Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/svgio/source/svgreader/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 25 kB image not shown  

Quelle  svgdocumenthandler.cxx   Sprache: C

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


#include <svgdocumenthandler.hxx>
#include <svgtoken.hxx>
#include <svgsvgnode.hxx>
#include <svggnode.hxx>
#include <svganode.hxx>
#include <svgnode.hxx>
#include <svgpathnode.hxx>
#include <svgrectnode.hxx>
#include <svggradientnode.hxx>
#include <svggradientstopnode.hxx>
#include <svgswitchnode.hxx>
#include <svgsymbolnode.hxx>
#include <svgusenode.hxx>
#include <svgcirclenode.hxx>
#include <svgellipsenode.hxx>
#include <svglinenode.hxx>
#include <svgpolynode.hxx>
#include <svgtextnode.hxx>
#include <svgcharacternode.hxx>
#include <svgtspannode.hxx>
#include <svgtrefnode.hxx>
#include <svgtextpathnode.hxx>
#include <svgstylenode.hxx>
#include <svgimagenode.hxx>
#include <svgclippathnode.hxx>
#include <svgfeblendnode.hxx>
#include <svgfecolormatrixnode.hxx>
#include <svgfecompositenode.hxx>
#include <svgfedropshadownode.hxx>
#include <svgfefloodnode.hxx>
#include <svgfeimagenode.hxx>
#include <svgfegaussianblurnode.hxx>
#include <svgfemergenode.hxx>
#include <svgfemergenodenode.hxx>
#include <svgfeoffsetnode.hxx>
#include <svgfilternode.hxx>
#include <svgmasknode.hxx>
#include <svgmarkernode.hxx>
#include <svgpatternnode.hxx>
#include <svgtitledescnode.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <com/sun/star/lang/Locale.hpp>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>

using namespace com::sun::star;

namespace svgio::svgreader
{

namespace
{
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 constpNode,
                                                      svgio::svgreader::SvgTspanNode* pParentLine,
                                                      svgio::svgreader::SvgCharacterNode* pLast,
                                                      CharacterNodeHandlerFunc* pHandlerFunc)
    {
        if(pNode)
        {
            const auto& rChilds = pNode->getChildren();
            const sal_uInt32 nCount(rChilds.size());

            for(sal_uInt32 a(0); a < nCount; a++)
            {
                svgio::svgreader::SvgNode* pCandidate = rChilds[a].get();

                if(pCandidate)
                {
                    switch(pCandidate->getType())
                    {
                        case SVGToken::Character:
                        {
                            svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);

                            pLast = pHandlerFunc(pCharNode, pParentLine, pLast);
                            break;
                        }
                        case SVGToken::Tspan:
                        {
                            svgio::svgreader::SvgTspanNode* pTspanNode = static_cast< svgio::svgreader::SvgTspanNode* >(pCandidate);

                            // If x or y exist it means it's a new line of text
                            if(!pTspanNode->getX().empty() || !pTspanNode->getY().empty())
                                pParentLine = pTspanNode;

                            // recursively handle subhierarchy
                            pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc);
                            break;
                        }
                        case SVGToken::TextPath:
                        case SVGToken::Tref:
                        {
                            // recursively handle subhierarchy
                            pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc);
                            break;
                        }
                        default:
                        {
                            OSL_ENSURE(false"Unexpected token inside SVGTokenText (!)");
                            break;
                        }
                    }
                }
            }
        }

        return pLast;
    }

// end anonymous namespace

        SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
        :   maDocument(aAbsolutePath),
            mpTarget(nullptr)
        {
        }

        SvgDocHdl::~SvgDocHdl()
        {
            if (mpTarget)
            {
                OSL_ENSURE(false"SvgDocHdl destructed with active target (!)");

                while (mpTarget->getParent())
                    mpTarget = const_cast< SvgNode* >(mpTarget->getParent());

                const SvgNodeVector& rOwnedTopLevels = maDocument.getSvgNodeVector();
                if (std::none_of(rOwnedTopLevels.begin(), rOwnedTopLevels.end(),
                                [&](std::unique_ptr<SvgNode> const & p) { return p.get() == mpTarget; }))
                    delete mpTarget;
            }
            OSL_ENSURE(maCssContents.empty(), "SvgDocHdl destructed with active css style stack entry (!)");
        }

        void SvgDocHdl::startDocument(  )
        {
            OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
            OSL_ENSURE(maCssContents.empty(), "SvgDocHdl startDocument with active css style stack entry (!)");
        }

        void SvgDocHdl::endDocument(  )
        {
            OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
            OSL_ENSURE(maCssContents.empty(), "SvgDocHdl endDocument with active css style stack entry (!)");
        }

        void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
        {
            if(aName.isEmpty())
                return;

            const SVGToken aSVGToken(StrToSVGToken(aName, false));

            switch (aSVGToken)
            {
                /// structural elements
                case SVGToken::Symbol:
                {
                    /// new basic node for Symbol. Content gets scanned, but
                    /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
                    mpTarget = new SvgSymbolNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Switch:
                {
                    /// new node for Switch
                    mpTarget = new SvgSwitchNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Defs:
                case SVGToken::G:
                {
                    /// new node for Defs/G
                    mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Svg:
                {
                    /// new node for Svg
                    mpTarget = new SvgSvgNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Use:
                {
                    /// new node for Use
                    mpTarget = new SvgUseNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::A:
                {
                    /// new node for A
                    mpTarget = new SvgANode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// shape elements
                case SVGToken::Circle:
                {
                    /// new node for Circle
                    mpTarget = new SvgCircleNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Ellipse:
                {
                    /// new node for Ellipse
                    mpTarget = new SvgEllipseNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Line:
                {
                    /// new node for Line
                    mpTarget = new SvgLineNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Path:
                {
                    /// new node for Path
                    mpTarget = new SvgPathNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Polygon:
                {
                    /// new node for Polygon
                    mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Polyline:
                {
                    /// new node for Polyline
                    mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Rect:
                {
                    /// new node for Rect
                    mpTarget = new SvgRectNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Image:
                {
                    /// new node for Image
                    mpTarget = new SvgImageNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// title and description
                case SVGToken::Title:
                case SVGToken::Desc:
                {
                    /// new node for Title and/or Desc
                    mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
                    break;
                }

                /// gradients
                case SVGToken::LinearGradient:
                case SVGToken::RadialGradient:
                {
                    mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// gradient stops
                case SVGToken::Stop:
                {
                    mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// text
                case SVGToken::Text:
                {
                    mpTarget = new SvgTextNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Tspan:
                {
                    mpTarget = new SvgTspanNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Tref:
                {
                    mpTarget = new SvgTrefNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::TextPath:
                {
                    mpTarget = new SvgTextPathNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// styles (as stylesheets)
                case SVGToken::Style:
                {
                    SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
                    mpTarget = pNew;

                    // #i125326# there are attributes, read them. This will set isTextCss to false if
                    // type attribute is different to "text/css"
                    mpTarget->parseAttributes(xAttribs);

                    if(pNew->isTextCss())
                    {
                        // if it is a Css style, allow reading text between the start and end tag (see
                        // SvgDocHdl::characters for details)
                        maCssContents.emplace_back();
                    }
                    break;
                }

                /// structural elements clip-path and mask. Content gets scanned, but
                /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
                case SVGToken::ClipPathNode:
                {
                    /// new node for ClipPath
                    mpTarget = new SvgClipPathNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Mask:
                {
                    /// new node for Mask
                    mpTarget = new SvgMaskNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeBlend:
                {
                    /// new node for feBlend
                    mpTarget = new SvgFeBlendNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeColorMatrix:
                {
                    /// new node for feColorMatrix
                    mpTarget = new SvgFeColorMatrixNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeComposite:
                {
                    /// new node for feComposite
                    mpTarget = new SvgFeCompositeNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeDropShadow:
                {
                    /// new node for feDropShadow
                    mpTarget = new SvgFeDropShadowNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeFlood:
                {
                    /// new node for feFlood
                    mpTarget = new SvgFeFloodNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeImage:
                {
                    /// new node for feImage
                    mpTarget = new SvgFeImageNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeGaussianBlur:
                {
                    /// new node for feGaussianBlur
                    mpTarget = new SvgFeGaussianBlurNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeMerge:
                {
                    /// new node for feMerge
                    mpTarget = new SvgFeMergeNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeMergeNode:
                {
                    /// new node for feMergeNode
                    mpTarget = new SvgFeMergeNodeNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::FeOffset:
                {
                    /// new node for feOffset
                    mpTarget = new SvgFeOffsetNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }
                case SVGToken::Filter:
                {
                    /// new node for Filter
                    mpTarget = new SvgFilterNode(aSVGToken, maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// structural element marker
                case SVGToken::Marker:
                {
                    /// new node for marker
                    mpTarget = new SvgMarkerNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                /// structural element pattern
                case SVGToken::Pattern:
                {
                    /// new node for pattern
                    mpTarget = new SvgPatternNode(maDocument, mpTarget);
                    mpTarget->parseAttributes(xAttribs);
                    break;
                }

                default:
                {
                    mpTarget = new SvgNode(SVGToken::Unknown, maDocument, mpTarget);
                    break;
                }
            }
        }

        void SvgDocHdl::endElement( const OUString& aName )
        {
            if(aName.isEmpty())
                return;

            if(!mpTarget)
                return;

            const SVGToken aSVGToken(StrToSVGToken(aName, false));
            SvgNode* pTextNode(SVGToken::Text == aSVGToken ? mpTarget : nullptr);
            SvgStyleNode* pCssStyle(SVGToken::Style == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr);
            SvgTitleDescNode* pSvgTitleDescNode(SVGToken::Title == aSVGToken || SVGToken::Desc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr);

            if(!mpTarget->getParent())
            {
                // last element closing, save this tree
                maDocument.appendNode(std::unique_ptr<SvgNode>(mpTarget));
            }

            mpTarget = const_cast< SvgNode* >(mpTarget->getParent());

            if (pSvgTitleDescNode && mpTarget)
            {
                const OUString& aText(pSvgTitleDescNode->getText());

                if(!aText.isEmpty())
                {
                    mpTarget->parseAttribute(aSVGToken, aText);
                }
            }

            if(pCssStyle && pCssStyle->isTextCss())
            {
                // css style parsing
                if(!maCssContents.empty())
                {
                    // need to interpret css styles and remember them as StyleSheets
                    // #125325# Caution! the Css content may contain block comments
                    // (see http://www.w3.org/wiki/CSS_basics#CSS_comments). These need
                    // to be removed first
                    const OUString aCommentFreeSource(removeBlockComments(*(maCssContents.end() - 1)));

                    if(aCommentFreeSource.getLength())
                    {
                        pCssStyle->addCssStyleSheet(aCommentFreeSource);
                    }

                    maCssContents.pop_back();
                }
                else
                {
                    OSL_ENSURE(false"Closing CssStyle, but no collector string on stack (!)");
                }
            }

            if(pTextNode)
            {
                // cleanup read strings
                // 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);
            }
        }

        void SvgDocHdl::characters( const OUString& aChars )
        {
            const sal_uInt32 nLength(aChars.getLength());

            if(!(mpTarget && nLength))
                return;

            switch(mpTarget->getType())
            {
                case SVGToken::Text:
                case SVGToken::Tspan:
                case SVGToken::TextPath:
                {
                    const auto& rChilds = mpTarget->getChildren();

                    if(!rChilds.empty())
                    {
                        SvgNode* pChild = rChilds[rChilds.size() - 1].get();
                        if ( pChild->getType() == SVGToken::Character )
                        {
                            SvgCharacterNode& rSvgCharacterNode = static_cast< SvgCharacterNode& >(*pChild);

                            // concatenate to current character span
                            rSvgCharacterNode.concatenate(aChars);
                            break;
                        }
                    }

                    // add character span as simplified tspan (no arguments)
                    // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
                    new SvgCharacterNode(maDocument, mpTarget, aChars);
                    break;
                }
                case SVGToken::Style:
                {
                    SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);

                    if(rSvgStyleNode.isTextCss())
                    {
                        // collect characters for css style
                        if(!maCssContents.empty())
                        {
                            const OUString aTrimmedChars(aChars.trim());

                            if(!aTrimmedChars.isEmpty())
                            {
                                std::vector< OUString >::iterator aString(maCssContents.end() - 1);
                                (*aString) += aTrimmedChars;
                            }
                        }
                        else
                        {
                            OSL_ENSURE(false"Closing CssStyle, but no collector string on stack (!)");
                        }
                    }
                    break;
                }
                case SVGToken::Title:
                case SVGToken::Desc:
                {
                    SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);

                    // add text directly to SvgTitleDescNode
                    rSvgTitleDescNode.concatenate(aChars);
                    break;
                }
                default:
                {
                    // characters not used by a known node
                    break;
                }
            }
        }

        void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/)
        {
        }

        void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString&&nbsp;/*aData*/)
        {
        }

        void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/)
        {
        }
// end of namespace svgio

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.