Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/oox/source/shape/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 48 kB image not shown  

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


#include "WpsContext.hxx"
#include "WpgContext.hxx"
#include "WordprocessingCanvasContext.hxx"
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <drawingml/customshapegeometry.hxx>
#include <drawingml/customshapeproperties.hxx>
#include <drawingml/fontworkhelpers.hxx>
#include <drawingml/textbody.hxx>
#include <drawingml/textbodyproperties.hxx>
#include <oox/drawingml/color.hxx>
#include <oox/drawingml/connectorshapecontext.hxx>
#include <oox/drawingml/drawingmltypes.hxx>
#include <oox/drawingml/shape.hxx>
#include <oox/drawingml/shapepropertymap.hxx>
#include <oox/helper/attributelist.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/tokens.hxx>
#include <svx/svdoashp.hxx>

#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/drawing/HomogenMatrix3.hpp>
#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextCursor.hpp>
#include <com/sun/star/text/WritingMode.hpp>
#include <com/sun/star/text/WritingMode2.hpp>

#include <optional>

using namespace com::sun::star;

namespace
{
bool lcl_getTextPropsFromFrameText(const uno::Reference<text::XText>& xText,
                                   std::vector<beans::PropertyValue>& rTextPropVec)
{
    if (!xText.is())
        return false;
    uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
    xTextCursor->gotoStart(false);
    xTextCursor->gotoEnd(true);
    uno::Reference<container::XEnumerationAccess> paraEnumAccess(xText, uno::UNO_QUERY);
    if (!paraEnumAccess.is())
        return false;
    uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
    while (paraEnum->hasMoreElements())
    {
        uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
        uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
        if (!runEnumAccess.is())
            continue;
        uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
        while (runEnum->hasMoreElements())
        {
            uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
            if (xRun->getString().isEmpty())
                continue;
            uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
            if (!xRunPropSet.is())
                continue;
            auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
            if (!xRunPropSetInfo.is())
                continue;

            // We have found a non-empty run. Collect its properties.
            auto aRunPropInfoSequence = xRunPropSetInfo->getProperties();
            for (const beans::Property& aProp : aRunPropInfoSequence)
            {
                rTextPropVec.push_back(comphelper::makePropertyValue(
                    aProp.Name, xRunPropSet->getPropertyValue(aProp.Name)));
            }
            return true;
        }
    }
    return false;
}

// CharInteropGrabBag puts all attributes of an element into a property with Name="attributes" and
// Value being a sequence of the attributes. This methods finds the value of an individual rName
// attribute and puts it into rValue parameter. If it does not find it, rValue is unchanged and
// the method returns false, otherwise it returns true.
bool lcl_getAttributeAsString(const uno::Sequence<beans::PropertyValue>& aPropertyValueAsSeq,
                              const OUString& rName, OUString& rValue)
{
    comphelper::SequenceAsHashMap aPropertyValueAsMap(aPropertyValueAsSeq);
    uno::Sequence<beans::PropertyValue> aAttributesSeq;
    if (!((aPropertyValueAsMap.getValue(u"attributes"_ustr) >>= aAttributesSeq)
          && aAttributesSeq.hasElements()))
        return false;
    comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
    OUString sRet;
    if (!(aAttributesMap.getValue(rName) >>= sRet))
        return false;
    rValue = sRet;
    return true;
}

// Same as above for a number as attribute value
bool lcl_getAttributeAsNumber(const uno::Sequence<beans::PropertyValue>& rPropertyValueAsSeq,
                              const OUString& rName, sal_Int32& rValue)
{
    comphelper::SequenceAsHashMap aPropertyValueAsMap(rPropertyValueAsSeq);
    uno::Sequence<beans::PropertyValue> aAttributesSeq;
    if (!((aPropertyValueAsMap.getValue(u"attributes"_ustr) >>= aAttributesSeq)
          && aAttributesSeq.hasElements()))
        return false;
    comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
    sal_Int32 nRet;
    if (!(aAttributesMap.getValue(rName) >>= nRet))
        return false;
    rValue = nRet;
    return true;
}

void lcl_getColorTransformationsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rPropSeq,
                                            oox::drawingml::Color& rColor)
{
    auto isValidPropName = [](const OUString& rName) -> bool {
        return rName == u"tint" || rName == u"shade" || rName == u"alpha" || rName == u"hueMod"
               || rName == u"sat" || rName == u"satOff" || rName == u"satMod" || rName == u"lum"
               || rName == u"lumOff" || rName == u"lumMod";
    };
    for (auto it = rPropSeq.begin(); it < rPropSeq.end(); ++it)
    {
        if (isValidPropName((*it).Name))
        {
            uno::Sequence<beans::PropertyValue> aValueSeq;
            sal_Int32 nNumber(0); // dummy value to make compiler happy, "val" should exist
            if (((*it).Value >>= aValueSeq)
                && lcl_getAttributeAsNumber(aValueSeq, u"val"_ustr, nNumber))
            {
                // char w14:alpha contains transparency, whereas shape fill a:alpha contains opacity.
                if ((*it).Name == u"alpha")
                    rColor.addTransformation(
                        oox::NMSP_dml | oox::AttributeConversion::decodeToken((*it).Name),
                        oox::drawingml::MAX_PERCENT - nNumber);
                else
                    rColor.addTransformation(
                        oox::NMSP_w14 | oox::AttributeConversion::decodeToken((*it).Name), nNumber);
            }
        }
    }
}

