tdf#160386: Add support for switch element

For now, only use language tag, meaning if
there is a file like in the unittest with

<text systemLanguage="en-us">Howdy!</text>
<text systemLanguage="en-gb">Wotcha!</text>
<text systemLanguage="en-au">G'day!</text>
<text systemLanguage="en">Hello!</text>

"Hello!" with be displayed in a en_AU system locale

This patch partially reverts 13a41e7a12
"tdf#150124: do nothing when parent is of unkown type"
making 0dfd8288a8
"ofz#60384 Direct-leak" no longer necessary

Change-Id: Ifc73bc69aa997088dc0a2b11d7d30446303fa3b3

Change-Id: I885ef0f2c44b86196881fe55a963db2e5c7eb1be
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165394
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Xisco Fauli 2024-03-27 11:38:44 +01:00
parent 6b15374d18
commit 0a2535ceb0
13 changed files with 270 additions and 13 deletions

View file

@ -34,6 +34,7 @@ $(eval $(call gb_CppunitTest_use_libraries,svgio,\
cppu \
cppuhelper \
comphelper \
i18nlangtag \
sal \
salhelper \
sax \

View file

@ -33,6 +33,7 @@ $(eval $(call gb_CppunitTest_use_libraries,svgio_tools,\
comphelper \
cppu \
cppuhelper \
i18nlangtag \
sal \
salhelper \
sax \

View file

@ -41,6 +41,7 @@ $(eval $(call gb_Library_use_libraries,svgio,\
comphelper \
cppu \
cppuhelper \
i18nlangtag \
sal \
salhelper \
tk \
@ -87,6 +88,7 @@ $(eval $(call gb_Library_add_exception_objects,svgio,\
svgio/source/svgreader/svgstyleattributes \
svgio/source/svgreader/svgstylenode \
svgio/source/svgreader/svgsvgnode \
svgio/source/svgreader/svgswitchnode \
svgio/source/svgreader/svgsymbolnode \
svgio/source/svgreader/svgtextnode \
svgio/source/svgreader/svgtextposition \

View file

@ -34,9 +34,6 @@ namespace svgio::svgreader
/// the document hierarchy with all root nodes
SvgNodeVector maNodes;
/// invalid nodes that have no parent
SvgNodeVector maOrphanNodes;
/// the absolute path of the Svg file in progress (if available)
const OUString maAbsolutePath;
@ -75,9 +72,6 @@ namespace svgio::svgreader
/// data read access
const SvgNodeVector& getSvgNodeVector() const { return maNodes; }
const OUString& getAbsolutePath() const { return maAbsolutePath; }
/// invalid nodes that have no parent
void addOrphanNode(SvgNode* pOrphan) { maOrphanNodes.emplace_back(pOrphan); }
};
} // end of namespace svgio::svgreader

View file

@ -95,6 +95,9 @@ namespace svgio::svgreader
/// Class svan value
std::optional<OUString> mpClass;
/// systemLanguage values
std::vector<OUString> maSystemLanguage;
/// XmlSpace value
XmlSpace maXmlSpace;
@ -174,6 +177,10 @@ namespace svgio::svgreader
std::optional<OUString> const & getClass() const { return mpClass; }
void setClass(OUString const &);
/// SystemLanguage access
std::vector<OUString> const & getSystemLanguage() const { return maSystemLanguage; }
void setSystemLanguage(OUString const &);
/// XmlSpace access
XmlSpace getXmlSpace() const;
void setXmlSpace(XmlSpace eXmlSpace) { maXmlSpace = eXmlSpace; }

View file

@ -0,0 +1,55 @@
/* -*- 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 .
*/
#pragma once
#include "svgstyleattributes.hxx"
#include <basegfx/matrix/b2dhommatrix.hxx>
namespace svgio::svgreader
{
class SvgSwitchNode final : public SvgNode
{
private:
/// use styles
SvgStyleAttributes maSvgStyleAttributes;
/// variable scan values, dependent of given XAttributeList
std::optional<basegfx::B2DHomMatrix> mpaTransform;
public:
SvgSwitchNode(SvgDocument& rDocument, SvgNode* pParent);
virtual ~SvgSwitchNode() override;
virtual const SvgStyleAttributes* getSvgStyleAttributes() const override;
virtual void parseAttribute(SVGToken aSVGToken, const OUString& aContent) override;
virtual void decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer& rTarget,
bool bReferenced) const override;
/// transform content
const std::optional<basegfx::B2DHomMatrix>& getTransform() const { return mpaTransform; }
void setTransform(const std::optional<basegfx::B2DHomMatrix>& pMatrix)
{
mpaTransform = pMatrix;
}
};
} // end of namespace svgio::svgreader
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -116,6 +116,7 @@ namespace svgio::svgreader
PatternContentUnits,
PatternTransform,
Opacity,
SystemLanguage,
Visibility,
Title,
Desc,

View file

@ -422,6 +422,17 @@ CPPUNIT_TEST_FIXTURE(Test, testFontsizeRelative)
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]"_ostr, "familyname"_ostr, "DejaVu Serif");
}
CPPUNIT_TEST_FIXTURE(Test, testTdf160386)
{
xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf160386.svg");
// Without the fix in place, this test would have failed with
// - Expected: 1
// - Actual : 11
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion"_ostr, 1);
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion"_ostr, "text"_ostr, "Hello!");
}
CPPUNIT_TEST_FIXTURE(Test, testTdf145896)
{
xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf145896.svg");

View file

@ -0,0 +1,16 @@
<svg viewBox="0 -20 100 50" xmlns="http://www.w3.org/2000/svg">
<switch font-family="DejaVu Sans">
<text systemLanguage="ar">مرحبا</text>
<text systemLanguage="de,nl">Hallo!</text>
<text systemLanguage="en-us">Howdy!</text>
<text systemLanguage="en-gb">Wotcha!</text>
<text systemLanguage="en-au">G'day!</text>
<text systemLanguage="en">Hello!</text>
<text systemLanguage="es">Hola!</text>
<text systemLanguage="fr">Bonjour!</text>
<text systemLanguage="ja">こんにちは</text>
<text systemLanguage="ru">Привет!</text>
<text></text>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 616 B

View file

@ -27,6 +27,7 @@
#include <svgrectnode.hxx>
#include <svggradientnode.hxx>
#include <svggradientstopnode.hxx>
#include <svgswitchnode.hxx>
#include <svgsymbolnode.hxx>
#include <svgusenode.hxx>
#include <svgcirclenode.hxx>
@ -203,7 +204,13 @@ namespace
mpTarget->parseAttributes(xAttribs);
break;
}
case SVGToken::Switch: //TODO: Support switch element
case SVGToken::Switch:
{
/// new node for Switch
mpTarget = new SvgSwitchNode(maDocument, mpTarget);
mpTarget->parseAttributes(xAttribs);
break;
}
case SVGToken::Defs:
case SVGToken::G:
{

View file

@ -367,17 +367,13 @@ namespace {
mpParent(pParent),
mpAlternativeParent(nullptr),
maXmlSpace(XmlSpace::NotSet),
maDisplay(Display::Inline),
maDisplay(maType == SVGToken::Unknown ? Display::None : Display::Inline), // tdf#150124: do not display unknown nodes
mbDecomposing(false),
mbCssStyleVectorBuilt(false)
{
if (pParent)
{
// tdf#150124 ignore when parent is unknown
if (pParent->getType() != SVGToken::Unknown)
pParent->maChildren.emplace_back(this);
else
mrDocument.addOrphanNode(this);
pParent->maChildren.emplace_back(this);
}
}
@ -527,6 +523,14 @@ namespace {
}
break;
}
case SVGToken::SystemLanguage:
{
if(!aContent.isEmpty())
{
setSystemLanguage(aContent);
}
break;
}
case SVGToken::XmlSpace:
{
if(!aContent.isEmpty())
@ -751,6 +755,34 @@ namespace {
mrDocument.addSvgNodeToMapper(*mpClass, *this);
}
void SvgNode::setSystemLanguage(OUString const & rSystemClass)
{
const sal_Int32 nLen(rSystemClass.getLength());
sal_Int32 nPos(0);
OUStringBuffer aToken;
// split into single tokens (currently only comma separator)
while(nPos < nLen)
{
const sal_Int32 nInitPos(nPos);
copyToLimiter(rSystemClass, u',', nPos, aToken, nLen);
skip_char(rSystemClass, u',', nPos, nLen);
const OUString aLang(o3tl::trim(aToken));
aToken.setLength(0);
if(!aLang.isEmpty())
{
maSystemLanguage.push_back(aLang);
}
if(nInitPos == nPos)
{
OSL_ENSURE(false, "Could not interpret on current position (!)");
nPos++;
}
}
}
XmlSpace SvgNode::getXmlSpace() const
{
if(maXmlSpace != XmlSpace::NotSet)

View file

@ -0,0 +1,129 @@
/* -*- 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 <svgswitchnode.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <unotools/syslocaleoptions.hxx>
namespace svgio::svgreader
{
SvgSwitchNode::SvgSwitchNode(SvgDocument& rDocument, SvgNode* pParent)
: SvgNode(SVGToken::Switch, rDocument, pParent)
, maSvgStyleAttributes(*this)
{
}
SvgSwitchNode::~SvgSwitchNode() {}
const SvgStyleAttributes* SvgSwitchNode::getSvgStyleAttributes() const
{
return checkForCssStyle(maSvgStyleAttributes);
}
void SvgSwitchNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent)
{
// call parent
SvgNode::parseAttribute(aSVGToken, aContent);
// read style attributes
maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
// parse own
switch (aSVGToken)
{
case SVGToken::Style:
{
readLocalCssStyle(aContent);
break;
}
case SVGToken::Transform:
{
const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
if (!aMatrix.isIdentity())
{
setTransform(aMatrix);
}
break;
}
default:
{
break;
}
}
}
void SvgSwitchNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer& rTarget,
bool bReferenced) const
{
// #i125258# for SVGTokenG decompose children
const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
if (pStyle)
{
drawinglayer::primitive2d::Primitive2DContainer aContent;
const auto& rChildren = getChildren();
const sal_uInt32 nCount(rChildren.size());
OUString sLanguage(SvtSysLocaleOptions().GetRealUILanguageTag().getLanguage());
SvgNode* pNodeToDecompose = nullptr;
for (sal_uInt32 a(0); a < nCount; a++)
{
SvgNode* pCandidate = rChildren[a].get();
if (pCandidate && Display::None != pCandidate->getDisplay())
{
std::vector<OUString> aSystemLanguage = pCandidate->getSystemLanguage();
if (!sLanguage.isEmpty() && !aSystemLanguage.empty())
{
for (const OUString& sSystemLang : aSystemLanguage)
{
if (sSystemLang == sLanguage)
{
pNodeToDecompose = pCandidate;
break;
}
}
}
else
{
pNodeToDecompose = pCandidate;
}
}
if (pNodeToDecompose)
{
pNodeToDecompose->decomposeSvgNode(aContent, bReferenced);
// break once it's descomposed
break;
}
}
if (!aContent.empty())
{
pStyle->add_postProcess(rTarget, std::move(aContent), getTransform());
}
}
}
} // end of namespace svgio::svgreader
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -114,6 +114,7 @@ constexpr auto aSVGTokenMap = frozen::make_unordered_map<std::u16string_view, SV
{ u"patternContentUnits", SVGToken::PatternContentUnits },
{ u"patternTransform", SVGToken::PatternTransform },
{ u"opacity", SVGToken::Opacity },
{ u"systemLanguage", SVGToken::SystemLanguage },
{ u"visibility", SVGToken::Visibility },
{ u"title", SVGToken::Title },
{ u"desc", SVGToken::Desc },