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