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