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