Kaydet (Commit) 7894fd2b authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Tomaž Vajngerl

svgio visitor, add draw commands and create the from svg

Adds a visitor for svgio for visiting svg nodes and create something
useful from them.

Basic draw commands - a tree of draw commands (with sub-pixel
precision support) just to store a simple definition for drawing.

Adds a svg draw visitor and create draw commands from the svg
structure and expose the commands through UNO API.

Change-Id: I073e891a2cffdd76d4e3b838590e3a19c998e9bf
Reviewed-on: https://gerrit.libreoffice.org/68770
Tested-by: Jenkins
Reviewed-by: 's avatarTomaž Vajngerl <quikee@gmail.com>
üst de5dc664
/* -*- 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/.
*/
class DrawBase;
class DrawCommand
{
public:
std::vector<std::shared_ptr<DrawBase>> maChildren;
};
enum class DrawCommandType
{
Root,
Rectangle,
Path
};
class DrawBase : public DrawCommand
{
private:
DrawCommandType meType;
public:
DrawBase(DrawCommandType eType)
: meType(eType)
{
}
DrawCommandType getType() { return meType; }
};
class DrawRoot : public DrawBase
{
public:
basegfx::B2DRange maRectangle;
DrawRoot()
: DrawBase(DrawCommandType::Root)
{
}
};
class DrawRectangle : public DrawBase
{
public:
basegfx::B2DRange maRectangle;
DrawRectangle(basegfx::B2DRange const& rRectangle)
: DrawBase(DrawCommandType::Rectangle)
, maRectangle(rRectangle)
{
}
};
class DrawPath : public DrawBase
{
public:
basegfx::B2DPolyPolygon maPolyPolygon;
DrawPath(basegfx::B2DPolyPolygon const& rPolyPolygon)
: DrawBase(DrawCommandType::Path)
, maPolyPolygon(rPolyPolygon)
{
}
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -45,6 +45,20 @@ interface XSvgParser : ::com::sun::star::uno::XInterface
sequence< XPrimitive2D > getDecomposition(
[in] io::XInputStream xSvgStream,
[in] string aAbsolutePath);
/** Get the "draw command" graph that is created from the SVG content
@param xSvgStream
The file containing the SVG XML data
@param aAbsolutePath
The path containing the SVG XML data
@since LibreOffice 6.3
*/
any getDrawCommands(
[in] io::XInputStream xSvgStream,
[in] string aAbsolutePath);
};
}; }; }; };
......
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
#
# 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/.
#
$(eval $(call gb_CppunitTest_CppunitTest,svgio_read))
$(eval $(call gb_CppunitTest_add_exception_objects,svgio_read,\
svgio/qa/cppunit/SvgRead \
))
$(eval $(call gb_CppunitTest_set_include,svgio_read,\
$$(INCLUDE) \
-I$(SRCDIR)/svgio/inc \
))
$(eval $(call gb_CppunitTest_use_externals,svgio_read,\
boost_headers \
libxml2 \
))
$(eval $(call gb_CppunitTest_use_library_objects,svgio_read,\
svgio \
))
$(eval $(call gb_CppunitTest_use_libraries,svgio_read, \
basegfx \
comphelper \
cppu \
cppuhelper \
drawinglayer \
editeng \
i18nlangtag \
sal \
salhelper \
sax \
sot \
svl \
svt \
svx \
svxcore \
test \
tl \
tk \
ucbhelper \
unotest \
utl \
vcl \
xo \
))
$(eval $(call gb_CppunitTest_use_sdk_api,svgio_read))
$(eval $(call gb_CppunitTest_use_ure,svgio_read))
$(eval $(call gb_CppunitTest_use_vcl,svgio_read))
$(eval $(call gb_CppunitTest_use_rdb,svgio_read,services))
$(eval $(call gb_CppunitTest_use_custom_headers,svgio_read,\
officecfg/registry \
))
$(eval $(call gb_CppunitTest_use_configuration,svgio_read))
# vim: set noet sw=4 ts=4:
......@@ -77,6 +77,7 @@ $(eval $(call gb_Library_add_exception_objects,svgio,\
svgio/source/svgreader/svgtextpathnode \
svgio/source/svgreader/svgtspannode \
svgio/source/svgreader/svgusenode \
svgio/source/svgreader/svgvisitor \
svgio/source/svguno/svguno \
svgio/source/svguno/xsvgparser \
))
......
......@@ -24,6 +24,7 @@ $(eval $(call gb_Module_add_targets,svgio,\
$(eval $(call gb_Module_add_check_targets,svgio,\
CppunitTest_svgio \
CppunitTest_svgio_read \
))
# vim: set noet ts=4 sw=4:
......@@ -80,6 +80,8 @@ namespace svgio
// which members should be initialized
Display getDisplayFromContent(const OUString& aContent);
class Visitor;
class SvgNode : public InfoProvider
{
private:
......@@ -137,6 +139,8 @@ namespace svgio
SvgNode(const SvgNode&) = delete;
SvgNode& operator=(const SvgNode&) = delete;
void accept(Visitor& rVisitor);
/// scan helper to read and interpret a local CssStyle to mpLocalCssStyle
void readLocalCssStyle(const OUString& aContent);
......@@ -182,6 +186,14 @@ namespace svgio
/// alternative parent
void setAlternativeParent(const SvgNode* pAlternativeParent = nullptr) { mpAlternativeParent = pAlternativeParent; }
};
class Visitor
{
public:
virtual ~Visitor() = default;
virtual void visit(SvgNode const & pNode) = 0;
};
} // end of namespace svgreader
} // end of namespace svgio
......
/* -*- 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/.
*
*/
#ifndef INCLUDED_SVGIO_INC_SVGVISITOR_HXX
#define INCLUDED_SVGIO_INC_SVGVISITOR_HXX
#include <basegfx/DrawCommands.hxx>
#include <memory>
#include "svgnode.hxx"
namespace svgio
{
namespace svgreader
{
class SvgDrawVisitor : public Visitor
{
private:
std::shared_ptr<DrawRoot> mpDrawRoot;
std::shared_ptr<DrawBase> mpCurrent;
public:
SvgDrawVisitor();
void visit(svgio::svgreader::SvgNode const& rNode) override;
void goToChildren(svgio::svgreader::SvgNode const& rNode);
std::shared_ptr<DrawRoot> const& getDrawRoot() { return mpDrawRoot; }
};
}
}
#endif // INCLUDED_SVGIO_INC_SVGVISITOR_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- 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 <sal/config.h>
#include <test/bootstrapfixture.hxx>
#include <memory>
#include <comphelper/seqstream.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/processfactory.hxx>
#include <tools/stream.hxx>
#include <com/sun/star/graphic/SvgTools.hpp>
#include <com/sun/star/graphic/Primitive2DTools.hpp>
#include <com/sun/star/graphic/XPrimitive2D.hpp>
#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <com/sun/star/graphic/XSvgParser.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/xml/sax/XParser.hpp>
#include <com/sun/star/xml/sax/Parser.hpp>
#include <com/sun/star/xml/sax/InputSource.hpp>
#include <cppuhelper/implbase2.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/sequence.hxx>
#include <svgdocumenthandler.hxx>
#include <svgrectnode.hxx>
#include <svgsvgnode.hxx>
#include <svggnode.hxx>
#include <basegfx/DrawCommands.hxx>
namespace
{
using namespace css;
class Test : public test::BootstrapFixture
{
void test();
uno::Reference<io::XInputStream> parseSvg(const OUString& aSource);
public:
CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(test);
CPPUNIT_TEST_SUITE_END();
};
uno::Reference<io::XInputStream> Test::parseSvg(const OUString& aSource)
{
SvFileStream aFileStream(aSource, StreamMode::READ);
std::size_t nSize = aFileStream.remainingSize();
std::unique_ptr<sal_Int8[]> pBuffer(new sal_Int8[nSize + 1]);
aFileStream.ReadBytes(pBuffer.get(), nSize);
pBuffer[nSize] = 0;
uno::Sequence<sal_Int8> aData(pBuffer.get(), nSize + 1);
uno::Reference<io::XInputStream> aInputStream(new comphelper::SequenceInputStream(aData));
return aInputStream;
}
void Test::test()
{
OUString aSvgFile = "/svgio/qa/cppunit/data/Rect.svg";
OUString aUrl = m_directories.getURLFromSrc(aSvgFile);
OUString aPath = m_directories.getPathFromSrc(aSvgFile);
uno::Reference<io::XInputStream> xStream = parseSvg(aUrl);
CPPUNIT_ASSERT(xStream.is());
uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
const uno::Reference<graphic::XSvgParser> xSvgParser = graphic::SvgTools::create(xContext);
uno::Any aAny = xSvgParser->getDrawCommands(xStream, aPath);
CPPUNIT_ASSERT(aAny.has<sal_uInt64>());
DrawRoot* pDrawRoot = reinterpret_cast<DrawRoot*>(aAny.get<sal_uInt64>());
CPPUNIT_ASSERT_EQUAL(size_t(1), pDrawRoot->maChildren.size());
CPPUNIT_ASSERT_EQUAL(basegfx::B2DRange(0, 0, 120, 120), pDrawRoot->maRectangle);
CPPUNIT_ASSERT_EQUAL(DrawCommandType::Rectangle, pDrawRoot->maChildren[0]->getType());
CPPUNIT_ASSERT_EQUAL(basegfx::B2DRange(10, 10, 110, 110),
static_cast<DrawRectangle*>(pDrawRoot->maChildren[0].get())->maRectangle);
}
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -673,6 +673,10 @@ namespace svgio
return XmlSpace_default;
}
void SvgNode::accept(Visitor & rVisitor)
{
rVisitor.visit(*this);
}
} // end of namespace svgreader
} // end of namespace svgio
......
/* -*- 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 <sal/config.h>
#include <sal/log.hxx>
#include <svgdocumenthandler.hxx>
#include <svgrectnode.hxx>
#include <svgsvgnode.hxx>
#include <svggnode.hxx>
#include <svgpathnode.hxx>
#include <svgvisitor.hxx>
namespace svgio
{
namespace svgreader
{
SvgDrawVisitor::SvgDrawVisitor()
: mpDrawRoot(std::make_shared<DrawRoot>())
, mpCurrent(mpDrawRoot)
{
}
void SvgDrawVisitor::visit(svgio::svgreader::SvgNode const& rNode)
{
switch (rNode.getType())
{
case svgio::svgreader::SVGTokenSvg:
{
auto const& rSvgNode = static_cast<svgio::svgreader::SvgSvgNode const&>(rNode);
double x = rSvgNode.getX().getNumber();
double y = rSvgNode.getY().getNumber();
double w = rSvgNode.getWidth().getNumber();
double h = rSvgNode.getHeight().getNumber();
static_cast<DrawRoot*>(mpCurrent.get())->maRectangle
= basegfx::B2DRange(x, y, x + w, y + h);
}
break;
case svgio::svgreader::SVGTokenG:
{
auto const& rGNode = static_cast<svgio::svgreader::SvgGNode const&>(rNode);
if (rGNode.getTransform() != nullptr)
{
basegfx::B2DHomMatrix rMatrix = *rGNode.getTransform();
printf("G [%f %f %f - %f %f %f - %f %f %f]\n", rMatrix.get(0, 0), rMatrix.get(0, 1),
rMatrix.get(0, 2), rMatrix.get(1, 0), rMatrix.get(1, 1), rMatrix.get(1, 2),
rMatrix.get(2, 0), rMatrix.get(2, 1), rMatrix.get(2, 2));
}
}
break;
case svgio::svgreader::SVGTokenRect:
{
auto const& rRectNode = static_cast<svgio::svgreader::SvgRectNode const&>(rNode);
double x = rRectNode.getX().getNumber();
double y = rRectNode.getY().getNumber();
double w = rRectNode.getWidth().getNumber();
double h = rRectNode.getHeight().getNumber();
auto pRectangle
= std::make_shared<DrawRectangle>(basegfx::B2DRange(x, y, x + w, y + h));
mpCurrent->maChildren.push_back(pRectangle);
}
break;
case svgio::svgreader::SVGTokenPath:
{
auto const& rPathNode = static_cast<svgio::svgreader::SvgPathNode const&>(rNode);
auto pPath = rPathNode.getPath();
if (pPath)
{
auto pDrawPath = std::make_shared<DrawPath>(*pPath);
mpCurrent->maChildren.push_back(pDrawPath);
}
}
break;
default:
break;
}
goToChildren(rNode);
}
void SvgDrawVisitor::goToChildren(svgio::svgreader::SvgNode const& rNode)
{
for (auto& rChild : rNode.getChildren())
{
rChild->accept(*this);
}
}
}
} // end of namespace svgio::svgreader
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -32,6 +32,8 @@
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <svgdocumenthandler.hxx>
#include <svgvisitor.hxx>
#include "xsvgparser.hxx"
using namespace ::com::sun::star;
......@@ -43,9 +45,11 @@ namespace svgio
class XSvgParser : public ::cppu::WeakAggImplHelper2< graphic::XSvgParser, lang::XServiceInfo >
{
private:
uno::Reference< uno::XComponentContext > context_;
std::shared_ptr<SvgDrawVisitor> mpVisitor;
protected:
uno::Reference< uno::XComponentContext > context_;
bool parseSvgXML(uno::Reference<io::XInputStream> const & xSVGStream,
uno::Reference<xml::sax::XDocumentHandler> const & xSvgDocHdl);
public:
explicit XSvgParser(
uno::Reference< uno::XComponentContext > const & context);
......@@ -57,6 +61,10 @@ namespace svgio
const uno::Reference< ::io::XInputStream >& xSVGStream,
const OUString& aAbsolutePath) override;
virtual uno::Any SAL_CALL getDrawCommands(
uno::Reference<io::XInputStream> const & xSvgStream,
const OUString& aAbsolutePath) override;
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override;
virtual sal_Bool SAL_CALL supportsService(const OUString&) override;
......@@ -97,6 +105,45 @@ namespace svgio
{
}
bool XSvgParser::parseSvgXML(uno::Reference<io::XInputStream> const & xSVGStream, uno::Reference<xml::sax::XDocumentHandler> const & xSvgDocHdl)
{
try
{
// prepare ParserInputSrouce
xml::sax::InputSource myInputSource;
myInputSource.aInputStream = xSVGStream;
// get parser
uno::Reference< xml::sax::XParser > xParser(
xml::sax::Parser::create(context_));
// fdo#60471 need to enable internal entities because
// certain ... popular proprietary products write SVG files
// that use entities to define XML namespaces.
uno::Reference<lang::XInitialization> const xInit(xParser,
uno::UNO_QUERY_THROW);
uno::Sequence<uno::Any> args(1);
args[0] <<= OUString("DoSmeplease");
xInit->initialize(args);
// connect parser and filter
xParser->setDocumentHandler(xSvgDocHdl);
// finally, parse the stream to a hierarchy of
// SVGGraphicPrimitive2D which will be embedded to the
// primitive sequence. Their decompositions will in the
// end create local low-level primitives, thus SVG will
// be processable from all our processors
xParser->parseStream(myInputSource);
}
catch(const uno::Exception& e)
{
SAL_WARN( "svg", "Parse error! : " << e);
return false;
}
return true;
}
uno::Sequence< uno::Reference< ::graphic::XPrimitive2D > > XSvgParser::getDecomposition(
const uno::Reference< ::io::XInputStream >& xSVGStream,
const OUString& aAbsolutePath )
......@@ -107,40 +154,8 @@ namespace svgio
{
// local document handler
SvgDocHdl* pSvgDocHdl = new SvgDocHdl(aAbsolutePath);
uno::Reference< xml::sax::XDocumentHandler > xSvgDocHdl(pSvgDocHdl);
try
{
// prepare ParserInputSrouce
xml::sax::InputSource myInputSource;
myInputSource.aInputStream = xSVGStream;
// get parser
uno::Reference< xml::sax::XParser > xParser(
xml::sax::Parser::create(context_));
// fdo#60471 need to enable internal entities because
// certain ... popular proprietary products write SVG files
// that use entities to define XML namespaces.
uno::Reference<lang::XInitialization> const xInit(xParser,
uno::UNO_QUERY_THROW);
uno::Sequence<uno::Any> args(1);
args[0] <<= OUString("DoSmeplease");
xInit->initialize(args);
// connect parser and filter
xParser->setDocumentHandler(xSvgDocHdl);
// finally, parse the stream to a hierarchy of
// SVGGraphicPrimitive2D which will be embedded to the
// primitive sequence. Their decompositions will in the
// end create local low-level primitives, thus SVG will
// be processable from all our processors
xParser->parseStream(myInputSource);
}
catch(const uno::Exception& e)
{
SAL_WARN( "svg", "Parse error! : " << e);
}
uno::Reference<xml::sax::XDocumentHandler> xSvgDocHdl(pSvgDocHdl);
parseSvgXML(xSVGStream, xSvgDocHdl);
// decompose to primitives
for(std::unique_ptr<SvgNode> const & pCandidate : pSvgDocHdl->getSvgDocument().getSvgNodeVector())
......@@ -159,6 +174,35 @@ namespace svgio
return comphelper::containerToSequence(aRetval);
}
uno::Any SAL_CALL XSvgParser::getDrawCommands(
uno::Reference<io::XInputStream> const & xSvgStream,
const OUString& aAbsolutePath)
{
uno::Any aAnyResult;
if (!xSvgStream.is())
return aAnyResult;
SvgDocHdl* pSvgDocHdl = new SvgDocHdl(aAbsolutePath);
uno::Reference<xml::sax::XDocumentHandler> xSvgDocHdl(pSvgDocHdl);
parseSvgXML(xSvgStream, xSvgDocHdl);
// decompose to primitives
for (std::unique_ptr<SvgNode> const & pCandidate : pSvgDocHdl->getSvgDocument().getSvgNodeVector())
{
if (Display_none != pCandidate->getDisplay())
{
mpVisitor = std::make_shared<SvgDrawVisitor>();
pCandidate->accept(*mpVisitor);
std::shared_ptr<DrawRoot> pDrawRoot(mpVisitor->getDrawRoot());
sal_uInt64 nPointer = reinterpret_cast<sal_uInt64>(pDrawRoot.get());
aAnyResult <<= sal_uInt64(nPointer);
}
}
return aAnyResult;
}
OUString SAL_CALL XSvgParser::getImplementationName()
{
return XSvgParser_getImplementationName();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment