tdf#163228 Enable vertical text options for Mongolian script
[LibreOffice.git] / svgio / source / svgreader / svgdocumenthandler.cxx
blobb74e9819673d4e98a9f4d2d95e5d684f2a99da12
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svgdocumenthandler.hxx>
21 #include <svgtoken.hxx>
22 #include <svgsvgnode.hxx>
23 #include <svggnode.hxx>
24 #include <svganode.hxx>
25 #include <svgnode.hxx>
26 #include <svgpathnode.hxx>
27 #include <svgrectnode.hxx>
28 #include <svggradientnode.hxx>
29 #include <svggradientstopnode.hxx>
30 #include <svgswitchnode.hxx>
31 #include <svgsymbolnode.hxx>
32 #include <svgusenode.hxx>
33 #include <svgcirclenode.hxx>
34 #include <svgellipsenode.hxx>
35 #include <svglinenode.hxx>
36 #include <svgpolynode.hxx>
37 #include <svgtextnode.hxx>
38 #include <svgcharacternode.hxx>
39 #include <svgtspannode.hxx>
40 #include <svgtrefnode.hxx>
41 #include <svgtextpathnode.hxx>
42 #include <svgstylenode.hxx>
43 #include <svgimagenode.hxx>
44 #include <svgclippathnode.hxx>
45 #include <svgfeblendnode.hxx>
46 #include <svgfecolormatrixnode.hxx>
47 #include <svgfecompositenode.hxx>
48 #include <svgfedropshadownode.hxx>
49 #include <svgfefloodnode.hxx>
50 #include <svgfeimagenode.hxx>
51 #include <svgfegaussianblurnode.hxx>
52 #include <svgfemergenode.hxx>
53 #include <svgfemergenodenode.hxx>
54 #include <svgfeoffsetnode.hxx>
55 #include <svgfilternode.hxx>
56 #include <svgmasknode.hxx>
57 #include <svgmarkernode.hxx>
58 #include <svgpatternnode.hxx>
59 #include <svgtitledescnode.hxx>
60 #include <sal/log.hxx>
61 #include <osl/diagnose.h>
63 #include <com/sun/star/lang/Locale.hpp>
64 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
66 using namespace com::sun::star;
68 namespace svgio::svgreader
71 namespace
73 using CharacterNodeHandlerFunc
74 = svgio::svgreader::SvgCharacterNode*(svgio::svgreader::SvgCharacterNode* pCharNode,
75 svgio::svgreader::SvgTspanNode* pParentLine,
76 svgio::svgreader::SvgCharacterNode* pLast);
77 // clean whitespace in text span
78 svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgCharacterNode* pCharNode,
79 svgio::svgreader::SvgTspanNode* pParentLine,
80 svgio::svgreader::SvgCharacterNode* pLast)
82 pCharNode->setParentLine(pParentLine);
83 return pCharNode->whiteSpaceHandling(pLast);
86 // set correct widths of text lines
87 svgio::svgreader::SvgCharacterNode* calcTextLineWidths(svgio::svgreader::SvgCharacterNode* pCharNode,
88 svgio::svgreader::SvgTspanNode* pParentLine,
89 svgio::svgreader::SvgCharacterNode* /*pLast*/)
91 if (const SvgStyleAttributes* pSvgStyleAttributes = pCharNode->getSvgStyleAttributes())
93 const drawinglayer::attribute::FontAttribute aFontAttribute(
94 svgio::svgreader::SvgCharacterNode::getFontAttribute(*pSvgStyleAttributes));
96 double fFontWidth(pSvgStyleAttributes->getFontSizeNumber().solve(*pCharNode));
97 double fFontHeight(fFontWidth);
99 css::lang::Locale aLocale;
100 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
101 aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale);
102 double fTextWidth = aTextLayouterDevice.getTextWidth(pCharNode->getText(), 0.0,
103 pCharNode->getText().getLength());
104 pParentLine->concatenateTextLineWidth(fTextWidth);
106 return nullptr; // no pLast handling
109 svgio::svgreader::SvgCharacterNode* walkRecursive(svgio::svgreader::SvgNode const* pNode,
110 svgio::svgreader::SvgTspanNode* pParentLine,
111 svgio::svgreader::SvgCharacterNode* pLast,
112 CharacterNodeHandlerFunc* pHandlerFunc)
114 if(pNode)
116 const auto& rChilds = pNode->getChildren();
117 const sal_uInt32 nCount(rChilds.size());
119 for(sal_uInt32 a(0); a < nCount; a++)
121 svgio::svgreader::SvgNode* pCandidate = rChilds[a].get();
123 if(pCandidate)
125 switch(pCandidate->getType())
127 case SVGToken::Character:
129 svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
131 pLast = pHandlerFunc(pCharNode, pParentLine, pLast);
132 break;
134 case SVGToken::Tspan:
136 svgio::svgreader::SvgTspanNode* pTspanNode = static_cast< svgio::svgreader::SvgTspanNode* >(pCandidate);
138 // If x or y exist it means it's a new line of text
139 if(!pTspanNode->getX().empty() || !pTspanNode->getY().empty())
140 pParentLine = pTspanNode;
142 // recursively handle subhierarchy
143 pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc);
144 break;
146 case SVGToken::TextPath:
147 case SVGToken::Tref:
149 // recursively handle subhierarchy
150 pLast = walkRecursive(pCandidate, pParentLine, pLast, pHandlerFunc);
151 break;
153 default:
155 OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
156 break;
163 return pLast;
166 } // end anonymous namespace
168 SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
169 : maDocument(aAbsolutePath),
170 mpTarget(nullptr)
174 SvgDocHdl::~SvgDocHdl()
176 if (mpTarget)
178 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
180 while (mpTarget->getParent())
181 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
183 const SvgNodeVector& rOwnedTopLevels = maDocument.getSvgNodeVector();
184 if (std::none_of(rOwnedTopLevels.begin(), rOwnedTopLevels.end(),
185 [&](std::unique_ptr<SvgNode> const & p) { return p.get() == mpTarget; }))
186 delete mpTarget;
188 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl destructed with active css style stack entry (!)");
191 void SvgDocHdl::startDocument( )
193 OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
194 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl startDocument with active css style stack entry (!)");
197 void SvgDocHdl::endDocument( )
199 OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
200 OSL_ENSURE(maCssContents.empty(), "SvgDocHdl endDocument with active css style stack entry (!)");
203 void SvgDocHdl::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
205 if(aName.isEmpty())
206 return;
208 const SVGToken aSVGToken(StrToSVGToken(aName, false));
210 switch (aSVGToken)
212 /// structural elements
213 case SVGToken::Symbol:
215 /// new basic node for Symbol. Content gets scanned, but
216 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
217 mpTarget = new SvgSymbolNode(maDocument, mpTarget);
218 mpTarget->parseAttributes(xAttribs);
219 break;
221 case SVGToken::Switch:
223 /// new node for Switch
224 mpTarget = new SvgSwitchNode(maDocument, mpTarget);
225 mpTarget->parseAttributes(xAttribs);
226 break;
228 case SVGToken::Defs:
229 case SVGToken::G:
231 /// new node for Defs/G
232 mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
233 mpTarget->parseAttributes(xAttribs);
234 break;
236 case SVGToken::Svg:
238 /// new node for Svg
239 mpTarget = new SvgSvgNode(maDocument, mpTarget);
240 mpTarget->parseAttributes(xAttribs);
241 break;
243 case SVGToken::Use:
245 /// new node for Use
246 mpTarget = new SvgUseNode(maDocument, mpTarget);
247 mpTarget->parseAttributes(xAttribs);
248 break;
250 case SVGToken::A:
252 /// new node for A
253 mpTarget = new SvgANode(maDocument, mpTarget);
254 mpTarget->parseAttributes(xAttribs);
255 break;
258 /// shape elements
259 case SVGToken::Circle:
261 /// new node for Circle
262 mpTarget = new SvgCircleNode(maDocument, mpTarget);
263 mpTarget->parseAttributes(xAttribs);
264 break;
266 case SVGToken::Ellipse:
268 /// new node for Ellipse
269 mpTarget = new SvgEllipseNode(maDocument, mpTarget);
270 mpTarget->parseAttributes(xAttribs);
271 break;
273 case SVGToken::Line:
275 /// new node for Line
276 mpTarget = new SvgLineNode(maDocument, mpTarget);
277 mpTarget->parseAttributes(xAttribs);
278 break;
280 case SVGToken::Path:
282 /// new node for Path
283 mpTarget = new SvgPathNode(maDocument, mpTarget);
284 mpTarget->parseAttributes(xAttribs);
285 break;
287 case SVGToken::Polygon:
289 /// new node for Polygon
290 mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
291 mpTarget->parseAttributes(xAttribs);
292 break;
294 case SVGToken::Polyline:
296 /// new node for Polyline
297 mpTarget = new SvgPolyNode(aSVGToken, maDocument, mpTarget);
298 mpTarget->parseAttributes(xAttribs);
299 break;
301 case SVGToken::Rect:
303 /// new node for Rect
304 mpTarget = new SvgRectNode(maDocument, mpTarget);
305 mpTarget->parseAttributes(xAttribs);
306 break;
308 case SVGToken::Image:
310 /// new node for Image
311 mpTarget = new SvgImageNode(maDocument, mpTarget);
312 mpTarget->parseAttributes(xAttribs);
313 break;
316 /// title and description
317 case SVGToken::Title:
318 case SVGToken::Desc:
320 /// new node for Title and/or Desc
321 mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
322 break;
325 /// gradients
326 case SVGToken::LinearGradient:
327 case SVGToken::RadialGradient:
329 mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
330 mpTarget->parseAttributes(xAttribs);
331 break;
334 /// gradient stops
335 case SVGToken::Stop:
337 mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
338 mpTarget->parseAttributes(xAttribs);
339 break;
342 /// text
343 case SVGToken::Text:
345 mpTarget = new SvgTextNode(maDocument, mpTarget);
346 mpTarget->parseAttributes(xAttribs);
347 break;
349 case SVGToken::Tspan:
351 mpTarget = new SvgTspanNode(aSVGToken, maDocument, mpTarget);
352 mpTarget->parseAttributes(xAttribs);
353 break;
355 case SVGToken::Tref:
357 mpTarget = new SvgTrefNode(maDocument, mpTarget);
358 mpTarget->parseAttributes(xAttribs);
359 break;
361 case SVGToken::TextPath:
363 mpTarget = new SvgTextPathNode(maDocument, mpTarget);
364 mpTarget->parseAttributes(xAttribs);
365 break;
368 /// styles (as stylesheets)
369 case SVGToken::Style:
371 SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
372 mpTarget = pNew;
374 // #i125326# there are attributes, read them. This will set isTextCss to false if
375 // type attribute is different to "text/css"
376 mpTarget->parseAttributes(xAttribs);
378 if(pNew->isTextCss())
380 // if it is a Css style, allow reading text between the start and end tag (see
381 // SvgDocHdl::characters for details)
382 maCssContents.emplace_back();
384 break;
387 /// structural elements clip-path and mask. Content gets scanned, but
388 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
389 case SVGToken::ClipPathNode:
391 /// new node for ClipPath
392 mpTarget = new SvgClipPathNode(maDocument, mpTarget);
393 mpTarget->parseAttributes(xAttribs);
394 break;
396 case SVGToken::Mask:
398 /// new node for Mask
399 mpTarget = new SvgMaskNode(maDocument, mpTarget);
400 mpTarget->parseAttributes(xAttribs);
401 break;
403 case SVGToken::FeBlend:
405 /// new node for feBlend
406 mpTarget = new SvgFeBlendNode(maDocument, mpTarget);
407 mpTarget->parseAttributes(xAttribs);
408 break;
410 case SVGToken::FeColorMatrix:
412 /// new node for feColorMatrix
413 mpTarget = new SvgFeColorMatrixNode(maDocument, mpTarget);
414 mpTarget->parseAttributes(xAttribs);
415 break;
417 case SVGToken::FeComposite:
419 /// new node for feComposite
420 mpTarget = new SvgFeCompositeNode(maDocument, mpTarget);
421 mpTarget->parseAttributes(xAttribs);
422 break;
424 case SVGToken::FeDropShadow:
426 /// new node for feDropShadow
427 mpTarget = new SvgFeDropShadowNode(maDocument, mpTarget);
428 mpTarget->parseAttributes(xAttribs);
429 break;
431 case SVGToken::FeFlood:
433 /// new node for feFlood
434 mpTarget = new SvgFeFloodNode(maDocument, mpTarget);
435 mpTarget->parseAttributes(xAttribs);
436 break;
438 case SVGToken::FeImage:
440 /// new node for feImage
441 mpTarget = new SvgFeImageNode(maDocument, mpTarget);
442 mpTarget->parseAttributes(xAttribs);
443 break;
445 case SVGToken::FeGaussianBlur:
447 /// new node for feGaussianBlur
448 mpTarget = new SvgFeGaussianBlurNode(maDocument, mpTarget);
449 mpTarget->parseAttributes(xAttribs);
450 break;
452 case SVGToken::FeMerge:
454 /// new node for feMerge
455 mpTarget = new SvgFeMergeNode(aSVGToken, maDocument, mpTarget);
456 mpTarget->parseAttributes(xAttribs);
457 break;
459 case SVGToken::FeMergeNode:
461 /// new node for feMergeNode
462 mpTarget = new SvgFeMergeNodeNode(maDocument, mpTarget);
463 mpTarget->parseAttributes(xAttribs);
464 break;
466 case SVGToken::FeOffset:
468 /// new node for feOffset
469 mpTarget = new SvgFeOffsetNode(maDocument, mpTarget);
470 mpTarget->parseAttributes(xAttribs);
471 break;
473 case SVGToken::Filter:
475 /// new node for Filter
476 mpTarget = new SvgFilterNode(aSVGToken, maDocument, mpTarget);
477 mpTarget->parseAttributes(xAttribs);
478 break;
481 /// structural element marker
482 case SVGToken::Marker:
484 /// new node for marker
485 mpTarget = new SvgMarkerNode(maDocument, mpTarget);
486 mpTarget->parseAttributes(xAttribs);
487 break;
490 /// structural element pattern
491 case SVGToken::Pattern:
493 /// new node for pattern
494 mpTarget = new SvgPatternNode(maDocument, mpTarget);
495 mpTarget->parseAttributes(xAttribs);
496 break;
499 default:
501 mpTarget = new SvgNode(SVGToken::Unknown, maDocument, mpTarget);
502 break;
507 void SvgDocHdl::endElement( const OUString& aName )
509 if(aName.isEmpty())
510 return;
512 if(!mpTarget)
513 return;
515 const SVGToken aSVGToken(StrToSVGToken(aName, false));
516 SvgNode* pTextNode(SVGToken::Text == aSVGToken ? mpTarget : nullptr);
517 SvgStyleNode* pCssStyle(SVGToken::Style == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr);
518 SvgTitleDescNode* pSvgTitleDescNode(SVGToken::Title == aSVGToken || SVGToken::Desc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr);
520 if(!mpTarget->getParent())
522 // last element closing, save this tree
523 maDocument.appendNode(std::unique_ptr<SvgNode>(mpTarget));
526 mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
528 if (pSvgTitleDescNode && mpTarget)
530 const OUString& aText(pSvgTitleDescNode->getText());
532 if(!aText.isEmpty())
534 mpTarget->parseAttribute(aSVGToken, aText);
538 if(pCssStyle && pCssStyle->isTextCss())
540 // css style parsing
541 if(!maCssContents.empty())
543 // need to interpret css styles and remember them as StyleSheets
544 // #125325# Caution! the Css content may contain block comments
545 // (see http://www.w3.org/wiki/CSS_basics#CSS_comments). These need
546 // to be removed first
547 const OUString aCommentFreeSource(removeBlockComments(*(maCssContents.end() - 1)));
549 if(aCommentFreeSource.getLength())
551 pCssStyle->addCssStyleSheet(aCommentFreeSource);
554 maCssContents.pop_back();
556 else
558 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
562 if(pTextNode)
564 // cleanup read strings
565 // First pass: handle whitespace. This works in a way that handling a following
566 // node may append a space to a previous node; so correct line width calculation
567 // may only happen after this pass finishes
568 walkRecursive(pTextNode, static_cast<SvgTspanNode*>(pTextNode), nullptr, whiteSpaceHandling);
569 // Second pass: calculate line widths
570 walkRecursive(pTextNode, static_cast<SvgTspanNode*>(pTextNode), nullptr, calcTextLineWidths);
574 void SvgDocHdl::characters( const OUString& aChars )
576 const sal_uInt32 nLength(aChars.getLength());
578 if(!(mpTarget && nLength))
579 return;
581 switch(mpTarget->getType())
583 case SVGToken::Text:
584 case SVGToken::Tspan:
585 case SVGToken::TextPath:
587 const auto& rChilds = mpTarget->getChildren();
589 if(!rChilds.empty())
591 SvgNode* pChild = rChilds[rChilds.size() - 1].get();
592 if ( pChild->getType() == SVGToken::Character )
594 SvgCharacterNode& rSvgCharacterNode = static_cast< SvgCharacterNode& >(*pChild);
596 // concatenate to current character span
597 rSvgCharacterNode.concatenate(aChars);
598 break;
602 // add character span as simplified tspan (no arguments)
603 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
604 new SvgCharacterNode(maDocument, mpTarget, aChars);
605 break;
607 case SVGToken::Style:
609 SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
611 if(rSvgStyleNode.isTextCss())
613 // collect characters for css style
614 if(!maCssContents.empty())
616 const OUString aTrimmedChars(aChars.trim());
618 if(!aTrimmedChars.isEmpty())
620 std::vector< OUString >::iterator aString(maCssContents.end() - 1);
621 (*aString) += aTrimmedChars;
624 else
626 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
629 break;
631 case SVGToken::Title:
632 case SVGToken::Desc:
634 SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
636 // add text directly to SvgTitleDescNode
637 rSvgTitleDescNode.concatenate(aChars);
638 break;
640 default:
642 // characters not used by a known node
643 break;
648 void SvgDocHdl::ignorableWhitespace(const OUString& /*aWhitespaces*/)
652 void SvgDocHdl::processingInstruction(const OUString& /*aTarget*/, const OUString& /*aData*/)
656 void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/)
659 } // end of namespace svgio
661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */