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