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

Generated by: LCOV version 1.10