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