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