LCOV - code coverage report
Current view: top level - sdext/source/pdfimport/tree - writertreevisiting.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 428 673 63.6 %
Date: 2014-11-03 Functions: 25 28 89.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 "pdfiprocessor.hxx"
      22             : #include "xmlemitter.hxx"
      23             : #include "pdfihelper.hxx"
      24             : #include "imagecontainer.hxx"
      25             : #include "style.hxx"
      26             : #include "writertreevisiting.hxx"
      27             : #include "genericelements.hxx"
      28             : 
      29             : #include "basegfx/polygon/b2dpolypolygontools.hxx"
      30             : #include "basegfx/range/b2drange.hxx"
      31             : 
      32             : using namespace ::com::sun::star;
      33             : 
      34             : namespace pdfi
      35             : {
      36             : 
      37           0 : void WriterXmlEmitter::visit( HyperlinkElement& elem, const std::list< Element* >::const_iterator&   )
      38             : {
      39           0 :     if( elem.Children.empty() )
      40           0 :         return;
      41             : 
      42           0 :     const char* pType = dynamic_cast<DrawElement*>(elem.Children.front()) ? "draw:a" : "text:a";
      43             : 
      44           0 :     PropertyMap aProps;
      45           0 :     aProps[ "xlink:type" ] = "simple";
      46           0 :     aProps[ "xlink:href" ] = elem.URI;
      47           0 :     aProps[ "office:target-frame-name" ] = "_blank";
      48           0 :     aProps[ "xlink:show" ] = "new";
      49             : 
      50           0 :     m_rEmitContext.rEmitter.beginTag( pType, aProps );
      51           0 :     std::list< Element* >::iterator this_it =  elem.Children.begin();
      52           0 :     while( this_it !=elem.Children.end() && *this_it != &elem )
      53             :     {
      54           0 :         (*this_it)->visitedBy( *this, this_it );
      55           0 :         ++this_it;
      56             :     }
      57           0 :     m_rEmitContext.rEmitter.endTag( pType );
      58             : }
      59             : 
      60         416 : void WriterXmlEmitter::visit( TextElement& elem, const std::list< Element* >::const_iterator&   )
      61             : {
      62         416 :     if( elem.Text.isEmpty() )
      63         624 :         return;
      64             : 
      65         208 :     PropertyMap aProps;
      66         208 :     if( elem.StyleId != -1 )
      67             :     {
      68         416 :         aProps[ OUString( "text:style-name" ) ] =
      69         208 :             m_rEmitContext.rStyles.getStyleName( elem.StyleId );
      70             :     }
      71             : 
      72         208 :     m_rEmitContext.rEmitter.beginTag( "text:span", aProps );
      73         208 :     m_rEmitContext.rEmitter.write( elem.Text.makeStringAndClear() );
      74         208 :     std::list< Element* >::iterator this_it =  elem.Children.begin();
      75         416 :     while( this_it !=elem.Children.end() && *this_it != &elem )
      76             :     {
      77           0 :         (*this_it)->visitedBy( *this, this_it );
      78           0 :         ++this_it;
      79             :     }
      80             : 
      81         208 :     m_rEmitContext.rEmitter.endTag( "text:span" );
      82             : }
      83             : 
      84          46 : void WriterXmlEmitter::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator&   )
      85             : {
      86          46 :     PropertyMap aProps;
      87          46 :     if( elem.StyleId != -1 )
      88             :     {
      89           2 :         aProps[ "text:style-name" ] = m_rEmitContext.rStyles.getStyleName( elem.StyleId );
      90             :     }
      91          46 :     const char* pTagType = "text:p";
      92          46 :     if( elem.Type == elem.Headline )
      93           0 :         pTagType = "text:h";
      94          46 :     m_rEmitContext.rEmitter.beginTag( pTagType, aProps );
      95             : 
      96          46 :     std::list< Element* >::iterator this_it =  elem.Children.begin();
      97         508 :     while( this_it !=elem.Children.end() && *this_it != &elem )
      98             :     {
      99         416 :         (*this_it)->visitedBy( *this, this_it );
     100         416 :         ++this_it;
     101             :     }
     102             : 
     103          46 :     m_rEmitContext.rEmitter.endTag( pTagType );
     104          46 : }
     105             : 
     106          60 : void WriterXmlEmitter::fillFrameProps( DrawElement&       rElem,
     107             :                                        PropertyMap&       rProps,
     108             :                                        const EmitContext& rEmitContext )
     109             : {
     110          60 :     double rel_x = rElem.x, rel_y = rElem.y;
     111             : 
     112             :     // find anchor type by recursing though parents
     113          60 :     Element* pAnchor = rElem.Parent;
     114         180 :     while( pAnchor &&
     115         180 :            ! dynamic_cast<ParagraphElement*>(pAnchor) &&
     116         120 :            ! dynamic_cast<PageElement*>(pAnchor) )
     117             :     {
     118           0 :         pAnchor = pAnchor->Parent;
     119             :     }
     120          60 :     if( pAnchor )
     121             :     {
     122          60 :         if( dynamic_cast<ParagraphElement*>(pAnchor) )
     123             :         {
     124           0 :             rProps[ "text:anchor-type" ] = rElem.isCharacter
     125           0 :                 ? OUString("character") : OUString("paragraph");
     126             :         }
     127             :         else
     128             :         {
     129          60 :             PageElement* pPage = dynamic_cast<PageElement*>(pAnchor);
     130          60 :             rProps[ "text:anchor-type" ] = "page";
     131          60 :             rProps[ "text:anchor-page-number" ] = OUString::number(pPage->PageNumber);
     132             :         }
     133          60 :         rel_x -= pAnchor->x;
     134          60 :         rel_y -= pAnchor->y;
     135             :     }
     136             : 
     137          60 :     rProps[ "draw:z-index" ] = OUString::number( rElem.ZOrder );
     138          60 :     rProps[ "draw:style-name"] = rEmitContext.rStyles.getStyleName( rElem.StyleId );
     139          60 :     rProps[ "svg:width" ]   = convertPixelToUnitString( rElem.w );
     140          60 :     rProps[ "svg:height" ]  = convertPixelToUnitString( rElem.h );
     141             : 
     142             :     const GraphicsContext& rGC =
     143          60 :         rEmitContext.rProcessor.getGraphicsContext( rElem.GCId );
     144          60 :     if( rGC.Transformation.isIdentity() )
     145             :     {
     146           0 :         if( !rElem.isCharacter )
     147             :         {
     148           0 :             rProps[ "svg:x" ]       = convertPixelToUnitString( rel_x );
     149           0 :             rProps[ "svg:y" ]       = convertPixelToUnitString( rel_y );
     150             :         }
     151             :     }
     152             :     else
     153             :     {
     154         120 :         basegfx::B2DTuple aScale, aTranslation;
     155             :         double fRotate, fShearX;
     156             : 
     157          60 :         rGC.Transformation.decompose( aScale, aTranslation, fRotate, fShearX );
     158             : 
     159         120 :         OUStringBuffer aBuf( 256 );
     160             : 
     161             :         // TODO(F2): general transformation case missing; if implemented, note
     162             :         // that ODF rotation is oriented the other way
     163             : 
     164             :         // build transformation string
     165          60 :         if( fShearX != 0.0 )
     166             :         {
     167           0 :             aBuf.appendAscii( "skewX( " );
     168           0 :             aBuf.append( fShearX );
     169           0 :             aBuf.appendAscii( " )" );
     170             :         }
     171          60 :         if( fRotate != 0.0 )
     172             :         {
     173           0 :             if( !aBuf.isEmpty() )
     174           0 :                 aBuf.append( ' ' );
     175           0 :             aBuf.appendAscii( "rotate( " );
     176           0 :             aBuf.append( -fRotate );
     177           0 :             aBuf.appendAscii( " )" );
     178             : 
     179             :         }
     180          60 :         if( ! rElem.isCharacter )
     181             :         {
     182          60 :             if( !aBuf.isEmpty() )
     183           0 :                 aBuf.append( ' ' );
     184          60 :             aBuf.appendAscii( "translate( " );
     185          60 :             aBuf.append( convertPixelToUnitString( rel_x ) );
     186          60 :             aBuf.append( ' ' );
     187          60 :             aBuf.append( convertPixelToUnitString( rel_y ) );
     188          60 :             aBuf.appendAscii( " )" );
     189             :          }
     190             : 
     191         120 :         rProps[ "draw:transform" ] = aBuf.makeStringAndClear();
     192             :     }
     193          60 : }
     194             : 
     195          48 : void WriterXmlEmitter::visit( FrameElement& elem, const std::list< Element* >::const_iterator&   )
     196             : {
     197          48 :     if( elem.Children.empty() )
     198          48 :         return;
     199             : 
     200          48 :     bool bTextBox = (dynamic_cast<ParagraphElement*>(elem.Children.front()) != NULL);
     201          48 :     PropertyMap aFrameProps;
     202          48 :     fillFrameProps( elem, aFrameProps, m_rEmitContext );
     203          48 :     m_rEmitContext.rEmitter.beginTag( "draw:frame", aFrameProps );
     204          48 :     if( bTextBox )
     205          44 :         m_rEmitContext.rEmitter.beginTag( "draw:text-box", PropertyMap() );
     206             : 
     207          48 :     std::list< Element* >::iterator this_it =  elem.Children.begin();
     208         144 :     while( this_it !=elem.Children.end() && *this_it != &elem )
     209             :     {
     210          48 :         (*this_it)->visitedBy( *this, this_it );
     211          48 :         ++this_it;
     212             :     }
     213             : 
     214          48 :     if( bTextBox )
     215          44 :         m_rEmitContext.rEmitter.endTag( "draw:text-box" );
     216          48 :     m_rEmitContext.rEmitter.endTag( "draw:frame" );
     217             : }
     218             : 
     219          12 : void WriterXmlEmitter::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
     220             : {
     221          12 :     elem.updateGeometry();
     222             :     /* note:
     223             :      *   aw recommends using 100dth of mm in all respects since the xml import
     224             :      *   (a) is buggy (see issue 37213)
     225             :      *   (b) is optimized for 100dth of mm and does not scale itself then,
     226             :      *       this does not gain us speed but makes for smaller rounding errors since
     227             :      *       the xml importer coordinates are integer based
     228             :      */
     229          24 :     for (sal_uInt32 i = 0; i< elem.PolyPoly.count(); i++)
     230             :     {
     231          12 :         basegfx::B2DPolygon b2dPolygon;
     232          12 :         b2dPolygon =  elem.PolyPoly.getB2DPolygon( i );
     233             : 
     234          48 :         for ( sal_uInt32 j = 0; j< b2dPolygon.count(); j++ )
     235             :         {
     236          36 :             basegfx::B2DPoint point;
     237          72 :             basegfx::B2DPoint nextPoint;
     238          36 :             point = b2dPolygon.getB2DPoint( j );
     239             : 
     240          72 :             basegfx::B2DPoint prevPoint;
     241          36 :             prevPoint = b2dPolygon.getPrevControlPoint( j ) ;
     242             : 
     243          36 :             point.setX( convPx2mmPrec2( point.getX() )*100.0 );
     244          36 :             point.setY( convPx2mmPrec2( point.getY() )*100.0 );
     245             : 
     246          36 :             if ( b2dPolygon.isPrevControlPointUsed( j ) )
     247             :             {
     248          16 :                 prevPoint.setX( convPx2mmPrec2( prevPoint.getX() )*100.0 );
     249          16 :                 prevPoint.setY( convPx2mmPrec2( prevPoint.getY() )*100.0 );
     250             :             }
     251             : 
     252          36 :             if ( b2dPolygon.isNextControlPointUsed( j ) )
     253             :             {
     254          16 :                 nextPoint = b2dPolygon.getNextControlPoint( j ) ;
     255          16 :                 nextPoint.setX( convPx2mmPrec2( nextPoint.getX() )*100.0 );
     256          16 :                 nextPoint.setY( convPx2mmPrec2( nextPoint.getY() )*100.0 );
     257             :             }
     258             : 
     259          36 :             b2dPolygon.setB2DPoint( j, point );
     260             : 
     261          36 :             if ( b2dPolygon.isPrevControlPointUsed( j ) )
     262          16 :                 b2dPolygon.setPrevControlPoint( j , prevPoint ) ;
     263             : 
     264          36 :             if ( b2dPolygon.isNextControlPointUsed( j ) )
     265          16 :                 b2dPolygon.setNextControlPoint( j , nextPoint ) ;
     266          36 :         }
     267             : 
     268          12 :         elem.PolyPoly.setB2DPolygon( i, b2dPolygon );
     269          12 :     }
     270             : 
     271          12 :     PropertyMap aProps;
     272          12 :     fillFrameProps( elem, aProps, m_rEmitContext );
     273          24 :     OUStringBuffer aBuf( 64 );
     274          12 :     aBuf.appendAscii( "0 0 " );
     275          12 :     aBuf.append( convPx2mmPrec2(elem.w)*100.0 );
     276          12 :     aBuf.append( ' ' );
     277          12 :     aBuf.append( convPx2mmPrec2(elem.h)*100.0 );
     278          12 :     aProps[ "svg:viewBox" ] = aBuf.makeStringAndClear();
     279          12 :     aProps[ "svg:d" ]       = basegfx::tools::exportToSvgD( elem.PolyPoly, true, true, false );
     280             : 
     281          12 :     m_rEmitContext.rEmitter.beginTag( "draw:path", aProps );
     282          24 :     m_rEmitContext.rEmitter.endTag( "draw:path" );
     283          12 : }
     284             : 
     285           4 : void WriterXmlEmitter::visit( ImageElement& elem, const std::list< Element* >::const_iterator& )
     286             : {
     287           4 :     PropertyMap aImageProps;
     288           4 :     m_rEmitContext.rEmitter.beginTag( "draw:image", aImageProps );
     289           4 :     m_rEmitContext.rEmitter.beginTag( "office:binary-data", PropertyMap() );
     290           4 :     m_rEmitContext.rImages.writeBase64EncodedStream( elem.Image, m_rEmitContext);
     291           4 :     m_rEmitContext.rEmitter.endTag( "office:binary-data" );
     292           4 :     m_rEmitContext.rEmitter.endTag( "draw:image" );
     293           4 : }
     294             : 
     295           2 : void WriterXmlEmitter::visit( PageElement& elem, const std::list< Element* >::const_iterator&   )
     296             : {
     297           2 :     if( m_rEmitContext.xStatusIndicator.is() )
     298           0 :         m_rEmitContext.xStatusIndicator->setValue( elem.PageNumber );
     299             : 
     300           2 :     std::list< Element* >::iterator this_it =  elem.Children.begin();
     301          36 :     while( this_it !=elem.Children.end() && *this_it != &elem )
     302             :     {
     303          32 :         (*this_it)->visitedBy( *this, this_it );
     304          32 :         ++this_it;
     305             :     }
     306           2 : }
     307             : 
     308           2 : void WriterXmlEmitter::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&)
     309             : {
     310           2 :     m_rEmitContext.rEmitter.beginTag( "office:body", PropertyMap() );
     311           2 :     m_rEmitContext.rEmitter.beginTag( "office:text", PropertyMap() );
     312             : 
     313           4 :     for( std::list< Element* >::iterator it = elem.Children.begin(); it != elem.Children.end(); ++it )
     314             :     {
     315           2 :         PageElement* pPage = dynamic_cast<PageElement*>(*it);
     316           2 :         if( pPage )
     317             :         {
     318             :             // emit only page anchored objects
     319             :             // currently these are only DrawElement types
     320          34 :             for( std::list< Element* >::iterator child_it = pPage->Children.begin(); child_it != pPage->Children.end(); ++child_it )
     321             :             {
     322          32 :                 if( dynamic_cast<DrawElement*>(*child_it) != NULL )
     323          30 :                     (*child_it)->visitedBy( *this, child_it );
     324             :             }
     325             :         }
     326             :     }
     327             : 
     328             :     // do not emit page anchored objects, they are emitted before
     329             :     // (must precede all pages in writer document) currently these are
     330             :     // only DrawElement types
     331           4 :     for( std::list< Element* >::iterator it = elem.Children.begin(); it != elem.Children.end(); ++it )
     332             :     {
     333           2 :         if( dynamic_cast<DrawElement*>(*it) == NULL )
     334           2 :             (*it)->visitedBy( *this, it );
     335             :     }
     336             : 
     337           2 :     m_rEmitContext.rEmitter.endTag( "office:text" );
     338           2 :     m_rEmitContext.rEmitter.endTag( "office:body" );
     339           2 : }
     340             : 
     341             : 
     342             : 
     343           0 : void WriterXmlOptimizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& )
     344             : {
     345           0 : }
     346             : 
     347         208 : void WriterXmlOptimizer::visit( TextElement&, const std::list< Element* >::const_iterator&)
     348             : {
     349         208 : }
     350             : 
     351          24 : void WriterXmlOptimizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator& )
     352             : {
     353          24 :     elem.applyToChildren(*this);
     354          24 : }
     355             : 
     356           2 : void WriterXmlOptimizer::visit( ImageElement&, const std::list< Element* >::const_iterator& )
     357             : {
     358           2 : }
     359             : 
     360           6 : void WriterXmlOptimizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
     361             : {
     362             :     /* note: optimize two consecutive PolyPolyElements that
     363             :      *  have the same path but one of which is a stroke while
     364             :      *     the other is a fill
     365             :      */
     366           6 :     if( elem.Parent )
     367             :     {
     368             :         // find following PolyPolyElement in parent's children list
     369           6 :         std::list< Element* >::iterator this_it = elem.Parent->Children.begin();
     370          44 :         while( this_it != elem.Parent->Children.end() && *this_it != &elem )
     371          32 :             ++this_it;
     372             : 
     373           6 :         if( this_it != elem.Parent->Children.end() )
     374             :         {
     375           6 :             std::list< Element* >::iterator next_it = this_it;
     376           6 :             if( ++next_it != elem.Parent->Children.end() )
     377             :             {
     378           6 :                 PolyPolyElement* pNext = dynamic_cast<PolyPolyElement*>(*next_it);
     379           6 :                 if( pNext && pNext->PolyPoly == elem.PolyPoly )
     380             :                 {
     381             :                     const GraphicsContext& rNextGC =
     382           0 :                         m_rProcessor.getGraphicsContext( pNext->GCId );
     383             :                     const GraphicsContext& rThisGC =
     384           0 :                         m_rProcessor.getGraphicsContext( elem.GCId );
     385             : 
     386           0 :                     if( rThisGC.BlendMode      == rNextGC.BlendMode &&
     387           0 :                         rThisGC.Flatness       == rNextGC.Flatness &&
     388           0 :                         rThisGC.Transformation == rNextGC.Transformation &&
     389           0 :                         rThisGC.Clip           == rNextGC.Clip &&
     390           0 :                         pNext->Action          == PATH_STROKE &&
     391           0 :                         (elem.Action == PATH_FILL || elem.Action == PATH_EOFILL) )
     392             :                     {
     393           0 :                         GraphicsContext aGC = rThisGC;
     394           0 :                         aGC.LineJoin  = rNextGC.LineJoin;
     395           0 :                         aGC.LineCap   = rNextGC.LineCap;
     396           0 :                         aGC.LineWidth = rNextGC.LineWidth;
     397           0 :                         aGC.MiterLimit= rNextGC.MiterLimit;
     398           0 :                         aGC.DashArray = rNextGC.DashArray;
     399           0 :                         aGC.LineColor = rNextGC.LineColor;
     400           0 :                         elem.GCId = m_rProcessor.getGCId( aGC );
     401             : 
     402           0 :                         elem.Action |= pNext->Action;
     403             : 
     404           0 :                         elem.Children.splice( elem.Children.end(), pNext->Children );
     405           0 :                         elem.Parent->Children.erase( next_it );
     406           0 :                         delete pNext;
     407             :                     }
     408             :                 }
     409             :             }
     410             :         }
     411             :     }
     412           6 : }
     413             : 
     414          22 : void WriterXmlOptimizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& rParentIt)
     415             : {
     416          22 :     optimizeTextElements( elem );
     417             : 
     418          22 :     elem.applyToChildren(*this);
     419             : 
     420          22 :     if( elem.Parent && rParentIt != elem.Parent->Children.end() )
     421             :     {
     422             :         // find if there is a previous paragraph that might be a heading for this one
     423          22 :         std::list<Element*>::const_iterator prev = rParentIt;
     424          22 :         ParagraphElement* pPrevPara = NULL;
     425          44 :         while( prev != elem.Parent->Children.begin() )
     426             :         {
     427           0 :             --prev;
     428           0 :             pPrevPara = dynamic_cast< ParagraphElement* >(*prev);
     429           0 :             if( pPrevPara )
     430             :             {
     431             :                 /* What constitutes a heading ? current hints are:
     432             :                  * - one line only
     433             :                  * - not too far away from this paragraph (two heading height max ?)
     434             :                  * - font larger or bold
     435             :                  * this is of course incomplete
     436             :                  * FIXME: improve hints for heading
     437             :                  */
     438             :                 // check for single line
     439           0 :                 if( pPrevPara->isSingleLined( m_rProcessor ) )
     440             :                 {
     441           0 :                     double head_line_height = pPrevPara->getLineHeight( m_rProcessor );
     442           0 :                     if( pPrevPara->y + pPrevPara->h + 2*head_line_height > elem.y )
     443             :                     {
     444             :                         // check for larger font
     445           0 :                         if( head_line_height > elem.getLineHeight( m_rProcessor ) )
     446             :                         {
     447           0 :                             pPrevPara->Type = elem.Headline;
     448             :                         }
     449             :                         else
     450             :                         {
     451             :                             // check whether text of pPrevPara is bold (at least first text element)
     452             :                             // and this para is not bold (dito)
     453           0 :                             TextElement* pPrevText = pPrevPara->getFirstTextChild();
     454           0 :                             TextElement* pThisText = elem.getFirstTextChild();
     455           0 :                             if( pPrevText && pThisText )
     456             :                             {
     457           0 :                                 const FontAttributes& rPrevFont = m_rProcessor.getFont( pPrevText->FontId );
     458           0 :                                 const FontAttributes& rThisFont = m_rProcessor.getFont( pThisText->FontId );
     459           0 :                                 if( rPrevFont.isBold && ! rThisFont.isBold )
     460           0 :                                     pPrevPara->Type = elem.Headline;
     461             :                             }
     462             :                         }
     463             :                     }
     464             :                 }
     465           0 :                 break;
     466             :             }
     467             :         }
     468             :     }
     469          22 : }
     470             : 
     471           2 : void WriterXmlOptimizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
     472             : {
     473           2 :     if( m_rProcessor.getStatusIndicator().is() )
     474           0 :         m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber );
     475             : 
     476             :     // resolve hyperlinks
     477           2 :     elem.resolveHyperlinks();
     478             : 
     479           2 :     elem.resolveFontStyles( m_rProcessor ); // underlines and such
     480             : 
     481             :     // FIXME: until hyperlinks and font effects are adjusted for
     482             :     // geometrical search handle them before sorting
     483           2 :     m_rProcessor.sortElements( &elem );
     484             : 
     485             :     // find paragraphs in text
     486           2 :     ParagraphElement* pCurPara = NULL;
     487           2 :     std::list< Element* >::iterator page_element, next_page_element;
     488           2 :     next_page_element = elem.Children.begin();
     489           2 :     double fCurLineHeight = 0.0; // average height of text items in current para
     490           2 :     int nCurLineElements = 0; // number of line contributing elements in current para
     491           2 :     double line_left = elem.w, line_right = 0.0;
     492           2 :     double column_width = elem.w*0.75; // estimate text width
     493             :     // TODO: guess columns
     494          34 :     while( next_page_element != elem.Children.end() )
     495             :     {
     496          30 :         page_element = next_page_element++;
     497          30 :         ParagraphElement* pPagePara = dynamic_cast<ParagraphElement*>(*page_element);
     498          30 :         if( pPagePara )
     499             :         {
     500           0 :             pCurPara = pPagePara;
     501             :             // adjust line height and text items
     502           0 :             fCurLineHeight = 0.0;
     503           0 :             nCurLineElements = 0;
     504           0 :             for( std::list< Element* >::iterator it = pCurPara->Children.begin();
     505           0 :                  it != pCurPara->Children.end(); ++it )
     506             :             {
     507           0 :                 TextElement* pTestText = dynamic_cast<TextElement*>(*it);
     508           0 :                 if( pTestText )
     509             :                 {
     510           0 :                     fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pTestText->h)/double(nCurLineElements+1);
     511           0 :                     nCurLineElements++;
     512             :                 }
     513             :             }
     514           0 :             continue;
     515             :         }
     516             : 
     517          30 :         HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(*page_element);
     518          30 :         DrawElement* pDraw = dynamic_cast<DrawElement*>(*page_element);
     519          30 :         if( ! pDraw && pLink && ! pLink->Children.empty() )
     520           0 :             pDraw = dynamic_cast<DrawElement*>(pLink->Children.front() );
     521          30 :         if( pDraw )
     522             :         {
     523             :             // insert small drawing objects as character, else leave them page bound
     524             : 
     525          30 :             bool bInsertToParagraph = false;
     526             :             // first check if this is either inside the paragraph
     527          30 :             if( pCurPara && pDraw->y < pCurPara->y + pCurPara->h )
     528             :             {
     529           0 :                 if( pDraw->h < fCurLineHeight * 1.5 )
     530             :                 {
     531           0 :                     bInsertToParagraph = true;
     532           0 :                     fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pDraw->h)/double(nCurLineElements+1);
     533           0 :                     nCurLineElements++;
     534             :                     // mark draw element as character
     535           0 :                     pDraw->isCharacter = true;
     536             :                 }
     537             :             }
     538             :             // or perhaps the draw element begins a new paragraph
     539          30 :             else if( next_page_element != elem.Children.end() )
     540             :             {
     541          28 :                 TextElement* pText = dynamic_cast<TextElement*>(*next_page_element);
     542          28 :                 if( ! pText )
     543             :                 {
     544          28 :                     ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*next_page_element);
     545          28 :                     if( pPara && ! pPara->Children.empty() )
     546           0 :                         pText = dynamic_cast<TextElement*>(pPara->Children.front());
     547             :                 }
     548          28 :                 if( pText && // check there is a text
     549           0 :                     pDraw->h < pText->h*1.5 && // and it is approx the same height
     550             :                     // and either upper or lower edge of pDraw is inside text's vertical range
     551           0 :                     ( ( pDraw->y >= pText->y && pDraw->y <= pText->y+pText->h ) ||
     552           0 :                       ( pDraw->y+pDraw->h >= pText->y && pDraw->y+pDraw->h <= pText->y+pText->h )
     553             :                       )
     554             :                     )
     555             :                 {
     556           0 :                     bInsertToParagraph = true;
     557           0 :                     fCurLineHeight = pDraw->h;
     558           0 :                     nCurLineElements = 1;
     559           0 :                     line_left = pDraw->x;
     560           0 :                     line_right = pDraw->x + pDraw->w;
     561             :                     // begin a new paragraph
     562           0 :                     pCurPara = NULL;
     563             :                     // mark draw element as character
     564           0 :                     pDraw->isCharacter = true;
     565             :                 }
     566             :             }
     567             : 
     568          30 :             if( ! bInsertToParagraph )
     569             :             {
     570          30 :                 pCurPara = NULL;
     571          30 :                 continue;
     572             :             }
     573             :         }
     574             : 
     575           0 :         TextElement* pText = dynamic_cast<TextElement*>(*page_element);
     576           0 :         if( ! pText && pLink && ! pLink->Children.empty() )
     577           0 :             pText = dynamic_cast<TextElement*>(pLink->Children.front());
     578           0 :         if( pText )
     579             :         {
     580             :             Element* pGeo = pLink ? static_cast<Element*>(pLink) :
     581           0 :                                     static_cast<Element*>(pText);
     582           0 :             if( pCurPara )
     583             :             {
     584             :                 // there was already a text element, check for a new paragraph
     585           0 :                 if( nCurLineElements > 0 )
     586             :                 {
     587             :                     // if the new text is significantly distant from the paragraph
     588             :                     // begin a new paragraph
     589           0 :                     if( pGeo->y > pCurPara->y+pCurPara->h + fCurLineHeight*0.5 )
     590           0 :                         pCurPara = NULL; // insert new paragraph
     591           0 :                     else if( pGeo->y > (pCurPara->y+pCurPara->h - fCurLineHeight*0.05) )
     592             :                     {
     593             :                         // new paragraph if either the last line of the paragraph
     594             :                         // was significantly shorter than the paragraph as a whole
     595           0 :                         if( (line_right - line_left) < pCurPara->w*0.75 )
     596           0 :                             pCurPara = NULL;
     597             :                         // or the last line was significantly smaller than the column width
     598           0 :                         else if( (line_right - line_left) < column_width*0.75 )
     599           0 :                             pCurPara = NULL;
     600             :                     }
     601             :                 }
     602             :             }
     603             :             // update line height/width
     604           0 :             if( pCurPara )
     605             :             {
     606           0 :                 fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pGeo->h)/double(nCurLineElements+1);
     607           0 :                 nCurLineElements++;
     608           0 :                 if( pGeo->x < line_left )
     609           0 :                     line_left = pGeo->x;
     610           0 :                 if( pGeo->x+pGeo->w > line_right )
     611           0 :                     line_right = pGeo->x+pGeo->w;
     612             :             }
     613             :             else
     614             :             {
     615           0 :                 fCurLineHeight = pGeo->h;
     616           0 :                 nCurLineElements = 1;
     617           0 :                 line_left = pGeo->x;
     618           0 :                 line_right = pGeo->x + pGeo->w;
     619             :             }
     620             :         }
     621             : 
     622             :         // move element to current paragraph
     623           0 :         if( ! pCurPara ) // new paragraph, insert one
     624             :         {
     625           0 :             pCurPara = m_rProcessor.getElementFactory()->createParagraphElement( NULL );
     626             :             // set parent
     627           0 :             pCurPara->Parent = &elem;
     628             :             //insert new paragraph before current element
     629           0 :             page_element = elem.Children.insert( page_element, pCurPara );
     630             :             // forward iterator to current element again
     631           0 :             ++ page_element;
     632             :             // update next_element which is now invalid
     633           0 :             next_page_element = page_element;
     634           0 :             ++ next_page_element;
     635             :         }
     636           0 :         Element* pCurEle = *page_element;
     637           0 :         Element::setParent( page_element, pCurPara );
     638             :         OSL_ENSURE( !pText || pCurEle == pText || pCurEle == pLink, "paragraph child list in disorder" );
     639           0 :         if( pText || pDraw )
     640           0 :             pCurPara->updateGeometryWith( pCurEle );
     641             :     }
     642             : 
     643             :     // process children
     644           2 :     elem.applyToChildren(*this);
     645             : 
     646             :     // find possible header and footer
     647           2 :     checkHeaderAndFooter( elem );
     648           2 : }
     649             : 
     650           2 : void WriterXmlOptimizer::checkHeaderAndFooter( PageElement& rElem )
     651             : {
     652             :     /* indicators for a header:
     653             :      *  - single line paragrah at top of page (  inside 15% page height)
     654             :      *  - at least linheight above the next paragr   aph
     655             :      *
     656             :      *  indicators for a footer likewise:
     657             :      *  - single line paragraph at bottom of page (inside 15% page height)
     658             :      *  - at least lineheight below the previous paragraph
     659             :      */
     660             : 
     661             :     // detect header
     662             :     // Note: the following assumes that the pages' chiuldren have been
     663             :     // sorted geometrically
     664           2 :     std::list< Element* >::iterator it = rElem.Children.begin();
     665          34 :     while( it != rElem.Children.end() )
     666             :     {
     667          30 :         ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*it);
     668          30 :         if( pPara )
     669             :         {
     670           0 :             if( pPara->y+pPara->h < rElem.h*0.15 && pPara->isSingleLined( m_rProcessor ) )
     671             :             {
     672           0 :                 std::list< Element* >::iterator next_it = it;
     673           0 :                 ParagraphElement* pNextPara = NULL;
     674           0 :                 while( ++next_it != rElem.Children.end() && pNextPara == NULL )
     675             :                 {
     676           0 :                     pNextPara = dynamic_cast<ParagraphElement*>(*next_it);
     677             :                 }
     678           0 :                 if( pNextPara && pNextPara->y > pPara->y+pPara->h*2 )
     679             :                 {
     680           0 :                     rElem.HeaderElement = pPara;
     681           0 :                     pPara->Parent = NULL;
     682           0 :                     rElem.Children.remove( pPara );
     683             :                 }
     684             :             }
     685           0 :             break;
     686             :         }
     687          30 :         ++it;
     688             :     }
     689             : 
     690             :     // detect footer
     691           2 :     std::list< Element* >::reverse_iterator rit = rElem.Children.rbegin();
     692          34 :     while( rit != rElem.Children.rend() )
     693             :     {
     694          30 :         ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*rit);
     695          30 :         if( pPara )
     696             :         {
     697           0 :             if( pPara->y > rElem.h*0.85 && pPara->isSingleLined( m_rProcessor ) )
     698             :             {
     699           0 :                 std::list< Element* >::reverse_iterator next_it = rit;
     700           0 :                 ParagraphElement* pNextPara = NULL;
     701           0 :                 while( ++next_it != rElem.Children.rend() && pNextPara == NULL )
     702             :                 {
     703           0 :                     pNextPara = dynamic_cast<ParagraphElement*>(*next_it);
     704             :                 }
     705           0 :                 if( pNextPara && pNextPara->y < pPara->y-pPara->h*2 )
     706             :                 {
     707           0 :                     rElem.FooterElement = pPara;
     708           0 :                     pPara->Parent = NULL;
     709           0 :                     rElem.Children.remove( pPara );
     710             :                 }
     711             :             }
     712           0 :             break;
     713             :         }
     714          30 :         ++rit;
     715             :     }
     716           2 : }
     717             : 
     718          22 : void WriterXmlOptimizer::optimizeTextElements(Element& rParent)
     719             : {
     720          22 :     if( rParent.Children.empty() ) // this should not happen
     721             :     {
     722             :         OSL_FAIL( "empty paragraph optimized" );
     723          22 :         return;
     724             :     }
     725             : 
     726             :     // concatenate child elements with same font id
     727          22 :     std::list< Element* >::iterator next = rParent.Children.begin();
     728          22 :     std::list< Element* >::iterator it = next++;
     729          22 :     FrameElement* pFrame = dynamic_cast<FrameElement*>(rParent.Parent);
     730          22 :     bool bRotatedFrame = false;
     731          22 :     if( pFrame )
     732             :     {
     733          22 :         const GraphicsContext& rFrameGC = m_rProcessor.getGraphicsContext( pFrame->GCId );
     734          22 :         if( rFrameGC.isRotatedOrSkewed() )
     735           0 :             bRotatedFrame = true;
     736             :     }
     737         230 :     while( next != rParent.Children.end() )
     738             :     {
     739         186 :         bool bConcat = false;
     740         186 :         TextElement* pCur = dynamic_cast<TextElement*>(*it);
     741         186 :         if( pCur )
     742             :         {
     743         186 :             TextElement* pNext = dynamic_cast<TextElement*>(*next);
     744         186 :             if( pNext )
     745             :             {
     746         186 :                 const GraphicsContext& rCurGC = m_rProcessor.getGraphicsContext( pCur->GCId );
     747         186 :                 const GraphicsContext& rNextGC = m_rProcessor.getGraphicsContext( pNext->GCId );
     748             : 
     749             :                 // line and space optimization; works only in strictly horizontal mode
     750             : 
     751         372 :                 if( !bRotatedFrame
     752         186 :                     && ! rCurGC.isRotatedOrSkewed()
     753         186 :                     && ! rNextGC.isRotatedOrSkewed()
     754         186 :                     && ! pNext->Text.isEmpty()
     755         186 :                     && pNext->Text[0] != ' '
     756         162 :                     && ! pCur->Text.isEmpty()
     757         348 :                     && pCur->Text[pCur->Text.getLength() - 1] != ' '
     758             :                     )
     759             :                 {
     760             :                     // check for new line in paragraph
     761         138 :                     if( pNext->y > pCur->y+pCur->h )
     762             :                     {
     763             :                         // new line begins
     764             :                         // check whether a space would should be inserted or a hyphen removed
     765           0 :                         sal_Unicode aLastCode = pCur->Text[pCur->Text.getLength() - 1];
     766           0 :                         if( aLastCode == '-'
     767           0 :                             || aLastCode == 0x2010
     768           0 :                             || (aLastCode >= 0x2012 && aLastCode <= 0x2015)
     769           0 :                             || aLastCode == 0xff0d
     770             :                         )
     771             :                         {
     772             :                             // cut a hyphen
     773           0 :                             pCur->Text.setLength( pCur->Text.getLength()-1 );
     774             :                         }
     775             :                         // append a space unless there is a non breaking hyphen
     776           0 :                         else if( aLastCode != 0x2011 )
     777             :                         {
     778           0 :                             pCur->Text.append( ' ' );
     779             :                         }
     780             :                     }
     781             :                     else // we're continuing the same line
     782             :                     {
     783             :                         // check whether a space would should be inserted
     784             :                         // check for a small horizontal offset
     785         138 :                         if( pCur->x + pCur->w + pNext->h*0.15 < pNext->x )
     786             :                         {
     787           0 :                             pCur->Text.append( ' ' );
     788             :                         }
     789             :                     }
     790             :                 }
     791             :                 // concatenate consecutive text elements unless there is a
     792             :                 // font or text color or matrix change, leave a new span in that case
     793         558 :                 if( pCur->FontId == pNext->FontId &&
     794         372 :                     rCurGC.FillColor.Red == rNextGC.FillColor.Red &&
     795         372 :                     rCurGC.FillColor.Green == rNextGC.FillColor.Green &&
     796         372 :                     rCurGC.FillColor.Blue == rNextGC.FillColor.Blue &&
     797         558 :                     rCurGC.FillColor.Alpha == rNextGC.FillColor.Alpha &&
     798         186 :                     rCurGC.Transformation == rNextGC.Transformation
     799             :                     )
     800             :                 {
     801           0 :                     pCur->updateGeometryWith( pNext );
     802             :                     // append text to current element
     803           0 :                     pCur->Text.append( pNext->Text.getStr(), pNext->Text.getLength() );
     804             :                     // append eventual children to current element
     805             :                     // and clear children (else the children just
     806             :                     // appended to pCur would be destroyed)
     807           0 :                     pCur->Children.splice( pCur->Children.end(), pNext->Children );
     808             :                     // get rid of the now useless element
     809           0 :                     rParent.Children.erase( next );
     810           0 :                     delete pNext;
     811           0 :                     bConcat = true;
     812             :                 }
     813             :             }
     814             :         }
     815           0 :         else if( dynamic_cast<HyperlinkElement*>(*it) )
     816           0 :             optimizeTextElements( **it );
     817         186 :         if( bConcat )
     818             :         {
     819           0 :             next = it;
     820           0 :             ++next;
     821             :         }
     822             :         else
     823             :         {
     824         186 :             ++it;
     825         186 :             ++next;
     826             :         }
     827             :     }
     828             : }
     829             : 
     830           2 : void WriterXmlOptimizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&)
     831             : {
     832           2 :     elem.applyToChildren(*this);
     833           2 : }
     834             : 
     835             : 
     836             : 
     837             : 
     838           6 : void WriterXmlFinalizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
     839             : {
     840             :     // xxx TODO copied from DrawElement
     841           6 :     const GraphicsContext& rGC = m_rProcessor.getGraphicsContext(elem.GCId );
     842           6 :     PropertyMap aProps;
     843           6 :     aProps[ "style:family" ] = "graphic";
     844             : 
     845          12 :     PropertyMap aGCProps;
     846           6 :     if (elem.Action & PATH_STROKE)
     847             :     {
     848           4 :         double scale = GetAverageTransformationScale(rGC.Transformation);
     849           4 :         if (rGC.DashArray.size() < 2)
     850             :         {
     851           2 :             aGCProps[ "draw:stroke" ] = "solid";
     852             :         }
     853             :         else
     854             :         {
     855           2 :             PropertyMap props;
     856           2 :             FillDashStyleProps(props, rGC.DashArray, scale);
     857           4 :             StyleContainer::Style style("draw:stroke-dash", props);
     858             : 
     859           2 :             aGCProps[ "draw:stroke" ] = "dash";
     860           4 :             aGCProps[ "draw:stroke-dash" ] =
     861             :                 m_rStyleContainer.getStyleName(
     862           4 :                 m_rStyleContainer.getStyleId(style));
     863             :         }
     864             : 
     865           4 :         aGCProps[ "svg:stroke-color" ] = getColorString(rGC.LineColor);
     866           4 :         aGCProps[ "svg:stroke-width" ] = convertPixelToUnitString(rGC.LineWidth * scale);
     867           4 :         aGCProps[ "draw:stroke-linejoin" ] = rGC.GetLineJoinString();
     868           4 :         aGCProps[ "svg:stroke-linecap" ] = rGC.GetLineCapString();
     869             :     }
     870             :     else
     871             :     {
     872           2 :         aGCProps[ "draw:stroke" ] = "none";
     873             :     }
     874             : 
     875             :     // TODO(F1): check whether stuff could be emulated by gradient/bitmap/hatch
     876           6 :     if( elem.Action & (PATH_FILL | PATH_EOFILL) )
     877             :     {
     878           2 :         aGCProps[ "draw:fill" ]   = "solid";
     879           2 :         aGCProps[ "draw:fill-color" ] = getColorString( rGC.FillColor );
     880             :     }
     881             :     else
     882             :     {
     883           4 :         aGCProps[ "draw:fill" ] = "none";
     884             :     }
     885             : 
     886          12 :     StyleContainer::Style aStyle( "style:style", aProps );
     887          12 :     StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps );
     888           6 :     aStyle.SubStyles.push_back( &aSubStyle );
     889             : 
     890          12 :     elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
     891           6 : }
     892             : 
     893           0 : void WriterXmlFinalizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& )
     894             : {
     895           0 : }
     896             : 
     897         208 : void WriterXmlFinalizer::visit( TextElement& elem, const std::list< Element* >::const_iterator& )
     898             : {
     899         208 :     const FontAttributes& rFont = m_rProcessor.getFont( elem.FontId );
     900         208 :     PropertyMap aProps;
     901         208 :     aProps[ "style:family" ] = "text";
     902             : 
     903         416 :     PropertyMap aFontProps;
     904             : 
     905             :     // family name
     906         208 :     aFontProps[ "fo:font-family" ] = rFont.familyName;
     907             :     // bold
     908         208 :     if( rFont.isBold )
     909             :     {
     910           0 :         aFontProps[ "fo:font-weight" ]         = "bold";
     911           0 :         aFontProps[ "fo:font-weight-asian" ]   = "bold";
     912           0 :         aFontProps[ "fo:font-weight-complex" ] = "bold";
     913             :     }
     914             :     // italic
     915         208 :     if( rFont.isItalic )
     916             :     {
     917           0 :         aFontProps[ "fo:font-style" ]         = "italic";
     918           0 :         aFontProps[ "fo:font-style-asian" ]   = "italic";
     919           0 :         aFontProps[ "fo:font-style-complex" ] = "italic";
     920             :     }
     921             :     // underline
     922         208 :     if( rFont.isUnderline )
     923             :     {
     924           0 :         aFontProps[ "style:text-underline-style" ]  = "solid";
     925           0 :         aFontProps[ "style:text-underline-width" ]  = "auto";
     926           0 :         aFontProps[ "style:text-underline-color" ]  = "font-color";
     927             :     }
     928             :     // outline
     929         208 :     if( rFont.isOutline )
     930             :     {
     931           0 :         aFontProps[ "style:text-outline" ]  = "true";
     932             :     }
     933             :     // size
     934         416 :     OUStringBuffer aBuf( 32 );
     935         208 :     aBuf.append( rFont.size*72/PDFI_OUTDEV_RESOLUTION );
     936         208 :     aBuf.appendAscii( "pt" );
     937         416 :     OUString aFSize = aBuf.makeStringAndClear();
     938         208 :     aFontProps[ "fo:font-size" ]            = aFSize;
     939         208 :     aFontProps[ "style:font-size-asian" ]   = aFSize;
     940         208 :     aFontProps[ "style:font-size-complex" ] = aFSize;
     941             :     // color
     942         208 :     const GraphicsContext& rGC = m_rProcessor.getGraphicsContext( elem.GCId );
     943         208 :     aFontProps[ "fo:color" ]                 =  getColorString( rFont.isOutline ? rGC.LineColor : rGC.FillColor );
     944             : 
     945         416 :     StyleContainer::Style aStyle( "style:style", aProps );
     946         416 :     StyleContainer::Style aSubStyle( "style:text-properties", aFontProps );
     947         208 :     aStyle.SubStyles.push_back( &aSubStyle );
     948         416 :     elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
     949         208 : }
     950             : 
     951          22 : void WriterXmlFinalizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& rParentIt )
     952             : {
     953          22 :     PropertyMap aParaProps;
     954             : 
     955          22 :     if( elem.Parent )
     956             :     {
     957             :         // check for center alignement
     958             :         // criterion: paragraph is small relative to parent and distributed around its center
     959          22 :         double p_x = elem.Parent->x;
     960          22 :         double p_w = elem.Parent->w;
     961             : 
     962          22 :         PageElement* pPage = dynamic_cast<PageElement*>(elem.Parent);
     963          22 :         if( pPage )
     964             :         {
     965           0 :             p_x += pPage->LeftMargin;
     966           0 :             p_w -= pPage->LeftMargin+pPage->RightMargin;
     967             :         }
     968          22 :         bool bIsCenter = false;
     969          22 :         if( elem.w < ( p_w/2) )
     970             :         {
     971           0 :             double delta = elem.w/4;
     972             :             // allow very small paragraphs to deviate a little more
     973             :             // relative to parent's center
     974           0 :             if( elem.w <  p_w/8 )
     975           0 :                 delta = elem.w;
     976           0 :             if( fabs( elem.x+elem.w/2 - ( p_x+ p_w/2) ) <  delta ||
     977           0 :                 (pPage && fabs( elem.x+elem.w/2 - (pPage->x + pPage->w/2) ) <  delta) )
     978             :             {
     979           0 :                 bIsCenter = true;
     980           0 :                 aParaProps[ "fo:text-align" ] = "center";
     981             :             }
     982             :         }
     983          22 :         if( ! bIsCenter && elem.x > p_x + p_w/10 )
     984             :         {
     985             :             // indent
     986           0 :             OUStringBuffer aBuf( 32 );
     987           0 :             aBuf.append( convPx2mm( elem.x - p_x ) );
     988           0 :             aBuf.appendAscii( "mm" );
     989           0 :             aParaProps[ "fo:margin-left" ] = aBuf.makeStringAndClear();
     990             :         }
     991             : 
     992             :         // check whether to leave some space to next paragraph
     993             :         // find whether there is a next paragraph
     994          22 :         std::list< Element* >::const_iterator it = rParentIt;
     995          22 :         const ParagraphElement* pNextPara = NULL;
     996          44 :         while( ++it != elem.Parent->Children.end() && ! pNextPara )
     997           0 :             pNextPara = dynamic_cast< const ParagraphElement* >(*it);
     998          22 :         if( pNextPara )
     999             :         {
    1000           0 :             if( pNextPara->y - (elem.y+elem.h) > convmm2Px( 10 ) )
    1001             :             {
    1002           0 :                 OUStringBuffer aBuf( 32 );
    1003           0 :                 aBuf.append( convPx2mm( pNextPara->y - (elem.y+elem.h) ) );
    1004           0 :                 aBuf.appendAscii( "mm" );
    1005           0 :                 aParaProps[ "fo:margin-bottom" ] = aBuf.makeStringAndClear();
    1006             :             }
    1007             :         }
    1008             :     }
    1009             : 
    1010          22 :     if( ! aParaProps.empty() )
    1011             :     {
    1012           0 :         PropertyMap aProps;
    1013           0 :         aProps[ "style:family" ] = "paragraph";
    1014           0 :         StyleContainer::Style aStyle( "style:style", aProps );
    1015           0 :         StyleContainer::Style aSubStyle( "style:paragraph-properties", aParaProps );
    1016           0 :         aStyle.SubStyles.push_back( &aSubStyle );
    1017           0 :         elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
    1018             :     }
    1019             : 
    1020          22 :     elem.applyToChildren(*this);
    1021          22 : }
    1022             : 
    1023          24 : void WriterXmlFinalizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator&)
    1024             : {
    1025          24 :     PropertyMap aProps;
    1026          24 :     aProps[ "style:family" ] = "graphic";
    1027             : 
    1028          48 :     PropertyMap aGCProps;
    1029             : 
    1030          24 :     aGCProps[ "draw:stroke" ]                    = "none";
    1031          24 :     aGCProps[ "draw:fill" ]                      = "none";
    1032          24 :     aGCProps[ "draw:auto-grow-height" ]          = "true";
    1033          24 :     aGCProps[ "draw:auto-grow-width" ]           = "true";
    1034          24 :     aGCProps[ "draw:textarea-horizontal-align" ] = "left";
    1035          24 :     aGCProps[ "draw:textarea-vertical-align" ]   = "top";
    1036          24 :     aGCProps[ "fo:min-height"]                   = "0cm";
    1037          24 :     aGCProps[ "fo:min-width"]                    = "0cm";
    1038          24 :     aGCProps[ "fo:padding-top" ]                 = "0cm";
    1039          24 :     aGCProps[ "fo:padding-left" ]                = "0cm";
    1040          24 :     aGCProps[ "fo:padding-right" ]               = "0cm";
    1041          24 :     aGCProps[ "fo:padding-bottom" ]              = "0cm";
    1042             : 
    1043          48 :     StyleContainer::Style aStyle( "style:style", aProps );
    1044          48 :     StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps );
    1045          24 :     aStyle.SubStyles.push_back( &aSubStyle );
    1046             : 
    1047          24 :     elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
    1048          48 :     elem.applyToChildren(*this);
    1049          24 : }
    1050             : 
    1051           2 : void WriterXmlFinalizer::visit( ImageElement&, const std::list< Element* >::const_iterator& )
    1052             : {
    1053           2 : }
    1054             : 
    1055           2 : void WriterXmlFinalizer::setFirstOnPage( ParagraphElement&    rElem,
    1056             :                                          StyleContainer&      rStyles,
    1057             :                                          const OUString& rMasterPageName )
    1058             : {
    1059           2 :     PropertyMap aProps;
    1060           2 :     if( rElem.StyleId != -1 )
    1061             :     {
    1062           0 :         const PropertyMap* pProps = rStyles.getProperties( rElem.StyleId );
    1063           0 :         if( pProps )
    1064           0 :             aProps = *pProps;
    1065             :     }
    1066             : 
    1067           2 :     aProps[ "style:family" ] = "paragraph";
    1068           2 :     aProps[ "style:master-page-name" ] = rMasterPageName;
    1069             : 
    1070           2 :     if( rElem.StyleId != -1 )
    1071           0 :         rElem.StyleId = rStyles.setProperties( rElem.StyleId, aProps );
    1072             :     else
    1073             :     {
    1074           2 :         StyleContainer::Style aStyle( "style:style", aProps );
    1075           2 :         rElem.StyleId = rStyles.getStyleId( aStyle );
    1076           2 :     }
    1077           2 : }
    1078             : 
    1079           2 : void WriterXmlFinalizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
    1080             : {
    1081           2 :     if( m_rProcessor.getStatusIndicator().is() )
    1082           0 :         m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber );
    1083             : 
    1084             :     // transform from pixel to mm
    1085           2 :     double page_width = convPx2mm( elem.w ), page_height = convPx2mm( elem.h );
    1086             : 
    1087             :     // calculate page margins out of the relevant children (paragraphs)
    1088           2 :     elem.TopMargin = elem.h, elem.BottomMargin = 0, elem.LeftMargin = elem.w, elem.RightMargin = 0;
    1089             :     // first element should be a paragraphy
    1090           2 :     ParagraphElement* pFirstPara = NULL;
    1091          32 :     for( std::list< Element* >::const_iterator it = elem.Children.begin(); it != elem.Children.end(); ++it )
    1092             :     {
    1093          30 :         if( dynamic_cast<ParagraphElement*>( *it ) )
    1094             :         {
    1095           0 :             if( (*it)->x < elem.LeftMargin )
    1096           0 :                 elem.LeftMargin = (*it)->x;
    1097           0 :             if( (*it)->y < elem.TopMargin )
    1098           0 :                 elem.TopMargin = (*it)->y;
    1099           0 :             if( (*it)->x + (*it)->w > elem.w - elem.RightMargin )
    1100           0 :                 elem.RightMargin = elem.w - ((*it)->x + (*it)->w);
    1101           0 :             if( (*it)->y + (*it)->h > elem.h - elem.BottomMargin )
    1102           0 :                 elem.BottomMargin = elem.h - ((*it)->y + (*it)->h);
    1103           0 :             if( ! pFirstPara )
    1104           0 :                 pFirstPara = dynamic_cast<ParagraphElement*>( *it );
    1105             :         }
    1106             :     }
    1107           2 :     if( elem.HeaderElement && elem.HeaderElement->y < elem.TopMargin )
    1108           0 :         elem.TopMargin = elem.HeaderElement->y;
    1109           2 :     if( elem.FooterElement && elem.FooterElement->y+elem.FooterElement->h > elem.h - elem.BottomMargin )
    1110           0 :         elem.BottomMargin = elem.h - (elem.FooterElement->y + elem.FooterElement->h);
    1111             : 
    1112             :     // transform margins to mm
    1113           2 :     double left_margin     = convPx2mm( elem.LeftMargin );
    1114           2 :     double right_margin    = convPx2mm( elem.RightMargin );
    1115           2 :     double top_margin      = convPx2mm( elem.TopMargin );
    1116           2 :     double bottom_margin   = convPx2mm( elem.BottomMargin );
    1117           2 :     if( ! pFirstPara )
    1118             :     {
    1119             :         // use default page margins
    1120           2 :         left_margin     = 10;
    1121           2 :         right_margin    = 10;
    1122           2 :         top_margin      = 10;
    1123           2 :         bottom_margin   = 10;
    1124             :     }
    1125             : 
    1126             :     // round left/top margin to nearest mm
    1127           2 :     left_margin     = rtl_math_round( left_margin, 0, rtl_math_RoundingMode_Floor );
    1128           2 :     top_margin      = rtl_math_round( top_margin, 0, rtl_math_RoundingMode_Floor );
    1129             :     // round (fuzzy) right/bottom margin to nearest cm
    1130           2 :     right_margin    = rtl_math_round( right_margin, right_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor );
    1131           2 :     bottom_margin   = rtl_math_round( bottom_margin, bottom_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor );
    1132             : 
    1133             :     // set reasonable default in case of way too large margins
    1134             :     // e.g. no paragraph case
    1135           2 :     if( left_margin > page_width/2.0 - 10 )
    1136           0 :         left_margin = 10;
    1137           2 :     if( right_margin > page_width/2.0 - 10 )
    1138           0 :         right_margin = 10;
    1139           2 :     if( top_margin > page_height/2.0 - 10 )
    1140           0 :         top_margin = 10;
    1141           2 :     if( bottom_margin > page_height/2.0 - 10 )
    1142           0 :         bottom_margin = 10;
    1143             : 
    1144             :     // catch the weird cases
    1145           2 :     if( left_margin < 0 )
    1146           0 :         left_margin = 0;
    1147           2 :     if( right_margin < 0 )
    1148           0 :         right_margin = 0;
    1149           2 :     if( top_margin < 0 )
    1150           0 :         top_margin = 0;
    1151           2 :     if( bottom_margin < 0 )
    1152           0 :         bottom_margin = 0;
    1153             : 
    1154             :     // widely differing margins are unlikely to be correct
    1155           2 :     if( right_margin > left_margin*1.5 )
    1156           0 :         right_margin = left_margin;
    1157             : 
    1158           2 :     elem.LeftMargin      = convmm2Px( left_margin );
    1159           2 :     elem.RightMargin     = convmm2Px( right_margin );
    1160           2 :     elem.TopMargin       = convmm2Px( top_margin );
    1161           2 :     elem.BottomMargin    = convmm2Px( bottom_margin );
    1162             : 
    1163             :     // get styles for paragraphs
    1164           2 :     PropertyMap aPageProps;
    1165           4 :     PropertyMap aPageLayoutProps;
    1166           2 :     aPageLayoutProps[ "fo:page-width" ]     = unitMMString( page_width );
    1167           2 :     aPageLayoutProps[ "fo:page-height" ]    = unitMMString( page_height );
    1168           4 :     aPageLayoutProps[ "style:print-orientation" ]
    1169           6 :         = elem.w < elem.h ? OUString("portrait") : OUString("landscape");
    1170           2 :     aPageLayoutProps[ "fo:margin-top" ]     = unitMMString( top_margin );
    1171           2 :     aPageLayoutProps[ "fo:margin-bottom" ]  = unitMMString( bottom_margin );
    1172           2 :     aPageLayoutProps[ "fo:margin-left" ]    = unitMMString( left_margin );
    1173           2 :     aPageLayoutProps[ "fo:margin-right" ]   = unitMMString( right_margin );
    1174           2 :     aPageLayoutProps[ "style:writing-mode" ]= "lr-tb";
    1175             : 
    1176           4 :     StyleContainer::Style aStyle( "style:page-layout", aPageProps);
    1177           4 :     StyleContainer::Style aSubStyle( "style:page-layout-properties", aPageLayoutProps);
    1178           2 :     aStyle.SubStyles.push_back(&aSubStyle);
    1179           2 :     sal_Int32 nPageStyle = m_rStyleContainer.impl_getStyleId( aStyle, false );
    1180             : 
    1181             :     // create master page
    1182           4 :     OUString aMasterPageLayoutName = m_rStyleContainer.getStyleName( nPageStyle );
    1183           2 :     aPageProps[ "style:page-layout-name" ] = aMasterPageLayoutName;
    1184           4 :     StyleContainer::Style aMPStyle( "style:master-page", aPageProps );
    1185           4 :     StyleContainer::Style aHeaderStyle( "style:header", PropertyMap() );
    1186           4 :     StyleContainer::Style aFooterStyle( "style:footer", PropertyMap() );
    1187           2 :     if( elem.HeaderElement )
    1188             :     {
    1189           0 :         elem.HeaderElement->visitedBy( *this, std::list<Element*>::iterator() );
    1190           0 :         aHeaderStyle.ContainedElement = elem.HeaderElement;
    1191           0 :         aMPStyle.SubStyles.push_back( &aHeaderStyle );
    1192             :     }
    1193           2 :     if( elem.FooterElement )
    1194             :     {
    1195           0 :         elem.FooterElement->visitedBy( *this, std::list<Element*>::iterator() );
    1196           0 :         aFooterStyle.ContainedElement = elem.FooterElement;
    1197           0 :         aMPStyle.SubStyles.push_back( &aFooterStyle );
    1198             :     }
    1199           2 :     elem.StyleId = m_rStyleContainer.impl_getStyleId( aMPStyle,false );
    1200             : 
    1201             : 
    1202           4 :     OUString aMasterPageName = m_rStyleContainer.getStyleName( elem.StyleId );
    1203             : 
    1204             :     // create styles for children
    1205           2 :     elem.applyToChildren(*this);
    1206             : 
    1207             :     // no paragraph or other elements before the first paragraph
    1208           2 :     if( ! pFirstPara )
    1209             :     {
    1210           2 :         pFirstPara = m_rProcessor.getElementFactory()->createParagraphElement( NULL );
    1211           2 :         pFirstPara->Parent = &elem;
    1212           2 :         elem.Children.push_front( pFirstPara );
    1213             :     }
    1214           4 :     setFirstOnPage(*pFirstPara, m_rStyleContainer, aMasterPageName);
    1215           2 : }
    1216             : 
    1217           2 : void WriterXmlFinalizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator& )
    1218             : {
    1219           2 :     elem.applyToChildren(*this);
    1220           2 : }
    1221             : 
    1222             : }
    1223             : 
    1224             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10