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

Generated by: LCOV version 1.11