LCOV - code coverage report
Current view: top level - sdext/source/pdfimport/tree - genericelements.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 95 234 40.6 %
Date: 2015-06-13 12:38:46 Functions: 22 30 73.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11