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