LibreOffice Module sdext (master) 1
genericelements.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21#include <genericelements.hxx>
22#include <pdfiprocessor.hxx>
23#include <pdfihelper.hxx>
24
25#include <com/sun/star/i18n/BreakIterator.hpp>
26#include <com/sun/star/i18n/ScriptType.hpp>
29#include <sal/log.hxx>
30
31namespace pdfi
32{
33
35{
36}
37
39{
40 for( auto it = Children.begin(); it != Children.end(); ++it )
41 (*it)->visitedBy( rVisitor, it );
42}
43
44void Element::setParent( std::list<std::unique_ptr<Element>>::iterator const & el, Element* pNewParent )
45{
46 if( pNewParent )
47 {
48 pNewParent->Children.splice( pNewParent->Children.end(), (*el)->Parent->Children, el );
49 (*el)->Parent = pNewParent;
50 }
51}
52
53void Element::updateGeometryWith( const Element* pMergeFrom )
54{
55 if( w == 0 && h == 0 )
56 {
57 x = pMergeFrom->x;
58 y = pMergeFrom->y;
59 w = pMergeFrom->w;
60 h = pMergeFrom->h;
61 }
62 else
63 {
64 if( pMergeFrom->x < x )
65 {
66 w += x - pMergeFrom->x;
67 x = pMergeFrom->x;
68 }
69 if( pMergeFrom->x+pMergeFrom->w > x+w )
70 w = pMergeFrom->w+pMergeFrom->x - x;
71 if( pMergeFrom->y < y )
72 {
73 h += y - pMergeFrom->y;
74 y = pMergeFrom->y;
75 }
76 if( pMergeFrom->y+pMergeFrom->h > y+h )
77 h = pMergeFrom->h+pMergeFrom->y - y;
78 }
79}
80
81
82#if OSL_DEBUG_LEVEL > 0
83#include <typeinfo>
84void Element::emitStructure( int nLevel)
85{
86 SAL_INFO( "sdext", std::string(nLevel, ' ') << "<" << typeid( *this ).name() << " " << this << "> ("
87 << std::setprecision(1) << x << "," << y << ")+(" << w << "x" << h << ")" );
88 for (auto const& child : Children)
89 child->emitStructure(nLevel+1);
90 SAL_INFO( "sdext", std::string(nLevel, ' ') << "</" << typeid( *this ).name() << ">" );
91}
92#endif
93
94void ListElement::visitedBy( ElementTreeVisitor& visitor, const std::list< std::unique_ptr<Element> >::const_iterator& )
95{
96 // this is only an inner node
97 applyToChildren(visitor);
98}
99
101 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt )
102{
103 rVisitor.visit(*this,rParentIt);
104}
105
107 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt )
108{
109 rVisitor.visit(*this,rParentIt);
110}
111
113 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt )
114{
115 rVisitor.visit(*this,rParentIt);
116}
117
119 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt)
120{
121 rVisitor.visit( *this, rParentIt);
122}
123
125 sal_Int32 nGCId,
126 const basegfx::B2DPolyPolygon& rPolyPoly,
127 sal_Int8 nAction )
128 : DrawElement( pParent, nGCId ),
129 PolyPoly( rPolyPoly ),
130 Action( nAction )
131{
132}
133
135{
136 basegfx::B2DRange aRange;
139 else
141 x = aRange.getMinX();
142 y = aRange.getMinY();
143 w = aRange.getWidth();
144 h = aRange.getHeight();
145
146 // fdo#32330 - non-closed paths will not show up filled in LibO
147 if( Action & (PATH_FILL | PATH_EOFILL) )
148 PolyPoly.setClosed(true);
149}
150
152 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt)
153{
154 rVisitor.visit( *this, rParentIt);
155}
156
157#if OSL_DEBUG_LEVEL > 0
159{
160 SAL_INFO( "sdext", std::string(nLevel, ' ') << "<" << typeid( *this ).name() << " " << this << ">" );
161 SAL_INFO( "sdext", "path=" );
162 int nPoly = PolyPoly.count();
163 for( int i = 0; i < nPoly; i++ )
164 {
165 OUStringBuffer buff;
167 int nPoints = aPoly.count();
168 for( int n = 0; n < nPoints; n++ )
169 {
170 basegfx::B2DPoint aPoint = aPoly.getB2DPoint( n );
171 buff.append( " (" + OUString::number(aPoint.getX()) + "," + OUString::number(aPoint.getY()) + ")");
172 }
173 SAL_INFO( "sdext", " " << buff.makeStringAndClear() );
174 }
175 for (auto const& child : Children)
176 child->emitStructure( nLevel+1 );
177 SAL_INFO( "sdext", std::string(nLevel, ' ') << "</" << typeid( *this ).name() << ">");
178}
179#endif
180
182 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt )
183{
184 rVisitor.visit(*this,rParentIt);
185}
186
188{
189 TextElement* pText = nullptr, *pLastText = nullptr;
190 for( auto& rxChild : Children )
191 {
192 // a paragraph containing subparagraphs cannot be single lined
193 if( dynamic_cast< ParagraphElement* >(rxChild.get()) != nullptr )
194 return false;
195
196 pText = rxChild->dynCastAsTextElement();
197 if( pText )
198 {
199 const FontAttributes& rFont = rProc.getFont( pText->FontId );
200 if( pText->h > rFont.size*1.5 )
201 return false;
202 if( pLastText )
203 {
204 if( pText->y > pLastText->y+pLastText->h ||
205 pLastText->y > pText->y+pText->h )
206 return false;
207 }
208 else
209 pLastText = pText;
210 }
211 }
212
213 // a paragraph without a single text is not considered single lined
214 return pLastText != nullptr;
215}
216
218{
219 double line_h = 0;
220 for( auto& rxChild : Children )
221 {
222 ParagraphElement* pPara = dynamic_cast< ParagraphElement* >(rxChild.get());
223 TextElement* pText = nullptr;
224 if( pPara )
225 {
226 double lh = pPara->getLineHeight( rProc );
227 if( lh > line_h )
228 line_h = lh;
229 }
230 else if( (pText = rxChild->dynCastAsTextElement()) != nullptr )
231 {
232 const FontAttributes& rFont = rProc.getFont( pText->FontId );
233 double lh = pText->h;
234 if( pText->h > rFont.size*1.5 )
235 lh = rFont.size;
236 if( lh > line_h )
237 line_h = lh;
238 }
239 }
240 return line_h;
241}
242
244{
245 TextElement* pText = nullptr;
246 auto it = std::find_if(Children.begin(), Children.end(),
247 [](const std::unique_ptr<Element>& rxElem) { return rxElem->dynCastAsTextElement() != nullptr; });
248 if (it != Children.end())
249 pText = (*it)->dynCastAsTextElement();
250 return pText;
251}
252
254{
255}
256
258 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt )
259{
260 rVisitor.visit(*this, rParentIt);
261}
262
263bool PageElement::resolveHyperlink( const std::list<std::unique_ptr<Element>>::iterator& link_it, std::list<std::unique_ptr<Element>>& rElements )
264{
265 HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(link_it->get());
266 if( ! pLink ) // sanity check
267 return false;
268
269 for( auto it = rElements.begin(); it != rElements.end(); ++it )
270 {
271 if( (*it)->x >= pLink->x && (*it)->x + (*it)->w <= pLink->x + pLink->w &&
272 (*it)->y >= pLink->y && (*it)->y + (*it)->h <= pLink->y + pLink->h )
273 {
274 TextElement* pText = (*it)->dynCastAsTextElement();
275 if( pText )
276 {
277 if( pLink->Children.empty() )
278 {
279 // insert the hyperlink before the frame
280 rElements.splice( it, Hyperlinks.Children, link_it );
281 pLink->Parent = (*it)->Parent;
282 }
283 // move text element into hyperlink
284 auto next = it;
285 ++next;
287 it = next;
288 --it;
289 continue;
290 }
291 // a link can contain multiple text elements or a single frame
292 if( ! pLink->Children.empty() )
293 continue;
294 if( dynamic_cast<ParagraphElement*>(it->get()) )
295 {
296 if( resolveHyperlink( link_it, (*it)->Children ) )
297 break;
298 continue;
299 }
300 FrameElement* pFrame = dynamic_cast<FrameElement*>(it->get());
301 if( pFrame )
302 {
303 // insert the hyperlink before the frame
304 rElements.splice( it, Hyperlinks.Children, link_it );
305 pLink->Parent = (*it)->Parent;
306 // move frame into hyperlink
308 break;
309 }
310 }
311 }
312 return ! pLink->Children.empty();
313}
314
316{
317 while( ! Hyperlinks.Children.empty() )
318 {
319 if( ! resolveHyperlink( Hyperlinks.Children.begin(), Children ) )
320 {
321 Hyperlinks.Children.pop_front();
322 }
323 }
324}
325
327{
328 resolveUnderlines(rProc);
329}
330
332{
333 // FIXME: currently the algorithm used is quadratic
334 // this could be solved by some sorting beforehand
335
336 std::vector<Element*> textAndHypers;
337 textAndHypers.reserve(Children.size());
338 for (auto const & p : Children)
339 {
340 if (p->dynCastAsTextElement() || dynamic_cast<HyperlinkElement*>(p.get()))
341 textAndHypers.push_back(p.get());
342 }
343
344 auto poly_it = Children.begin();
345 while( poly_it != Children.end() )
346 {
347 PolyPolyElement* pPoly = dynamic_cast< PolyPolyElement* >(poly_it->get());
348 if( ! pPoly || ! pPoly->Children.empty() )
349 {
350 ++poly_it;
351 continue;
352 }
353 /* check for: no filling
354 * only two points (FIXME: handle small rectangles, too)
355 * y coordinates of points are equal
356 */
357 if( pPoly->Action != PATH_STROKE )
358 {
359 ++poly_it;
360 continue;
361 }
362 if( pPoly->PolyPoly.count() != 1 )
363 {
364 ++poly_it;
365 continue;
366 }
367
368 bool bRemovePoly = false;
370 if( aPoly.count() != 2 ||
371 aPoly.getB2DPoint(0).getY() != aPoly.getB2DPoint(1).getY() )
372 {
373 ++poly_it;
374 continue;
375 }
376 double l_x = aPoly.getB2DPoint(0).getX();
377 double r_x = aPoly.getB2DPoint(1).getX();
378 double u_y;
379 if( r_x < l_x )
380 {
381 u_y = r_x; r_x = l_x; l_x = u_y;
382 }
383 u_y = aPoly.getB2DPoint(0).getY();
384 for( Element* pEle : textAndHypers )
385 {
386 if( pEle->y <= u_y && pEle->y + pEle->h*1.1 >= u_y )
387 {
388 // first: is the element underlined completely ?
389 if( pEle->x + pEle->w*0.1 >= l_x &&
390 pEle->x + pEle->w*0.9 <= r_x )
391 {
392 TextElement* pText = pEle->dynCastAsTextElement();
393 if( pText )
394 {
395 const GraphicsContext& rTextGC = rProc.getGraphicsContext( pText->GCId );
396 if( ! rTextGC.isRotatedOrSkewed() )
397 {
398 bRemovePoly = true;
399 // retrieve ID for modified font
400 FontAttributes aAttr = rProc.getFont( pText->FontId );
401 aAttr.isUnderline = true;
402 pText->FontId = rProc.getFontId( aAttr );
403 }
404 }
405 else // must be HyperlinkElement
406 bRemovePoly = true;
407 }
408 // second: hyperlinks may be larger than their underline
409 // since they are just arbitrary rectangles in the action definition
410 else if( l_x >= pEle->x && r_x <= pEle->x+pEle->w &&
411 dynamic_cast< HyperlinkElement* >(pEle) != nullptr )
412 {
413 bRemovePoly = true;
414 }
415 }
416 }
417 if( bRemovePoly )
418 poly_it = Children.erase( poly_it );
419 else
420 ++poly_it;
421 }
422}
423
425{
426}
427
429 const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt)
430{
431 rVisitor.visit(*this, rParentIt);
432}
433
434bool isComplex(const css::uno::Reference<css::i18n::XBreakIterator>& rBreakIterator, TextElement* const pTextElem) {
435 OUString str(pTextElem->Text.toString());
436 for(int i=0; i< str.getLength(); i++)
437 {
438 sal_Int16 nType = rBreakIterator->getScriptType(str, i);
439 if (nType == css::i18n::ScriptType::COMPLEX)
440 {
441 return true;
442 }
443 }
444 return false;
445}
446
447}
448
449/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void setClosed(bool bNew)
bool areControlPointsUsed() const
sal_uInt32 count() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
sal_uInt32 count() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
Main entry from the parser.
const GraphicsContext & getGraphicsContext(sal_Int32 nGCId) const
const FontAttributes & getFont(sal_Int32 nFontId) const
sal_Int32 getFontId(const FontAttributes &rAttr) const
void * p
sal_Int64 n
SvBaseLink * pLink
#define SAL_INFO(area, stream)
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
B2DRange getRange(const B2DPolygon &rCandidate)
int i
@ PATH_EOFILL
Definition: pdfihelper.hxx:48
@ PATH_STROKE
Definition: pdfihelper.hxx:48
@ PATH_FILL
Definition: pdfihelper.hxx:48
bool isComplex(const css::uno::Reference< css::i18n::XBreakIterator > &rBreakIterator, TextElement *const pTextElem)
QPRO_FUNC_TYPE nType
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &) override
To be implemented by every tree node that needs to be visitable.
virtual ~DocumentElement() override
To be visited by all tree element types.
virtual void visit(HyperlinkElement &, const std::list< std::unique_ptr< Element > >::const_iterator &)=0
static void setParent(std::list< std::unique_ptr< Element > >::iterator const &el, Element *pNewParent)
el must be a valid dereferenceable iterator of el->Parent->Children pNewParent must not be NULL
std::list< std::unique_ptr< Element > > Children
virtual ~Element()
virtual void emitStructure(int nLevel)
void applyToChildren(ElementTreeVisitor &)
Apply visitor to all children.
void updateGeometryWith(const Element *pMergeFrom)
Union element geometry with given element.
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &) override
To be implemented by every tree node that needs to be visitable.
bool isRotatedOrSkewed() const
Definition: pdfihelper.hxx:180
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &) override
To be implemented by every tree node that needs to be visitable.
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &) override
To be implemented by every tree node that needs to be visitable.
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &) override
To be implemented by every tree node that needs to be visitable.
void resolveUnderlines(PDFIProcessor const &rProc)
bool resolveHyperlink(const std::list< std::unique_ptr< Element > >::iterator &link_it, std::list< std::unique_ptr< Element > > &rElements)
virtual ~PageElement() override
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &rParentIt) override
To be implemented by every tree node that needs to be visitable.
void resolveFontStyles(PDFIProcessor const &rProc)
TextElement * getFirstTextChild() const
double getLineHeight(PDFIProcessor &rProc) const
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &rParentIt) override
To be implemented by every tree node that needs to be visitable.
bool isSingleLined(PDFIProcessor const &rProc) const
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &rParentIt) override
To be implemented by every tree node that needs to be visitable.
basegfx::B2DPolyPolygon PolyPoly
PolyPolyElement(Element *pParent, sal_Int32 nGCId, const basegfx::B2DPolyPolygon &rPolyPoly, sal_Int8 nAction)
virtual void emitStructure(int nLevel) override
virtual const TextElement * dynCastAsTextElement() const override
To avoid some dynamic_cast cost.
OUStringBuffer Text
virtual void visitedBy(ElementTreeVisitor &, const std::list< std::unique_ptr< Element > >::const_iterator &) override
To be implemented by every tree node that needs to be visitable.
signed char sal_Int8