// Expected: rPropSeq contains a property "schemeClr" or a property "srgbClr".
bool lcl_getColorFromPropSeq(const uno::Sequence<beans::PropertyValue>& rPropSeq,
                             oox::drawingml::Color& rColor)
{
    bool bColorFound = false;
    comphelper::SequenceAsHashMap aPropMap(rPropSeq);
    uno::Sequence<beans::PropertyValue> aColorDetailSeq;
    if (aPropMap.getValue(u"schemeClr"_ustr) >>= aColorDetailSeq)
    {
        OUString sColorString;
        bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val"_ustr, sColorString);
        if (bColorFound)
        {
            sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
            rColor.setSchemeClr(nColorToken);
            rColor.setSchemeName(sColorString);
        }
    }
    if (!bColorFound && (aPropMap.getValue(u"srgbClr"_ustr) >>= aColorDetailSeq))
    {
        OUString sColorString;
        bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val"_ustr, sColorString);
        if (bColorFound)
        {
            sal_Int32 nColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
            rColor.setSrgbClr(nColor);
        }
    }
    // Without color, color transformations are pointless.
    if (bColorFound)
        lcl_getColorTransformationsFromPropSeq(aColorDetailSeq, rColor);
    return bColorFound;
}

void lcl_getFillDetailsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rTextFillSeq,
                                   oox::drawingml::FillProperties& rFillProperties)
{
    // rTextFillSeq should have an item containing either "noFill" or "solidFill" or "gradFill"
    // property.
    if (!rTextFillSeq.hasElements())
        return;
    comphelper::SequenceAsHashMap aTextFillMap(rTextFillSeq);
    if (aTextFillMap.contains(u"noFill"_ustr))
    {
        rFillProperties.moFillType = oox::XML_noFill;
        return;
    }

    uno::Sequence<beans::PropertyValue> aPropSeq;
    if ((aTextFillMap.getValue(u"solidFill"_ustr) >>= aPropSeq) && aPropSeq.hasElements())
    {
        rFillProperties.moFillType = oox::XML_solidFill;
        lcl_getColorFromPropSeq(aPropSeq, rFillProperties.maFillColor);
        return;
    }

    if ((aTextFillMap.getValue(u"gradFill"_ustr) >>= aPropSeq) && aPropSeq.hasElements())
    {
        rFillProperties.moFillType = oox::XML_gradFill;
        // aPropSeq should have two items. One is "gsLst" for the stop colors, the other is
        // either "lin" or "path" for the kind of gradient.
        // First get stop colors
        comphelper::SequenceAsHashMap aPropMap(aPropSeq);
        uno::Sequence<beans::PropertyValue> aGsLstSeq;
        if (aPropMap.getValue(u"gsLst"_ustr) >>= aGsLstSeq)
        {
            for (auto it = aGsLstSeq.begin(); it < aGsLstSeq.end(); ++it)
            {
                // (*it) is a bean::PropertyValue with Name="gs". Its Value is a property sequence.
                uno::Sequence<beans::PropertyValue> aColorStopSeq;
                if ((*it).Value >>= aColorStopSeq)
                {
                    // aColorStopSeq should have an item for the color and an item for the position
                    sal_Int32 nPos;
                    oox::drawingml::Color aColor;
                    if (lcl_getAttributeAsNumber(aColorStopSeq, u"pos"_ustr, nPos)
                        && lcl_getColorFromPropSeq(aColorStopSeq, aColor))
                    {
                        // The position in maGradientStops is relative, thus in range [0.0;1.0].
                        double fPos = nPos / 100000.0;
                        rFillProperties.maGradientProps.maGradientStops.insert({ fPos, aColor });
                    }
                }
            }
        }
        // Now determine kind of gradient.
        uno::Sequence<beans::PropertyValue> aKindSeq;
        if (aPropMap.getValue(u"lin"_ustr) >>= aKindSeq)
        {
            // aKindSeq contains the attributes "ang" and "scaled"
            sal_Int32 nAngle; // in 1/60000 deg
            if (lcl_getAttributeAsNumber(aKindSeq, u"ang"_ustr, nAngle))
                rFillProperties.maGradientProps.moShadeAngle = nAngle;
            OUString sScaledString;
            if (lcl_getAttributeAsString(aKindSeq, u"scaled"_ustr, sScaledString))
                rFillProperties.maGradientProps.moShadeScaled
                    = sScaledString == u"1" || sScaledString == u"true";
            return;
        }
        if (aPropMap.getValue(u"path"_ustr) >>= aKindSeq)
        {
            // aKindSeq contains the attribute "path" for the kind of path and a property "fillToRect"
            // which defines the center rectangle of the gradient. The property "a:tileRect" known from
            // fill of shapes does not exist in w14 namespace.
            OUString sKind;
            if (lcl_getAttributeAsString(aKindSeq, u"path"_ustr, sKind))
                rFillProperties.maGradientProps.moGradientPath
                    = oox::AttributeConversion::decodeToken(sKind);
            comphelper::SequenceAsHashMap aKindMap(aKindSeq);
            uno::Sequence<beans::PropertyValue> aFillToRectSeq;
            if (aKindMap.getValue(u"fillToRect"_ustr) >>= aFillToRectSeq)
            {
                // The values l, t, r and b are not coordinates, but determine an offset from the
                // edge of the bounding box of the shape. This unusual meaning of X1, Y1, X2 and
                // Y2 is needed for method pushToPropMap() of FillProperties.
                geometry::IntegerRectangle2D aRect;
                if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"l"_ustr, aRect.X1))
                    aRect.X1 = 0;
                if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"t"_ustr, aRect.Y1))
                    aRect.Y1 = 0;
                if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"r"_ustr, aRect.X2))
                    aRect.X2 = 0;
                if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"b"_ustr, aRect.Y2))
                    aRect.Y2 = 0;
                rFillProperties.maGradientProps.moFillToRect = aRect;
            }
        }
        return;
    }
}

void lcl_getLineDetailsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rTextOutlineSeq,
                                   oox::drawingml::LineProperties& rLineProperties)
{
    if (!rTextOutlineSeq.hasElements())
    {
        rLineProperties.maLineFill.moFillType = oox::XML_noFill; // MS Office default
        return;
    }
    // aTextOulineSeq contains e.g. "attributes" {w, cap, cmpd, ctr}, either
    // "solidFill" or "gradFill or "noFill", and "prstDash" and "lineJoint" properties.

    // Fill
    lcl_getFillDetailsFromPropSeq(rTextOutlineSeq, rLineProperties.maLineFill);

    // LineJoint
    comphelper::SequenceAsHashMap aTextOutlineMap(rTextOutlineSeq);
    if (aTextOutlineMap.contains(u"bevel"_ustr))
        rLineProperties.moLineJoint = oox::XML_bevel;
    else if (aTextOutlineMap.contains(u"round"_ustr))
        rLineProperties.moLineJoint = oox::XML_round;
    else if (aTextOutlineMap.contains(u"miter"_ustr))
    {
        // LineProperties has no member to store a miter limit. Therefore some heuristic is
        // added here. 0 is default for attribute "lim" in MS Office. It is rendered same as bevel.
        sal_Int32 nMiterLimit
            = aTextOutlineMap.getUnpackedValueOrDefault(u"lim"_ustr, sal_Int32(0));
        if (nMiterLimit == 0)
            rLineProperties.moLineJoint = oox::XML_bevel;
        else
            rLineProperties.moLineJoint = oox::XML_miter;
    }

    // Dash
    uno::Sequence<beans::PropertyValue> aDashSeq;
    if (aTextOutlineMap.getValue(u"prstDash"_ustr) >>= aDashSeq)
    {
        // aDashSeq contains the attribute "val" with the kind of dash, e.g. "sysDot"
        OUString sDashKind;
        if (lcl_getAttributeAsString(aDashSeq, u"val"_ustr, sDashKind))
            rLineProperties.moPresetDash = oox::AttributeConversion::decodeToken(sDashKind);
    }
    OUString sCapKind;
    if (lcl_getAttributeAsString(rTextOutlineSeq, u"cap"_ustr, sCapKind))
        rLineProperties.moLineCap = oox::AttributeConversion::decodeToken(sCapKind);

    // Width
    sal_Int32 nWidth; // EMU
    if (lcl_getAttributeAsNumber(rTextOutlineSeq, u"w"_ustr, nWidth))
        rLineProperties.moLineWidth = nWidth;

    // Compound. LineProperties has a member for it, however Fontwork can currently only render "sng".
    OUString sCompoundKind;
    if (lcl_getAttributeAsString(rTextOutlineSeq, u"cmpd"_ustr, sCompoundKind))
        rLineProperties.moLineCompound = oox::AttributeConversion::decodeToken(sCompoundKind);

    // Align. LineProperties has no member for attribute "algn".

    return;
}

oox::drawingml::LineProperties
lcl_generateLinePropertiesFromTextProps(const comphelper::SequenceAsHashMap& aTextPropMap)
{
    oox::drawingml::LineProperties aLineProperties;
    aLineProperties.maLineFill.moFillType = oox::XML_noFill; // default

    // Get property "textOutline" from aTextPropMap
    uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
    if (!(aTextPropMap.getValue(u"CharInteropGrabBag"_ustr) >>= aCharInteropGrabBagSeq))
        return aLineProperties;
    if (!aCharInteropGrabBagSeq.hasElements())
        return aLineProperties;
    comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
    beans::PropertyValue aProp;
    if (!(aCharInteropGrabBagMap.getValue(u"CharTextOutlineTextEffect"_ustr) >>= aProp))
        return aLineProperties;
    uno::Sequence<beans::PropertyValue> aTextOutlineSeq;
    if (!(aProp.Name == "textOutline" && (aProp.Value >>= aTextOutlineSeq)
          && aTextOutlineSeq.hasElements()))
        return aLineProperties;

    // Copy line properties from aTextOutlineSeq to aLineProperties
    lcl_getLineDetailsFromPropSeq(aTextOutlineSeq, aLineProperties);
    return aLineProperties;
}

oox::drawingml::FillProperties
lcl_generateFillPropertiesFromTextProps(const comphelper::SequenceAsHashMap& rTextPropMap)
{
    oox::drawingml::FillProperties aFillProperties;
    aFillProperties.moFillType = oox::XML_solidFill; // default
    // Theme color supersedes direct color. textFill supersedes theme color. Theme color and textFill
    // are in CharInteropGrabBag.
    uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
    if ((rTextPropMap.getValue(u"CharInteropGrabBag"_ustr) >>= aCharInteropGrabBagSeq)
        && aCharInteropGrabBagSeq.hasElements())
    {
        // Handle case textFill
        comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
        beans::PropertyValue aProp;
        if (aCharInteropGrabBagMap.getValue(u"CharTextFillTextEffect"_ustr) >>= aProp)
        {
            uno::Sequence<beans::PropertyValue> aTextFillSeq;
            if (aProp.Name == "textFill" && (aProp.Value >>= aTextFillSeq)
                && aTextFillSeq.hasElements())
            {
                // Copy fill properties from aTextFillSeq to aFillProperties
                lcl_getFillDetailsFromPropSeq(aTextFillSeq, aFillProperties);
                return aFillProperties;
            }
        }

        // no textFill, look for theme color, tint and shade
        bool bColorFound(false);
        OUString sColorString;
        if (aCharInteropGrabBagMap.getValue(u"CharThemeOriginalColor"_ustr) >>= sColorString)
        {
            sal_Int32 nThemeOrigColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
            aFillProperties.maFillColor.setSrgbClr(nThemeOrigColor);
            bColorFound = true;
        }
        if (aCharInteropGrabBagMap.getValue(u"CharThemeColor"_ustr) >>= sColorString)
        {
            sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
            aFillProperties.maFillColor.setSchemeClr(nColorToken);
            aFillProperties.maFillColor.setSchemeName(sColorString);
            bColorFound = true;
            // A character color has shade or tint, a shape color has lumMod and lumOff.
            OUString sTransformString;
            if (aCharInteropGrabBagMap.getValue(u"CharThemeColorTint"_ustr) >>= sTransformString)
            {
                double fTint = oox::AttributeConversion::decodeIntegerHex(sTransformString);
                fTint = fTint / 255.0 * oox::drawingml::MAX_PERCENT;
                aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumMod),
                                                              static_cast<sal_Int32>(fTint + 0.5));
                double fOff = oox::drawingml::MAX_PERCENT - fTint;
                aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumOff),
                                                              static_cast<sal_Int32>(fOff + 0.5));
            }
            else if (aCharInteropGrabBagMap.getValue(u"CharThemeColorShade"_ustr)
                     >>= sTransformString)
            {
                double fShade = oox::AttributeConversion::decodeIntegerHex(sTransformString);
                fShade = fShade / 255.0 * oox::drawingml::MAX_PERCENT;
                aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumMod),
                                                              static_cast<sal_Int32>(fShade + 0.5));
            }
        }
        if (bColorFound)
            return aFillProperties;
    }

    // Neither textFill nor theme color. Look for direct color.
    sal_Int32 aCharColor = 0;
    if (rTextPropMap.getValue(u"CharColor"_ustr) >>= aCharColor)
        aFillProperties.maFillColor.setSrgbClr(aCharColor);
    else
        aFillProperties.maFillColor.setUnused();
    return aFillProperties;
}

void lcl_applyShapePropsToShape(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
                                const oox::drawingml::ShapePropertyMap& rShapeProps)
{
    for (const auto& rProp : rShapeProps.makePropertyValueSequence())
    {
        xShapePropertySet->setPropertyValue(rProp.Name, rProp.Value);
    }
}

void lcl_setTextAnchorFromTextProps(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
                                    const comphelper::SequenceAsHashMap& aTextPropMap)
{
    // Fontwork does not evaluate paragraph alignment but uses text anchor instead
    auto eHorzAdjust(drawing::TextHorizontalAdjust_CENTER);
    sal_Int16 nParaAlign = sal_Int16(drawing::TextHorizontalAdjust_CENTER);
    aTextPropMap.getValue(u"ParaAdjust"_ustr) >>= nParaAlign;
    switch (nParaAlign)
    {
        case sal_Int16(style::ParagraphAdjust_LEFT):
            eHorzAdjust = drawing::TextHorizontalAdjust_LEFT;
            break;
        case sal_Int16(style::ParagraphAdjust_RIGHT):
            eHorzAdjust = drawing::TextHorizontalAdjust_RIGHT;
            break;
        default:
            eHorzAdjust = drawing::TextHorizontalAdjust_CENTER;
    }
    xShapePropertySet->setPropertyValue(u"TextHorizontalAdjust"_ustr, uno::Any(eHorzAdjust));
    xShapePropertySet->setPropertyValue(u"TextVerticalAdjust"_ustr,
                                        uno::Any(drawing::TextVerticalAdjust_TOP));
}

void lcl_setTextPropsToShape(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
                             std::vector<beans::PropertyValue>& aTextPropVec)
{
    auto xShapePropertySetInfo = xShapePropertySet->getPropertySetInfo();
    if (!xShapePropertySetInfo.is())
        return;
    for (size_t i = 0; i < aTextPropVec.size(); ++i)
    {
        if (xShapePropertySetInfo->hasPropertyByName(aTextPropVec[i].Name)
            && !(xShapePropertySetInfo->getPropertyByName(aTextPropVec[i].Name).Attributes
                 & beans::PropertyAttribute::READONLY)
            && aTextPropVec[i].Name != u"CharInteropGrabBag")
        {
            xShapePropertySet->setPropertyValue(aTextPropVec[i].Name, aTextPropVec[i].Value);
        }
    }
}

void lcl_applyUsedTextPropsToAllTextRuns(const uno::Reference<text::XText>& xText,
                                         const std::vector<beans::PropertyValue>& aTextPropVec)
{
    if (!xText.is())
        return;
    uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
    xTextCursor->gotoStart(false);
    xTextCursor->gotoEnd(true);
    uno::Reference<container::XEnumerationAccess> paraEnumAccess(xText, uno::UNO_QUERY);
    if (!paraEnumAccess.is())
        return;
    uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
    while (paraEnum->hasMoreElements())
    {
        uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
        uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
        if (!runEnumAccess.is())
            continue;
        uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
        while (runEnum->hasMoreElements())
        {
            uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
            if (xRun->getString().isEmpty())
                continue;
            uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
            if (!xRunPropSet.is())
                continue;
            auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
            if (!xRunPropSetInfo.is())
                continue;

            for (size_t i = 0; i < aTextPropVec.size(); ++i)
            {
                if (xRunPropSetInfo->hasPropertyByName(aTextPropVec[i].Name)
                    && !(xRunPropSetInfo->getPropertyByName(aTextPropVec[i].Name).Attributes
                         & beans::PropertyAttribute::READONLY))
                    xRunPropSet->setPropertyValue(aTextPropVec[i].Name, aTextPropVec[i].Value);
            }
        }
    }
}
// anonymous namespace

namespace oox::shape
{
WpsContext::WpsContext(ContextHandler2Helper const& rParent, uno::Reference<drawing::XShape> xShape,
                       const drawingml::ShapePtr& pMasterShapePtr,
                       const drawingml::ShapePtr& pShapePtr)
    : ShapeContext(rParent, pMasterShapePtr, pShapePtr)
    , mxShape(std::move(xShape))
{
    if (mpShapePtr)
        mpShapePtr->setWps(true);

    if (const auto pParent = dynamic_cast<const WpgContext*>(&rParent))
        m_bHasWPGParent = pParent->isFullWPGSupport();
    else if (dynamic_cast<const WordprocessingCanvasContext*>(&rParent))
        m_bHasWPGParent = true;
    else
        m_bHasWPGParent = false;

    if ((pMasterShapePtr && pMasterShapePtr->isInWordprocessingCanvas())
        || dynamic_cast<const WordprocessingCanvasContext*>(&rParent) != nullptr)
        pShapePtr->setWordprocessingCanvas(true);
}

WpsContext::~WpsContext() = default;

oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken,
                                                         const oox::AttributeList& rAttribs)
{
    switch (getBaseToken(nElementToken))
    {
        case XML_wsp:
            break;
        case XML_cNvCnPr:
        {
            // It might be a connector shape in a wordprocessing canvas
            // Replace the custom shape with a connector shape.
            if (!mpShapePtr || !mpShapePtr->isInWordprocessingCanvas() || !mpMasterShapePtr)
                break;
            // Generate new shape
            oox::drawingml::ShapePtr pShape = std::make_shared<oox::drawingml::Shape>(
                u"com.sun.star.drawing.ConnectorShape"_ustr, false);
            pShape->setConnectorShape(true);
            pShape->setWps(true);
            pShape->setWordprocessingCanvas(true);
            // ToDo: Can only copy infos from mpShapePtr to pShape for which getter available.
            pShape->setName(mpShapePtr->getName());
            pShape->setId(mpShapePtr->getId());
            pShape->setWPGChild(mpShapePtr->isWPGChild());
            // And actually replace the shape.
            mpShapePtr = pShape;
            mpMasterShapePtr->getChildren().pop_back();
            mpMasterShapePtr->getChildren().push_back(pShape);
            return new oox::drawingml::ConnectorShapePropertiesContext(
                *this, mpShapePtr, mpShapePtr->getConnectorShapeProperties());
        }
        case XML_bodyPr:
            if (mxShape.is())
            {
                // no evaluation of attribute XML_rot, because Word ignores it, as of 2022-07.

                uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
                uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
                sal_Int32 nVert = rAttribs.getToken(XML_vert, XML_horz);
                if (nVert == XML_eaVert)
                {
                    xPropertySet->setPropertyValue(u"TextWritingMode"_ustr,
                                                   uno::Any(text::WritingMode_TB_RL));
                    xPropertySet->setPropertyValue(u"WritingMode"_ustr,
                                                   uno::Any(text::WritingMode2::TB_RL));
                }
                else if (nVert == XML_mongolianVert)
                {
                    xPropertySet->setPropertyValue(u"WritingMode"_ustr,
                                                   uno::Any(text::WritingMode2::TB_LR));
                }
                else if (nVert == XML_wordArtVert || nVert == XML_wordArtVertRtl)
                {
                    // Multiline wordArtVert is not implemented yet.
                    // It will render all the text in 1 line.
                    // Map 'wordArtVertRtl' to 'wordArtVert', as they are the same now.
                    xPropertySet->setPropertyValue(u"WritingMode"_ustr,
                                                   uno::Any(text::WritingMode2::STACKED));
                }
                else if (nVert != XML_horz) // cases XML_vert and XML_vert270
                {
                    // Hack to get same rendering as after the fix for tdf#87924. If shape rotation
                    // plus text direction results in upright text, use horizontal text direction.
                    // Remove hack when frame is able to rotate.

                    // Need transformation matrix since RotateAngle does not contain flip.
                    drawing::HomogenMatrix3 aMatrix;
                    xPropertySet->getPropertyValue(u"Transformation"_ustr) >>= aMatrix;
                    basegfx::B2DHomMatrix aTransformation;
                    aTransformation.set(0, 0, aMatrix.Line1.Column1);
                    aTransformation.set(0, 1, aMatrix.Line1.Column2);
                    aTransformation.set(0, 2, aMatrix.Line1.Column3);
                    aTransformation.set(1, 0, aMatrix.Line2.Column1);
                    aTransformation.set(1, 1, aMatrix.Line2.Column2);
                    aTransformation.set(1, 2, aMatrix.Line2.Column3);
                    // For this to be a valid 2D transform matrix, the last row must be [0,0,1]
                    assert(aMatrix.Line3.Column1 == 0);
                    assert(aMatrix.Line3.Column2 == 0);
                    assert(aMatrix.Line3.Column3 == 1);
                    basegfx::B2DTuple aScale;
                    basegfx::B2DTuple aTranslate;
                    double fRotate = 0;
                    double fShearX = 0;
                    aTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
                    auto nRotate(static_cast<sal_uInt16>(NormAngle360(basegfx::rad2deg(fRotate))));
                    if ((nVert == XML_vert && nRotate == 270)
                        || (nVert == XML_vert270 && nRotate == 90))
                    {
                        xPropertySet->setPropertyValue(u"WritingMode"_ustr,
                                                       uno::Any(text::WritingMode2::LR_TB));
                        // ToDo: Remember original vert value and remove hack on export.
                    }
                    else if (nVert == XML_vert)
                        xPropertySet->setPropertyValue(u"WritingMode"_ustr,
                                                       uno::Any(text::WritingMode2::TB_RL90));
                    else // nVert == XML_vert270
                        xPropertySet->setPropertyValue(u"WritingMode"_ustr,
                                                       uno::Any(text::WritingMode2::BT_LR));
                }

                if (bool bUpright = rAttribs.getBool(XML_upright, false))
                {
                    uno::Sequence<beans::PropertyValue> aGrabBag;
                    xPropertySet->getPropertyValue(u"InteropGrabBag"_ustr) >>= aGrabBag;
                    sal_Int32 length = aGrabBag.getLength();
                    aGrabBag.realloc(length + 1);
                    auto pGrabBag = aGrabBag.getArray();
                    pGrabBag[length].Name = "Upright";
                    pGrabBag[length].Value <<= bUpright;
                    xPropertySet->setPropertyValue(u"InteropGrabBag"_ustr, uno::Any(aGrabBag));
                }

                auto xPropertySetInfo = xPropertySet->getPropertySetInfo();

                if (xServiceInfo.is())
                {
                    // Handle inset attributes for Writer textframes.
                    sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
                    std::optional<sal_Int32> oInsets[4];
                    for (std::size_t i = 0; i < SAL_N_ELEMENTS(aInsets); ++i)
                    {
                        std::optional<OUString> oValue = rAttribs.getString(aInsets[i]);
                        if (oValue.has_value())
                            oInsets[i] = oox::drawingml::GetCoordinate(oValue.value());
                        else
                            // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
                            oInsets[i]
                                = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
                    }
                    const OUString aShapeProps[]
                        = { u"TextLeftDistance"_ustr, u"TextUpperDistance"_ustr,
                            u"TextRightDistance"_ustr, u"TextLowerDistance"_ustr };
                    for (std::size_t i = 0; i < SAL_N_ELEMENTS(aShapeProps); ++i)
                    {
                        if (!oInsets[i])
                            continue;
                        if (xPropertySetInfo && xPropertySetInfo->hasPropertyByName(aShapeProps[i]))
                            xPropertySet->setPropertyValue(aShapeProps[i], uno::Any(*oInsets[i]));
                        else
                            SAL_WARN("oox""Property: " << aShapeProps[i] << " not supported");
                    }
                }

                // Handle text vertical adjustment inside a text frame
                if (rAttribs.hasAttribute(XML_anchor))
                {
                    if (xPropertySetInfo
                        && xPropertySetInfo->hasPropertyByName(u"TextVerticalAdjust"_ustr))
                    {
                        drawing::TextVerticalAdjust eAdjust = drawingml::GetTextVerticalAdjust(
                            rAttribs.getToken(XML_anchor, XML_t));
                        xPropertySet->setPropertyValue(u"TextVerticalAdjust"_ustr,
                                                       uno::Any(eAdjust));
                    }
                    else
                        SAL_WARN("oox""Property: TextVerticalAdjust not supported");
                }

                // Apply character color of the shape to the shape's textbox.
                uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
                if (xText)
                {
                    uno::Any xCharColor = xPropertySet->getPropertyValue(u"CharColor"_ustr);
                    Color aColor = COL_AUTO;
                    if ((xCharColor >>= aColor) && aColor != COL_AUTO)
                    {
                        // tdf#135923 Apply character color of the shape to the textrun
                        //            when the character color of the textrun is default.
                        // tdf#153791 But only if the run has no background color (shd element in OOXML)
                        if (uno::Reference<container::XEnumerationAccess> paraEnumAccess{
                                xText, uno::UNO_QUERY })
                        {
                            uno::Reference<container::XEnumeration> paraEnum(
                                paraEnumAccess->createEnumeration());

                            while (paraEnum->hasMoreElements())
                            {
                                uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(),
                                                                            uno::UNO_QUERY);
                                uno::Reference<container::XEnumerationAccess> runEnumAccess(
                                    xParagraph, uno::UNO_QUERY);
                                if (!runEnumAccess.is())
                                    continue;
                                if (uno::Reference<beans::XPropertySet> xParaPropSet{
                                        xParagraph, uno::UNO_QUERY })
                                    if ((xParaPropSet->getPropertyValue(u"ParaBackColor"_ustr)
                                         >>= aColor)
                                        && aColor != COL_AUTO)
                                        continue;

                                uno::Reference<container::XEnumeration> runEnum
                                    = runEnumAccess->createEnumeration();

                                while (runEnum->hasMoreElements())
                                {
                                    uno::Reference<text::XTextRange> xRun(runEnum->nextElement(),
                                                                          uno::UNO_QUERY);
                                    const uno::Reference<beans::XPropertyState> xRunState(
                                        xRun, uno::UNO_QUERY);
                                    if (!xRunState
                                        || xRunState->getPropertyState(u"CharColor"_ustr)
                                               == beans::PropertyState_DEFAULT_VALUE)
                                    {
                                        uno::Reference<beans::XPropertySet> xRunPropSet(
                                            xRun, uno::UNO_QUERY);
                                        if (!xRunPropSet)
                                            continue;
                                        if ((xRunPropSet->getPropertyValue(u"CharBackColor"_ustr)
                                             >>= aColor)
                                            && aColor != COL_AUTO)
                                            continue;
                                        if (!(xRunPropSet->getPropertyValue(u"CharColor"_ustr)
                                              >>= aColor)
                                            || aColor == COL_AUTO)
                                            xRunPropSet->setPropertyValue(u"CharColor"_ustr,
                                                                          xCharColor);
                                    }
                                }
                            }
                        }
                    }
                }

                if (xPropertySetInfo && xPropertySetInfo->hasPropertyByName(u"TextWordWrap"_ustr))
                {
                    auto nWrappingType = rAttribs.getToken(XML_wrap, XML_square);
                    xPropertySet->setPropertyValue(u"TextWordWrap"_ustr,
                                                   uno::Any(nWrappingType == XML_square));
                }
                else
                    SAL_WARN("oox""Property: TextWordWrap not supported");

                return this;
            }
            else if (m_bHasWPGParent && mpShapePtr)
            {
                // this WPS context has to be inside a WPG shape, so the <BodyPr> element
                // cannot be applied to mxShape member, use mpShape instead, and after the
                // the parent shape finished, apply it for its children.
                mpShapePtr->setWPGChild(true);
                oox::drawingml::TextBodyPtr pTextBody;
                pTextBody.reset(new oox::drawingml::TextBody());

                if (rAttribs.hasAttribute(XML_anchor))
                {
                    drawing::TextVerticalAdjust eAdjust
                        = drawingml::GetTextVerticalAdjust(rAttribs.getToken(XML_anchor, XML_t));
                    pTextBody->getTextProperties().meVA = eAdjust;
                }

                sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
                for (int i = 0; i < 4; ++i)
                {
                    if (rAttribs.hasAttribute(XML_lIns))
                    {
                        std::optional<OUString> oValue = rAttribs.getString(aInsets[i]);
                        if (oValue.has_value())
                            pTextBody->getTextProperties().moInsets[i]
                                = oox::drawingml::GetCoordinate(oValue.value());
                        else
                            // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
                            pTextBody->getTextProperties().moInsets[i]
                                = (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
                    }
                }

                mpShapePtr->setTextBody(pTextBody);
            }
            break;
        case XML_noAutofit:
        case XML_spAutoFit:
        {
            uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
            // We can't use oox::drawingml::TextBodyPropertiesContext here, as this
            // is a child context of bodyPr, so the shape is already sent: we need
            // to alter the XShape directly.
            uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
            if (xPropertySet.is())
            {
                if (xServiceInfo->supportsService(u"com.sun.star.text.TextFrame"_ustr))
                    xPropertySet->setPropertyValue(
                        u"FrameIsAutomaticHeight"_ustr,
                        uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
                else
                {
                    auto xPropertySetInfo = xPropertySet->getPropertySetInfo();
                    if (xPropertySetInfo
                        && xPropertySetInfo->hasPropertyByName(u"TextAutoGrowHeight"_ustr))
                    {
                        xPropertySet->setPropertyValue(
                            u"TextAutoGrowHeight"_ustr,
                            uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
                    }
                    else
                        SAL_WARN("oox""Property: TextAutoGrowHeight not supported");
                }
            }
        }
        break;
        case XML_prstTxWarp:
            if (rAttribs.hasAttribute(XML_prst))
            {
                uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
                if (xPropertySet.is())
                {
                    std::optional<OUString> presetShapeName = rAttribs.getString(XML_prst);
                    const OUString& preset = presetShapeName.value();
                    comphelper::SequenceAsHashMap aCustomShapeGeometry(
                        xPropertySet->getPropertyValue(u"CustomShapeGeometry"_ustr));
                    aCustomShapeGeometry[u"PresetTextWarp"_ustr] <<= preset;
                    xPropertySet->setPropertyValue(
                        u"CustomShapeGeometry"_ustr,
                        uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList()));
                }
            }
            return new oox::drawingml::PresetTextShapeContext(
                *this, rAttribs, *(getShape()->getCustomShapeProperties()));
        case XML_txbx:
        {
            mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
            mpShapePtr->setTextBox(true);
            //in case if the textbox is linked, save the attributes
            //for further processing.
            if (rAttribs.hasAttribute(XML_id))
            {
                std::optional<OUString> id = rAttribs.getString(XML_id);
                if (id.has_value())
                {
                    oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
                    linkedTxtBoxAttr.id = id.value().toInt32();
                    mpShapePtr->setTxbxHasLinkedTxtBox(true);
                    mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
                }
            }
            return this;
        }
        break;
        case XML_linkedTxbx:
        {
            //in case if the textbox is linked, save the attributes
            //for further processing.
            mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
            mpShapePtr->setTextBox(true);
            std::optional<OUString> id = rAttribs.getString(XML_id);
            std::optional<OUString> seq = rAttribs.getString(XML_seq);
            if (id.has_value() && seq.has_value())
            {
                oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
                linkedTxtBoxAttr.id = id.value().toInt32();
                linkedTxtBoxAttr.seq = seq.value().toInt32();
                mpShapePtr->setTxbxHasLinkedTxtBox(true);
                mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
            }
        }
        break;
        default:
            return ShapeContext::onCreateContext(nElementToken, rAttribs);
    }
    return nullptr;
}

void WpsContext::onEndElement()
{
    // Convert shape to Fontwork shape if necessary and meaningful.
    // Only at end of bodyPr all needed info is available.

    if (getBaseToken(getCurrentElement()) != XML_bodyPr)
        return;

    // Make sure all needed parts are available
    auto* pCustomShape
        = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(mxShape));
    if (!pCustomShape || !mpShapePtr || !mxShape.is())
        return;
    uno::Reference<beans::XPropertySet> xShapePropertySet(mxShape, uno::UNO_QUERY);
    if (!xShapePropertySet.is())
        return;
    // This is the text in the frame, associated with the shape
    uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
    if (!xText.is())
        return;

    OUString sMSPresetType;
    comphelper::SequenceAsHashMap aCustomShapeGeometry(
        xShapePropertySet->getPropertyValue(u"CustomShapeGeometry"_ustr));
    aCustomShapeGeometry[u"PresetTextWarp"_ustr] >>= sMSPresetType;
    if (sMSPresetType.isEmpty() || sMSPresetType == u"textNoShape")
        return;

    // Word can combine its "abc Transform" with a lot of shape types. LibreOffice can only render
    // the old kind WordArt, which is based on a rectangle. In case of non rectangular shape we keep
    // the shape and do not convert the text to Fontwork.
    OUString sType;
    aCustomShapeGeometry[u"Type"_ustr] >>= sType;
    if (sType != u"ooxml-rect")
        return;

    // Copy properties from frame text to have them available after the frame is removed.
    std::vector<beans::PropertyValue> aTextPropVec;
    if (!lcl_getTextPropsFromFrameText(xText, aTextPropVec))
        return;
    comphelper::SequenceAsHashMap aTextPropMap(comphelper::containerToSequence(aTextPropVec));

    // Copy text content from frame to shape. Since Fontwork uses simple text anyway, we can use
    // a string.
    OUString sFrameContent(xText->getString());
    pCustomShape->NbcSetText(sFrameContent);

    // Setting the property "TextBox" to false includes removing the attached frame from the shape.
    xShapePropertySet->setPropertyValue(u"TextBox"_ustr, uno::Any(false));

    // Set the shape into text path mode, so that the text is drawn as Fontwork. Word renders a legacy
    // "text on path" without the legacy stretching, therefore use false for bFromWordArt.
    mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
    FontworkHelpers::putCustomShapeIntoTextPathMode(mxShape, getShape()->getCustomShapeProperties(),
                                                    sMSPresetType, /*bFromWordArt*/ false);

    // Apply the text props to the fontwork shape
    lcl_setTextPropsToShape(xShapePropertySet, aTextPropVec); // includes e.g. FontName
    lcl_setTextAnchorFromTextProps(xShapePropertySet, aTextPropMap);

    // Fontwork in LO uses fill and stroke of the shape and cannot style text portions individually.
    // "abc Transform" in Word uses fill and outline of the characters.
    // We need to copy the properties from a run to the shape.
    oox::drawingml::ShapePropertyMap aStrokeShapeProps(getFilter().getModelObjectHelper());
    oox::drawingml::LineProperties aCreatedLineProperties
        = lcl_generateLinePropertiesFromTextProps(aTextPropMap);
    aCreatedLineProperties.pushToPropMap(aStrokeShapeProps, getFilter().getGraphicHelper());
    lcl_applyShapePropsToShape(xShapePropertySet, aStrokeShapeProps);

    oox::drawingml::ShapePropertyMap aFillShapeProps(getFilter().getModelObjectHelper());
    oox::drawingml::FillProperties aCreatedFillProperties
        = lcl_generateFillPropertiesFromTextProps(aTextPropMap);
    aCreatedFillProperties.pushToPropMap(aFillShapeProps, getFilter().getGraphicHelper(),
                                         /*nShapeRotation*/ 0,
                                         /*nPhClr*/ API_RGB_TRANSPARENT,
                                         /*aShapeSize*/ css::awt::Size(0, 0), /*nPhClrTheme*/ -1,
                                         pCustomShape->IsMirroredX(), pCustomShape->IsMirroredY(),
                                         /*bIsCustomShape*/ true);
    lcl_applyShapePropsToShape(xShapePropertySet, aFillShapeProps);

    // Copying the text content from frame to shape as string has lost the styles. Apply the used text
    // properties back to all runs in the text.
    uno::Reference<text::XText> xNewText(pCustomShape->getUnoShape(), uno::UNO_QUERY);
    if (xNewText.is())
        lcl_applyUsedTextPropsToAllTextRuns(xNewText, aTextPropVec);

    // Fontwork stretches the text to the given path. So adapt shape size to text is nonsensical.
    xShapePropertySet->setPropertyValue(u"TextAutoGrowHeight"_ustr, uno::Any(false));
    xShapePropertySet->setPropertyValue(u"TextAutoGrowWidth"_ustr, uno::Any(false));
}
}

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

Messung V0.5
C=89 H=92 G=90

¤ Dauer der Verarbeitung: 0.11 Sekunden  ¤

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