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