1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: genericelements.cxx,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_sdext.hxx"
35 #include "xmlemitter.hxx"
36 #include "genericelements.hxx"
37 #include "pdfiprocessor.hxx"
38 #include "pdfihelper.hxx"
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <basegfx/range/b2drange.hxx>
48 ElementFactory::~ElementFactory()
54 while( !Children
.empty() )
56 Element
* pCurr( Children
.front() );
62 void Element::applyToChildren( ElementTreeVisitor
& rVisitor
)
64 for( std::list
< Element
* >::iterator it
= Children
.begin(); it
!= Children
.end(); ++it
)
65 (*it
)->visitedBy( rVisitor
, it
);
68 void Element::setParent( std::list
<Element
*>::iterator
& el
, Element
* pNewParent
)
72 pNewParent
->Children
.splice( pNewParent
->Children
.end(), (*el
)->Parent
->Children
, el
);
73 (*el
)->Parent
= pNewParent
;
77 void Element::updateGeometryWith( const Element
* pMergeFrom
)
79 if( w
== 0 && h
== 0 )
88 if( pMergeFrom
->x
< x
)
90 w
+= x
- pMergeFrom
->x
;
93 if( pMergeFrom
->x
+pMergeFrom
->w
> x
+w
)
94 w
= pMergeFrom
->w
+pMergeFrom
->x
- x
;
95 if( pMergeFrom
->y
< y
)
97 h
+= y
- pMergeFrom
->y
;
100 if( pMergeFrom
->y
+pMergeFrom
->h
> y
+h
)
101 h
= pMergeFrom
->h
+pMergeFrom
->y
- y
;
106 #if OSL_DEBUG_LEVEL > 1
108 void Element::emitStructure( int nLevel
)
110 OSL_TRACE( "%*s<%s %p> (%.1f,%.1f)+(%.1fx%.1f)\n",
111 nLevel
, "", typeid( *this ).name(), this,
113 for( std::list
< Element
* >::iterator it
= Children
.begin(); it
!= Children
.end(); ++it
)
114 (*it
)->emitStructure(nLevel
+1 );
115 OSL_TRACE( "%*s</%s>\n", nLevel
, "", typeid( *this ).name() );
119 void ListElement::visitedBy( ElementTreeVisitor
& visitor
, const std::list
< Element
* >::const_iterator
& )
121 // this is only an inner node
122 applyToChildren(visitor
);
125 void HyperlinkElement::visitedBy( ElementTreeVisitor
& rVisitor
,
126 const std::list
< Element
* >::const_iterator
& rParentIt
)
128 rVisitor
.visit(*this,rParentIt
);
131 void TextElement::visitedBy( ElementTreeVisitor
& rVisitor
,
132 const std::list
< Element
* >::const_iterator
& rParentIt
)
134 rVisitor
.visit(*this,rParentIt
);
137 void FrameElement::visitedBy( ElementTreeVisitor
& rVisitor
,
138 const std::list
< Element
* >::const_iterator
& rParentIt
)
140 rVisitor
.visit(*this,rParentIt
);
143 void ImageElement::visitedBy( ElementTreeVisitor
& rVisitor
,
144 const std::list
< Element
* >::const_iterator
& rParentIt
)
146 rVisitor
.visit( *this, rParentIt
);
149 PolyPolyElement::PolyPolyElement( Element
* pParent
,
151 const basegfx::B2DPolyPolygon
& rPolyPoly
,
153 : DrawElement( pParent
, nGCId
),
154 PolyPoly( rPolyPoly
),
159 void PolyPolyElement::updateGeometry()
161 basegfx::B2DRange aRange
;
162 if( PolyPoly
.areControlPointsUsed() )
163 aRange
= basegfx::tools::getRange( basegfx::tools::adaptiveSubdivideByAngle( PolyPoly
) );
165 aRange
= basegfx::tools::getRange( PolyPoly
);
166 x
= aRange
.getMinX();
167 y
= aRange
.getMinY();
168 w
= aRange
.getWidth();
169 h
= aRange
.getHeight();
172 void PolyPolyElement::visitedBy( ElementTreeVisitor
& rVisitor
,
173 const std::list
< Element
* >::const_iterator
& rParentIt
)
175 rVisitor
.visit( *this, rParentIt
);
178 #if OSL_DEBUG_LEVEL > 1
179 void PolyPolyElement::emitStructure( int nLevel
)
181 OSL_TRACE( "%*s<%s %p>\n", nLevel
, "", typeid( *this ).name(), this );
182 OSL_TRACE( "path=" );
183 int nPoly
= PolyPoly
.count();
184 for( int i
= 0; i
< nPoly
; i
++ )
186 basegfx::B2DPolygon aPoly
= PolyPoly
.getB2DPolygon( i
);
187 int nPoints
= aPoly
.count();
188 for( int n
= 0; n
< nPoints
; n
++ )
190 basegfx::B2DPoint aPoint
= aPoly
.getB2DPoint( n
);
191 OSL_TRACE( " (%g,%g)", aPoint
.getX(), aPoint
.getY() );
195 for( std::list
< Element
* >::iterator it
= Children
.begin(); it
!= Children
.end(); ++it
)
196 (*it
)->emitStructure( nLevel
+1 );
197 OSL_TRACE( "%*s</%s>\n", nLevel
, "", typeid( *this ).name() );
201 void ParagraphElement::visitedBy( ElementTreeVisitor
& rVisitor
,
202 const std::list
< Element
* >::const_iterator
& rParentIt
)
204 rVisitor
.visit(*this,rParentIt
);
207 bool ParagraphElement::isSingleLined( PDFIProcessor
& rProc
) const
209 std::list
< Element
* >::const_iterator it
= Children
.begin();
210 TextElement
* pText
= NULL
, *pLastText
= NULL
;
211 while( it
!= Children
.end() )
213 // a paragraph containing subparagraphs cannot be single lined
214 if( dynamic_cast< ParagraphElement
* >(*it
) != NULL
)
217 pText
= dynamic_cast< TextElement
* >(*it
);
220 const FontAttributes
& rFont
= rProc
.getFont( pText
->FontId
);
221 if( pText
->h
> rFont
.size
*1.5 )
225 if( pText
->y
> pLastText
->y
+pLastText
->h
||
226 pLastText
->y
> pText
->y
+pText
->h
)
235 // a paragraph without a single text is not considered single lined
236 return pLastText
!= NULL
;
239 double ParagraphElement::getLineHeight( PDFIProcessor
& rProc
) const
242 for( std::list
< Element
* >::const_iterator it
= Children
.begin(); it
!= Children
.end(); ++it
)
244 ParagraphElement
* pPara
= dynamic_cast< ParagraphElement
* >(*it
);
245 TextElement
* pText
= NULL
;
248 double lh
= pPara
->getLineHeight( rProc
);
252 else if( (pText
= dynamic_cast< TextElement
* >( *it
)) != NULL
)
254 const FontAttributes
& rFont
= rProc
.getFont( pText
->FontId
);
255 double lh
= pText
->h
;
256 if( pText
->h
> rFont
.size
*1.5 )
265 TextElement
* ParagraphElement::getFirstTextChild() const
267 TextElement
* pText
= NULL
;
268 for( std::list
< Element
* >::const_iterator it
= Children
.begin();
269 it
!= Children
.end() && ! pText
; ++it
)
271 pText
= dynamic_cast<TextElement
*>(*it
);
276 PageElement::~PageElement()
279 delete HeaderElement
;
281 delete FooterElement
;
284 void PageElement::visitedBy( ElementTreeVisitor
& rVisitor
,
285 const std::list
< Element
* >::const_iterator
& rParentIt
)
287 rVisitor
.visit(*this, rParentIt
);
290 void PageElement::updateParagraphGeometry( Element
* pEle
)
292 // update geometry of children
293 for( std::list
< Element
* >::iterator it
= pEle
->Children
.begin();
294 it
!= pEle
->Children
.end(); ++it
)
296 updateParagraphGeometry( *it
);
298 // if this is a paragraph itself, then update according to children geometry
299 if( dynamic_cast<ParagraphElement
*>(pEle
) )
301 for( std::list
< Element
* >::iterator it
= pEle
->Children
.begin();
302 it
!= pEle
->Children
.end(); ++it
)
304 Element
* pChild
= NULL
;
305 TextElement
* pText
= dynamic_cast<TextElement
*>(*it
);
310 ParagraphElement
* pPara
= dynamic_cast<ParagraphElement
*>(*it
);
315 pEle
->updateGeometryWith( pChild
);
320 bool PageElement::resolveHyperlink( std::list
<Element
*>::iterator link_it
, std::list
<Element
*>& rElements
)
322 HyperlinkElement
* pLink
= dynamic_cast<HyperlinkElement
*>(*link_it
);
323 if( ! pLink
) // sanity check
326 for( std::list
<Element
*>::iterator it
= rElements
.begin(); it
!= rElements
.end(); ++it
)
328 if( (*it
)->x
>= pLink
->x
&& (*it
)->x
+ (*it
)->w
<= pLink
->x
+ pLink
->w
&&
329 (*it
)->y
>= pLink
->y
&& (*it
)->y
+ (*it
)->h
<= pLink
->y
+ pLink
->h
)
331 TextElement
* pText
= dynamic_cast<TextElement
*>(*it
);
334 if( pLink
->Children
.empty() )
336 // insert the hyperlink before the frame
337 rElements
.splice( it
, Hyperlinks
.Children
, link_it
);
338 pLink
->Parent
= (*it
)->Parent
;
340 // move text element into hyperlink
341 std::list
<Element
*>::iterator next
= it
;
343 Element::setParent( it
, pLink
);
348 // a link can contain multiple text elements or a single frame
349 if( ! pLink
->Children
.empty() )
351 if( dynamic_cast<ParagraphElement
*>(*it
) )
353 if( resolveHyperlink( link_it
, (*it
)->Children
) )
357 FrameElement
* pFrame
= dynamic_cast<FrameElement
*>(*it
);
360 // insert the hyperlink before the frame
361 rElements
.splice( it
, Hyperlinks
.Children
, link_it
);
362 pLink
->Parent
= (*it
)->Parent
;
363 // move frame into hyperlink
364 Element::setParent( it
, pLink
);
369 return ! pLink
->Children
.empty();
372 void PageElement::resolveHyperlinks()
374 while( ! Hyperlinks
.Children
.empty() )
376 if( ! resolveHyperlink( Hyperlinks
.Children
.begin(), Children
) )
378 delete Hyperlinks
.Children
.front();
379 Hyperlinks
.Children
.pop_front();
384 void PageElement::resolveFontStyles( PDFIProcessor
& rProc
)
386 resolveUnderlines(rProc
);
389 void PageElement::resolveUnderlines( PDFIProcessor
& rProc
)
391 // FIXME: currently the algorithm used is quadratic
392 // this could be solved by some sorting beforehand
394 std::list
< Element
* >::iterator poly_it
= Children
.begin();
395 while( poly_it
!= Children
.end() )
397 PolyPolyElement
* pPoly
= dynamic_cast< PolyPolyElement
* >(*poly_it
);
398 if( ! pPoly
|| ! pPoly
->Children
.empty() )
403 /* check for: no filling
404 * only two points (FIXME: handle small rectangles, too)
405 * y coordinates of points are equal
407 if( pPoly
->Action
!= PATH_STROKE
)
412 if( pPoly
->PolyPoly
.count() != 1 )
418 bool bRemovePoly
= false;
419 basegfx::B2DPolygon aPoly
= pPoly
->PolyPoly
.getB2DPolygon(0);
420 if( aPoly
.count() != 2 ||
421 aPoly
.getB2DPoint(0).getY() != aPoly
.getB2DPoint(1).getY() )
426 double l_x
= aPoly
.getB2DPoint(0).getX();
427 double r_x
= aPoly
.getB2DPoint(1).getX();
431 u_y
= r_x
; r_x
= l_x
; l_x
= u_y
;
433 u_y
= aPoly
.getB2DPoint(0).getY();
434 for( std::list
< Element
*>::iterator it
= Children
.begin();
435 it
!= Children
.end(); ++it
)
438 if( pEle
->y
<= u_y
&& pEle
->y
+ pEle
->h
*1.1 >= u_y
)
440 // first: is the element underlined completely ?
441 if( pEle
->x
+ pEle
->w
*0.1 >= l_x
&&
442 pEle
->x
+ pEle
->w
*0.9 <= r_x
)
444 TextElement
* pText
= dynamic_cast< TextElement
* >(pEle
);
447 const GraphicsContext
& rTextGC
= rProc
.getGraphicsContext( pText
->GCId
);
448 if( ! rTextGC
.isRotatedOrSkewed() )
451 // retrieve ID for modified font
452 FontAttributes aAttr
= rProc
.getFont( pText
->FontId
);
453 aAttr
.isUnderline
= true;
454 pText
->FontId
= rProc
.getFontId( aAttr
);
457 else if( dynamic_cast< HyperlinkElement
* >(pEle
) )
460 // second: hyperlinks may be larger than their underline
461 // since they are just arbitrary rectangles in the action definition
462 else if( dynamic_cast< HyperlinkElement
* >(pEle
) != NULL
&&
463 l_x
>= pEle
->x
&& r_x
<= pEle
->x
+pEle
->w
)
471 std::list
< Element
* >::iterator next_it
= poly_it
;
473 Children
.erase( poly_it
);
482 DocumentElement::~DocumentElement()
486 void DocumentElement::visitedBy( ElementTreeVisitor
& rVisitor
,
487 const std::list
< Element
* >::const_iterator
& rParentIt
)
489 rVisitor
.visit(*this, rParentIt
);