1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sdext.hxx"
26
27 #include "xmlemitter.hxx"
28 #include "genericelements.hxx"
29 #include "pdfiprocessor.hxx"
30 #include "pdfihelper.hxx"
31 #include "style.hxx"
32
33
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <basegfx/range/b2drange.hxx>
36
37 namespace pdfi
38 {
39
~ElementFactory()40 ElementFactory::~ElementFactory()
41 {
42 }
43
~Element()44 Element::~Element()
45 {
46 while( !Children.empty() )
47 {
48 Element* pCurr( Children.front() );
49 delete pCurr;
50 Children.pop_front();
51 }
52 }
53
applyToChildren(ElementTreeVisitor & rVisitor)54 void Element::applyToChildren( ElementTreeVisitor& rVisitor )
55 {
56 for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it )
57 (*it)->visitedBy( rVisitor, it );
58 }
59
setParent(std::list<Element * >::iterator & el,Element * pNewParent)60 void Element::setParent( std::list<Element*>::iterator& el, Element* pNewParent )
61 {
62 if( pNewParent )
63 {
64 pNewParent->Children.splice( pNewParent->Children.end(), (*el)->Parent->Children, el );
65 (*el)->Parent = pNewParent;
66 }
67 }
68
updateGeometryWith(const Element * pMergeFrom)69 void Element::updateGeometryWith( const Element* pMergeFrom )
70 {
71 if( w == 0 && h == 0 )
72 {
73 x = pMergeFrom->x;
74 y = pMergeFrom->y;
75 w = pMergeFrom->w;
76 h = pMergeFrom->h;
77 }
78 else
79 {
80 if( pMergeFrom->x < x )
81 {
82 w += x - pMergeFrom->x;
83 x = pMergeFrom->x;
84 }
85 if( pMergeFrom->x+pMergeFrom->w > x+w )
86 w = pMergeFrom->w+pMergeFrom->x - x;
87 if( pMergeFrom->y < y )
88 {
89 h += y - pMergeFrom->y;
90 y = pMergeFrom->y;
91 }
92 if( pMergeFrom->y+pMergeFrom->h > y+h )
93 h = pMergeFrom->h+pMergeFrom->y - y;
94 }
95 }
96
97
98 #if OSL_DEBUG_LEVEL > 1
99 #include <typeinfo>
emitStructure(int nLevel)100 void Element::emitStructure( int nLevel)
101 {
102 OSL_TRACE( "%*s<%s %p> (%.1f,%.1f)+(%.1fx%.1f)\n",
103 nLevel, "", typeid( *this ).name(), this,
104 x, y, w, h );
105 for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it )
106 (*it)->emitStructure(nLevel+1 );
107 OSL_TRACE( "%*s</%s>\n", nLevel, "", typeid( *this ).name() );
108 }
109 #endif
110
visitedBy(ElementTreeVisitor & visitor,const std::list<Element * >::const_iterator &)111 void ListElement::visitedBy( ElementTreeVisitor& visitor, const std::list< Element* >::const_iterator& )
112 {
113 // this is only an inner node
114 applyToChildren(visitor);
115 }
116
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)117 void HyperlinkElement::visitedBy( ElementTreeVisitor& rVisitor,
118 const std::list< Element* >::const_iterator& rParentIt )
119 {
120 rVisitor.visit(*this,rParentIt);
121 }
122
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)123 void TextElement::visitedBy( ElementTreeVisitor& rVisitor,
124 const std::list< Element* >::const_iterator& rParentIt )
125 {
126 rVisitor.visit(*this,rParentIt);
127 }
128
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)129 void FrameElement::visitedBy( ElementTreeVisitor& rVisitor,
130 const std::list< Element* >::const_iterator& rParentIt )
131 {
132 rVisitor.visit(*this,rParentIt);
133 }
134
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)135 void ImageElement::visitedBy( ElementTreeVisitor& rVisitor,
136 const std::list< Element* >::const_iterator& rParentIt)
137 {
138 rVisitor.visit( *this, rParentIt);
139 }
140
PolyPolyElement(Element * pParent,sal_Int32 nGCId,const basegfx::B2DPolyPolygon & rPolyPoly,sal_Int8 nAction)141 PolyPolyElement::PolyPolyElement( Element* pParent,
142 sal_Int32 nGCId,
143 const basegfx::B2DPolyPolygon& rPolyPoly,
144 sal_Int8 nAction )
145 : DrawElement( pParent, nGCId ),
146 PolyPoly( rPolyPoly ),
147 Action( nAction )
148 {
149 }
150
updateGeometry()151 void PolyPolyElement::updateGeometry()
152 {
153 basegfx::B2DRange aRange;
154 if( PolyPoly.areControlPointsUsed() )
155 aRange = basegfx::tools::getRange( basegfx::tools::adaptiveSubdivideByAngle( PolyPoly ) );
156 else
157 aRange = basegfx::tools::getRange( PolyPoly );
158 x = aRange.getMinX();
159 y = aRange.getMinY();
160 w = aRange.getWidth();
161 h = aRange.getHeight();
162 }
163
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)164 void PolyPolyElement::visitedBy( ElementTreeVisitor& rVisitor,
165 const std::list< Element* >::const_iterator& rParentIt)
166 {
167 rVisitor.visit( *this, rParentIt);
168 }
169
170 #if OSL_DEBUG_LEVEL > 1
emitStructure(int nLevel)171 void PolyPolyElement::emitStructure( int nLevel)
172 {
173 OSL_TRACE( "%*s<%s %p>\n", nLevel, "", typeid( *this ).name(), this );
174 OSL_TRACE( "path=" );
175 int nPoly = PolyPoly.count();
176 for( int i = 0; i < nPoly; i++ )
177 {
178 basegfx::B2DPolygon aPoly = PolyPoly.getB2DPolygon( i );
179 int nPoints = aPoly.count();
180 for( int n = 0; n < nPoints; n++ )
181 {
182 basegfx::B2DPoint aPoint = aPoly.getB2DPoint( n );
183 OSL_TRACE( " (%g,%g)", aPoint.getX(), aPoint.getY() );
184 }
185 OSL_TRACE( "\n" );
186 }
187 for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it )
188 (*it)->emitStructure( nLevel+1 );
189 OSL_TRACE( "%*s</%s>\n", nLevel, "", typeid( *this ).name() );
190 }
191 #endif
192
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)193 void ParagraphElement::visitedBy( ElementTreeVisitor& rVisitor,
194 const std::list< Element* >::const_iterator& rParentIt )
195 {
196 rVisitor.visit(*this,rParentIt);
197 }
198
isSingleLined(PDFIProcessor & rProc) const199 bool ParagraphElement::isSingleLined( PDFIProcessor& rProc ) const
200 {
201 std::list< Element* >::const_iterator it = Children.begin();
202 TextElement* pText = NULL, *pLastText = NULL;
203 while( it != Children.end() )
204 {
205 // a paragraph containing subparagraphs cannot be single lined
206 if( dynamic_cast< ParagraphElement* >(*it) != NULL )
207 return false;
208
209 pText = dynamic_cast< TextElement* >(*it);
210 if( pText )
211 {
212 const FontAttributes& rFont = rProc.getFont( pText->FontId );
213 if( pText->h > rFont.size*1.5 )
214 return false;
215 if( pLastText )
216 {
217 if( pText->y > pLastText->y+pLastText->h ||
218 pLastText->y > pText->y+pText->h )
219 return false;
220 }
221 else
222 pLastText = pText;
223 }
224 ++it;
225 }
226
227 // a paragraph without a single text is not considered single lined
228 return pLastText != NULL;
229 }
230
getLineHeight(PDFIProcessor & rProc) const231 double ParagraphElement::getLineHeight( PDFIProcessor& rProc ) const
232 {
233 double line_h = 0;
234 for( std::list< Element* >::const_iterator it = Children.begin(); it != Children.end(); ++it )
235 {
236 ParagraphElement* pPara = dynamic_cast< ParagraphElement* >(*it);
237 TextElement* pText = NULL;
238 if( pPara )
239 {
240 double lh = pPara->getLineHeight( rProc );
241 if( lh > line_h )
242 line_h = lh;
243 }
244 else if( (pText = dynamic_cast< TextElement* >( *it )) != NULL )
245 {
246 const FontAttributes& rFont = rProc.getFont( pText->FontId );
247 double lh = pText->h;
248 if( pText->h > rFont.size*1.5 )
249 lh = rFont.size;
250 if( lh > line_h )
251 line_h = lh;
252 }
253 }
254 return line_h;
255 }
256
getFirstTextChild() const257 TextElement* ParagraphElement::getFirstTextChild() const
258 {
259 TextElement* pText = NULL;
260 for( std::list< Element* >::const_iterator it = Children.begin();
261 it != Children.end() && ! pText; ++it )
262 {
263 pText = dynamic_cast<TextElement*>(*it);
264 }
265 return pText;
266 }
267
~PageElement()268 PageElement::~PageElement()
269 {
270 if( HeaderElement )
271 delete HeaderElement;
272 if( FooterElement )
273 delete FooterElement;
274 }
275
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)276 void PageElement::visitedBy( ElementTreeVisitor& rVisitor,
277 const std::list< Element* >::const_iterator& rParentIt )
278 {
279 rVisitor.visit(*this, rParentIt);
280 }
281
updateParagraphGeometry(Element * pEle)282 void PageElement::updateParagraphGeometry( Element* pEle )
283 {
284 // update geometry of children
285 for( std::list< Element* >::iterator it = pEle->Children.begin();
286 it != pEle->Children.end(); ++it )
287 {
288 updateParagraphGeometry( *it );
289 }
290 // if this is a paragraph itself, then update according to children geometry
291 if( dynamic_cast<ParagraphElement*>(pEle) )
292 {
293 for( std::list< Element* >::iterator it = pEle->Children.begin();
294 it != pEle->Children.end(); ++it )
295 {
296 Element* pChild = NULL;
297 TextElement* pText = dynamic_cast<TextElement*>(*it);
298 if( pText )
299 pChild = pText;
300 else
301 {
302 ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*it);
303 if( pPara )
304 pChild = pPara;
305 }
306 if( pChild )
307 pEle->updateGeometryWith( pChild );
308 }
309 }
310 }
311
resolveHyperlink(std::list<Element * >::iterator link_it,std::list<Element * > & rElements)312 bool PageElement::resolveHyperlink( std::list<Element*>::iterator link_it, std::list<Element*>& rElements )
313 {
314 HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(*link_it);
315 if( ! pLink ) // sanity check
316 return false;
317
318 for( std::list<Element*>::iterator it = rElements.begin(); it != rElements.end(); ++it )
319 {
320 if( (*it)->x >= pLink->x && (*it)->x + (*it)->w <= pLink->x + pLink->w &&
321 (*it)->y >= pLink->y && (*it)->y + (*it)->h <= pLink->y + pLink->h )
322 {
323 TextElement* pText = dynamic_cast<TextElement*>(*it);
324 if( pText )
325 {
326 if( pLink->Children.empty() )
327 {
328 // insert the hyperlink before the frame
329 rElements.splice( it, Hyperlinks.Children, link_it );
330 pLink->Parent = (*it)->Parent;
331 }
332 // move text element into hyperlink
333 std::list<Element*>::iterator next = it;
334 ++next;
335 Element::setParent( it, pLink );
336 it = next;
337 --it;
338 continue;
339 }
340 // a link can contain multiple text elements or a single frame
341 if( ! pLink->Children.empty() )
342 continue;
343 if( dynamic_cast<ParagraphElement*>(*it) )
344 {
345 if( resolveHyperlink( link_it, (*it)->Children ) )
346 break;
347 continue;
348 }
349 FrameElement* pFrame = dynamic_cast<FrameElement*>(*it);
350 if( pFrame )
351 {
352 // insert the hyperlink before the frame
353 rElements.splice( it, Hyperlinks.Children, link_it );
354 pLink->Parent = (*it)->Parent;
355 // move frame into hyperlink
356 Element::setParent( it, pLink );
357 break;
358 }
359 }
360 }
361 return ! pLink->Children.empty();
362 }
363
resolveHyperlinks()364 void PageElement::resolveHyperlinks()
365 {
366 while( ! Hyperlinks.Children.empty() )
367 {
368 if( ! resolveHyperlink( Hyperlinks.Children.begin(), Children ) )
369 {
370 delete Hyperlinks.Children.front();
371 Hyperlinks.Children.pop_front();
372 }
373 }
374 }
375
resolveFontStyles(PDFIProcessor & rProc)376 void PageElement::resolveFontStyles( PDFIProcessor& rProc )
377 {
378 resolveUnderlines(rProc);
379 }
380
resolveUnderlines(PDFIProcessor & rProc)381 void PageElement::resolveUnderlines( PDFIProcessor& rProc )
382 {
383 // FIXME: currently the algorithm used is quadratic
384 // this could be solved by some sorting beforehand
385
386 std::list< Element* >::iterator poly_it = Children.begin();
387 while( poly_it != Children.end() )
388 {
389 PolyPolyElement* pPoly = dynamic_cast< PolyPolyElement* >(*poly_it);
390 if( ! pPoly || ! pPoly->Children.empty() )
391 {
392 ++poly_it;
393 continue;
394 }
395 /* check for: no filling
396 * only two points (FIXME: handle small rectangles, too)
397 * y coordinates of points are equal
398 */
399 if( pPoly->Action != PATH_STROKE )
400 {
401 ++poly_it;
402 continue;
403 }
404 if( pPoly->PolyPoly.count() != 1 )
405 {
406 ++poly_it;
407 continue;
408 }
409
410 bool bRemovePoly = false;
411 basegfx::B2DPolygon aPoly = pPoly->PolyPoly.getB2DPolygon(0);
412 if( aPoly.count() != 2 ||
413 aPoly.getB2DPoint(0).getY() != aPoly.getB2DPoint(1).getY() )
414 {
415 ++poly_it;
416 continue;
417 }
418 double l_x = aPoly.getB2DPoint(0).getX();
419 double r_x = aPoly.getB2DPoint(1).getX();
420 double u_y;
421 if( r_x < l_x )
422 {
423 u_y = r_x; r_x = l_x; l_x = u_y;
424 }
425 u_y = aPoly.getB2DPoint(0).getY();
426 for( std::list< Element*>::iterator it = Children.begin();
427 it != Children.end(); ++it )
428 {
429 Element* pEle = *it;
430 if( pEle->y <= u_y && pEle->y + pEle->h*1.1 >= u_y )
431 {
432 // first: is the element underlined completely ?
433 if( pEle->x + pEle->w*0.1 >= l_x &&
434 pEle->x + pEle->w*0.9 <= r_x )
435 {
436 TextElement* pText = dynamic_cast< TextElement* >(pEle);
437 if( pText )
438 {
439 const GraphicsContext& rTextGC = rProc.getGraphicsContext( pText->GCId );
440 if( ! rTextGC.isRotatedOrSkewed() )
441 {
442 bRemovePoly = true;
443 // retrieve ID for modified font
444 FontAttributes aAttr = rProc.getFont( pText->FontId );
445 aAttr.isUnderline = true;
446 pText->FontId = rProc.getFontId( aAttr );
447 }
448 }
449 else if( dynamic_cast< HyperlinkElement* >(pEle) )
450 bRemovePoly = true;
451 }
452 // second: hyperlinks may be larger than their underline
453 // since they are just arbitrary rectangles in the action definition
454 else if( dynamic_cast< HyperlinkElement* >(pEle) != NULL &&
455 l_x >= pEle->x && r_x <= pEle->x+pEle->w )
456 {
457 bRemovePoly = true;
458 }
459 }
460 }
461 if( bRemovePoly )
462 {
463 std::list< Element* >::iterator next_it = poly_it;
464 ++next_it;
465 Children.erase( poly_it );
466 delete pPoly;
467 poly_it = next_it;
468 }
469 else
470 ++poly_it;
471 }
472 }
473
~DocumentElement()474 DocumentElement::~DocumentElement()
475 {
476 }
477
visitedBy(ElementTreeVisitor & rVisitor,const std::list<Element * >::const_iterator & rParentIt)478 void DocumentElement::visitedBy( ElementTreeVisitor& rVisitor,
479 const std::list< Element* >::const_iterator& rParentIt)
480 {
481 rVisitor.visit(*this, rParentIt);
482 }
483
484
485 }
486