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 0 : const Reference< XBreakIterator >& DrawXmlOptimizer::GetBreakIterator()
52 : {
53 0 : if ( !mxBreakIter.is() )
54 : {
55 0 : Reference< XComponentContext > xContext( this->m_rProcessor.m_xContext, uno::UNO_SET_THROW );
56 0 : mxBreakIter = BreakIterator::create(xContext);
57 : }
58 0 : return mxBreakIter;
59 : }
60 :
61 0 : const Reference< XCharacterClassification >& DrawXmlEmitter::GetCharacterClassification()
62 : {
63 0 : if ( !mxCharClass.is() )
64 : {
65 0 : Reference< XComponentContext > xContext( m_rEmitContext.m_xContext, uno::UNO_SET_THROW );
66 0 : mxCharClass = CharacterClassification::create(xContext);
67 : }
68 0 : 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 0 : void DrawXmlEmitter::visit( TextElement& elem, const std::list< Element* >::const_iterator& )
95 : {
96 0 : if( elem.Text.isEmpty() )
97 0 : return;
98 :
99 0 : OUString strSpace(32);
100 0 : OUString strNbSpace(160);
101 0 : OUString tabSpace(0x09);
102 0 : PropertyMap aProps;
103 0 : if( elem.StyleId != -1 )
104 : {
105 0 : aProps[ OUString( "text:style-name" ) ] =
106 0 : m_rEmitContext.rStyles.getStyleName( elem.StyleId );
107 : }
108 :
109 0 : OUString str(elem.Text.getStr());
110 :
111 : // Check for RTL
112 0 : bool isRTL = false;
113 0 : Reference< i18n::XCharacterClassification > xCC( GetCharacterClassification() );
114 0 : if( xCC.is() )
115 : {
116 0 : for(int i=1; i< elem.Text.getLength(); i++)
117 : {
118 0 : sal_Int16 nType = xCC->getCharacterDirection( str, i );
119 0 : if ( nType == ::com::sun::star::i18n::DirectionProperty_RIGHT_TO_LEFT ||
120 0 : nType == ::com::sun::star::i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC ||
121 0 : 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 0 : if (isRTL) // If so, reverse string
129 0 : str = m_rProcessor.mirrorString( str );
130 :
131 0 : m_rEmitContext.rEmitter.beginTag( "text:span", aProps );
132 :
133 0 : for(int i=0; i< elem.Text.getLength(); i++)
134 : {
135 0 : OUString strToken= str.copy(i,1) ;
136 0 : if( strSpace.equals(strToken) || strNbSpace.equals(strToken))
137 : {
138 0 : aProps[ "text:c" ] = "1";
139 0 : m_rEmitContext.rEmitter.beginTag( "text:s", aProps );
140 0 : m_rEmitContext.rEmitter.endTag( "text:s");
141 : }
142 : else
143 : {
144 0 : 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 0 : m_rEmitContext.rEmitter.write( strToken );
152 : }
153 : }
154 0 : }
155 :
156 0 : std::list< Element* >::iterator this_it = elem.Children.begin();
157 0 : 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 0 : m_rEmitContext.rEmitter.endTag( "text:span" );
164 : }
165 :
166 0 : void DrawXmlEmitter::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& )
167 : {
168 0 : PropertyMap aProps;
169 0 : if( elem.StyleId != -1 )
170 : {
171 0 : aProps[ "text:style-name" ] = m_rEmitContext.rStyles.getStyleName( elem.StyleId );
172 : }
173 0 : const char* pTagType = "text:p";
174 0 : if( elem.Type == elem.Headline )
175 0 : pTagType = "text:h";
176 0 : m_rEmitContext.rEmitter.beginTag( pTagType, aProps );
177 :
178 0 : std::list< Element* >::iterator this_it = elem.Children.begin();
179 0 : while( this_it !=elem.Children.end() && *this_it != &elem )
180 : {
181 0 : (*this_it)->visitedBy( *this, this_it );
182 0 : ++this_it;
183 : }
184 :
185 0 : m_rEmitContext.rEmitter.endTag( pTagType );
186 0 : }
187 :
188 0 : void DrawXmlEmitter::fillFrameProps( DrawElement& rElem,
189 : PropertyMap& rProps,
190 : const EmitContext& rEmitContext,
191 : bool bWasTransformed
192 : )
193 : {
194 0 : double rel_x = rElem.x, rel_y = rElem.y;
195 :
196 0 : rProps[ "draw:z-index" ] = OUString::number( rElem.ZOrder );
197 0 : rProps[ "draw:style-name"] = rEmitContext.rStyles.getStyleName( rElem.StyleId );
198 0 : rProps[ "svg:width" ] = convertPixelToUnitString( rElem.w );
199 0 : rProps[ "svg:height" ] = convertPixelToUnitString( rElem.h );
200 :
201 : const GraphicsContext& rGC =
202 0 : rEmitContext.rProcessor.getGraphicsContext( rElem.GCId );
203 0 : if( rGC.Transformation.isIdentity() || bWasTransformed )
204 : {
205 0 : rProps[ "svg:x" ] = convertPixelToUnitString( rel_x );
206 0 : rProps[ "svg:y" ] = convertPixelToUnitString( rel_y );
207 : }
208 : else
209 : {
210 0 : basegfx::B2DTuple aScale, aTranslation;
211 : double fRotate, fShearX;
212 :
213 0 : rGC.Transformation.decompose( aScale, aTranslation, fRotate, fShearX );
214 :
215 0 : 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 0 : if( rElem.MirrorVertical )
223 0 : fRotate += M_PI;
224 :
225 : // First check here is to skip image frame case
226 0 : if (rElem.IsForText &&
227 0 : (aScale.getX() < 0) &&
228 0 : (aScale.getY() > 0) &&
229 0 : (basegfx::fTools::equalZero(aScale.getX() + aScale.getY(), 0.0001)))
230 : {
231 0 : fRotate += M_PI;
232 : }
233 :
234 : // build transformation string
235 0 : if( fShearX != 0.0 )
236 : {
237 0 : aBuf.appendAscii( "skewX( " );
238 0 : aBuf.append( fShearX );
239 0 : aBuf.appendAscii( " )" );
240 : }
241 0 : 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 0 : if( !aBuf.isEmpty() )
251 0 : aBuf.append( ' ' );
252 0 : aBuf.appendAscii( "translate( " );
253 0 : aBuf.append( convertPixelToUnitString( rel_x ) );
254 0 : aBuf.append( ' ' );
255 0 : aBuf.append( convertPixelToUnitString( rel_y ) );
256 0 : aBuf.appendAscii( " )" );
257 :
258 0 : rProps[ "draw:transform" ] = aBuf.makeStringAndClear();
259 : }
260 0 : }
261 :
262 0 : void DrawXmlEmitter::visit( FrameElement& elem, const std::list< Element* >::const_iterator& )
263 : {
264 0 : if( elem.Children.empty() )
265 0 : return;
266 :
267 0 : bool bTextBox = (dynamic_cast<ParagraphElement*>(elem.Children.front()) != NULL);
268 0 : PropertyMap aFrameProps;
269 0 : fillFrameProps( elem, aFrameProps, m_rEmitContext );
270 0 : m_rEmitContext.rEmitter.beginTag( "draw:frame", aFrameProps );
271 0 : if( bTextBox )
272 0 : m_rEmitContext.rEmitter.beginTag( "draw:text-box", PropertyMap() );
273 :
274 0 : std::list< Element* >::iterator this_it = elem.Children.begin();
275 0 : while( this_it !=elem.Children.end() && *this_it != &elem )
276 : {
277 0 : (*this_it)->visitedBy( *this, this_it );
278 0 : ++this_it;
279 : }
280 :
281 0 : if( bTextBox )
282 0 : m_rEmitContext.rEmitter.endTag( "draw:text-box" );
283 0 : m_rEmitContext.rEmitter.endTag( "draw:frame" );
284 : }
285 :
286 0 : void DrawXmlEmitter::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
287 : {
288 0 : 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 0 : for (sal_uInt32 i = 0; i< elem.PolyPoly.count(); i++)
297 : {
298 0 : basegfx::B2DPolygon b2dPolygon;
299 0 : b2dPolygon = elem.PolyPoly.getB2DPolygon( i );
300 :
301 0 : for ( sal_uInt32 j = 0; j< b2dPolygon.count(); j++ )
302 : {
303 0 : basegfx::B2DPoint point;
304 0 : basegfx::B2DPoint nextPoint;
305 0 : point = b2dPolygon.getB2DPoint( j );
306 :
307 0 : basegfx::B2DPoint prevPoint;
308 0 : prevPoint = b2dPolygon.getPrevControlPoint( j ) ;
309 :
310 0 : point.setX( convPx2mmPrec2( point.getX() )*100.0 );
311 0 : point.setY( convPx2mmPrec2( point.getY() )*100.0 );
312 :
313 0 : if ( b2dPolygon.isPrevControlPointUsed( j ) )
314 : {
315 0 : prevPoint.setX( convPx2mmPrec2( prevPoint.getX() )*100.0 );
316 0 : prevPoint.setY( convPx2mmPrec2( prevPoint.getY() )*100.0 );
317 : }
318 :
319 0 : if ( b2dPolygon.isNextControlPointUsed( j ) )
320 : {
321 0 : nextPoint = b2dPolygon.getNextControlPoint( j ) ;
322 0 : nextPoint.setX( convPx2mmPrec2( nextPoint.getX() )*100.0 );
323 0 : nextPoint.setY( convPx2mmPrec2( nextPoint.getY() )*100.0 );
324 : }
325 :
326 0 : b2dPolygon.setB2DPoint( j, point );
327 :
328 0 : if ( b2dPolygon.isPrevControlPointUsed( j ) )
329 0 : b2dPolygon.setPrevControlPoint( j , prevPoint ) ;
330 :
331 0 : if ( b2dPolygon.isNextControlPointUsed( j ) )
332 0 : b2dPolygon.setNextControlPoint( j , nextPoint ) ;
333 0 : }
334 :
335 0 : elem.PolyPoly.setB2DPolygon( i, b2dPolygon );
336 0 : }
337 :
338 0 : 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 0 : fillFrameProps( elem, aProps, m_rEmitContext, true );
343 0 : OUStringBuffer aBuf( 64 );
344 0 : aBuf.appendAscii( "0 0 " );
345 0 : aBuf.append( convPx2mmPrec2(elem.w)*100.0 );
346 0 : aBuf.append( ' ' );
347 0 : aBuf.append( convPx2mmPrec2(elem.h)*100.0 );
348 0 : aProps[ "svg:viewBox" ] = aBuf.makeStringAndClear();
349 0 : aProps[ "svg:d" ] = basegfx::tools::exportToSvgD( elem.PolyPoly, true, true, false );
350 :
351 0 : m_rEmitContext.rEmitter.beginTag( "draw:path", aProps );
352 0 : m_rEmitContext.rEmitter.endTag( "draw:path" );
353 0 : }
354 :
355 0 : void DrawXmlEmitter::visit( ImageElement& elem, const std::list< Element* >::const_iterator& )
356 : {
357 0 : PropertyMap aImageProps;
358 0 : m_rEmitContext.rEmitter.beginTag( "draw:image", aImageProps );
359 0 : m_rEmitContext.rEmitter.beginTag( "office:binary-data", PropertyMap() );
360 0 : m_rEmitContext.rImages.writeBase64EncodedStream( elem.Image, m_rEmitContext);
361 0 : m_rEmitContext.rEmitter.endTag( "office:binary-data" );
362 0 : m_rEmitContext.rEmitter.endTag( "draw:image" );
363 0 : }
364 :
365 0 : void DrawXmlEmitter::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
366 : {
367 0 : PropertyMap aPageProps;
368 0 : aPageProps[ "draw:master-page-name" ] = m_rEmitContext.rStyles.getStyleName( elem.StyleId );
369 :
370 0 : m_rEmitContext.rEmitter.beginTag("draw:page", aPageProps);
371 :
372 0 : if( m_rEmitContext.xStatusIndicator.is() )
373 0 : m_rEmitContext.xStatusIndicator->setValue( elem.PageNumber );
374 :
375 0 : std::list< Element* >::iterator this_it = elem.Children.begin();
376 0 : while( this_it !=elem.Children.end() && *this_it != &elem )
377 : {
378 0 : (*this_it)->visitedBy( *this, this_it );
379 0 : ++this_it;
380 : }
381 :
382 0 : m_rEmitContext.rEmitter.endTag("draw:page");
383 0 : }
384 :
385 0 : void DrawXmlEmitter::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&)
386 : {
387 0 : m_rEmitContext.rEmitter.beginTag( "office:body", PropertyMap() );
388 : m_rEmitContext.rEmitter.beginTag( m_bWriteDrawDocument ? "office:drawing" : "office:presentation",
389 0 : PropertyMap() );
390 :
391 0 : std::list< Element* >::iterator this_it = elem.Children.begin();
392 0 : while( this_it !=elem.Children.end() && *this_it != &elem )
393 : {
394 0 : (*this_it)->visitedBy( *this, this_it );
395 0 : ++this_it;
396 : }
397 :
398 0 : m_rEmitContext.rEmitter.endTag( m_bWriteDrawDocument ? "office:drawing" : "office:presentation" );
399 0 : m_rEmitContext.rEmitter.endTag( "office:body" );
400 0 : }
401 :
402 :
403 :
404 0 : void DrawXmlOptimizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& )
405 : {
406 0 : }
407 :
408 0 : void DrawXmlOptimizer::visit( TextElement&, const std::list< Element* >::const_iterator&)
409 : {
410 0 : }
411 :
412 0 : void DrawXmlOptimizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator& )
413 : {
414 0 : elem.applyToChildren(*this);
415 0 : }
416 :
417 0 : void DrawXmlOptimizer::visit( ImageElement&, const std::list< Element* >::const_iterator& )
418 : {
419 0 : }
420 :
421 0 : 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 0 : if( elem.Parent )
428 : {
429 : // find following PolyPolyElement in parent's children list
430 0 : std::list< Element* >::iterator this_it = elem.Parent->Children.begin();
431 0 : while( this_it != elem.Parent->Children.end() && *this_it != &elem )
432 0 : ++this_it;
433 :
434 0 : if( this_it != elem.Parent->Children.end() )
435 : {
436 0 : std::list< Element* >::iterator next_it = this_it;
437 0 : if( ++next_it != elem.Parent->Children.end() )
438 : {
439 0 : PolyPolyElement* pNext = dynamic_cast<PolyPolyElement*>(*next_it);
440 :
441 : // TODO(F2): this comparison fails for OOo-generated polygons with beziers.
442 0 : 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 0 : }
480 :
481 0 : void DrawXmlOptimizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& )
482 : {
483 0 : optimizeTextElements( elem );
484 :
485 0 : elem.applyToChildren(*this);
486 0 : }
487 :
488 0 : void DrawXmlOptimizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
489 : {
490 0 : if( m_rProcessor.getStatusIndicator().is() )
491 0 : m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber );
492 :
493 : // resolve hyperlinks
494 0 : elem.resolveHyperlinks();
495 :
496 0 : 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 0 : m_rProcessor.sortElements( &elem );
501 :
502 : // find paragraphs in text
503 0 : ParagraphElement* pCurPara = NULL;
504 0 : std::list< Element* >::iterator page_element, next_page_element;
505 0 : next_page_element = elem.Children.begin();
506 0 : double fCurLineHeight = 0.0; // average height of text items in current para
507 0 : int nCurLineElements = 0; // number of line contributing elements in current para
508 0 : double line_left = elem.w, line_right = 0.0;
509 0 : double column_width = elem.w*0.75; // estimate text width
510 : // TODO: guess columns
511 0 : while( next_page_element != elem.Children.end() )
512 : {
513 0 : page_element = next_page_element++;
514 0 : ParagraphElement* pPagePara = dynamic_cast<ParagraphElement*>(*page_element);
515 0 : 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 0 : HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(*page_element);
535 0 : DrawElement* pDraw = dynamic_cast<DrawElement*>(*page_element);
536 0 : if( ! pDraw && pLink && ! pLink->Children.empty() )
537 0 : pDraw = dynamic_cast<DrawElement*>(pLink->Children.front() );
538 0 : if( pDraw )
539 : {
540 : // insert small drawing objects as character, else leave them page bound
541 :
542 0 : bool bInsertToParagraph = false;
543 : // first check if this is either inside the paragraph
544 0 : 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 0 : else if( next_page_element != elem.Children.end() )
557 : {
558 0 : TextElement* pText = dynamic_cast<TextElement*>(*next_page_element);
559 0 : if( ! pText )
560 : {
561 0 : ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*next_page_element);
562 0 : if( pPara && ! pPara->Children.empty() )
563 0 : pText = dynamic_cast<TextElement*>(pPara->Children.front());
564 : }
565 0 : 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 0 : if( ! bInsertToParagraph )
586 : {
587 0 : pCurPara = NULL;
588 0 : 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 0 : elem.applyToChildren(*this);
667 0 : }
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 0 : bool notTransformed(const GraphicsContext& GC)
680 : {
681 : return (
682 0 : GC.Transformation.get(0,0) == 100.00 &&
683 0 : GC.Transformation.get(1,0) == 0.00 &&
684 0 : GC.Transformation.get(0,1) == 0.00 &&
685 0 : GC.Transformation.get(1,1) == -100.00
686 0 : );
687 : }
688 :
689 0 : void DrawXmlOptimizer::optimizeTextElements(Element& rParent)
690 : {
691 0 : if( rParent.Children.empty() ) // this should not happen
692 : {
693 : OSL_FAIL( "empty paragraph optimized" );
694 0 : return;
695 : }
696 :
697 : // concatenate child elements with same font id
698 0 : std::list< Element* >::iterator next = rParent.Children.begin();
699 0 : std::list< Element* >::iterator it = next++;
700 :
701 0 : while( next != rParent.Children.end() )
702 : {
703 0 : bool bConcat = false;
704 0 : TextElement* pCur = dynamic_cast<TextElement*>(*it);
705 :
706 0 : if( pCur )
707 : {
708 0 : TextElement* pNext = dynamic_cast<TextElement*>(*next);
709 0 : bool isComplex = false;
710 0 : OUString str(pCur->Text.getStr());
711 0 : for(int i=0; i< str.getLength(); i++)
712 : {
713 0 : sal_Int16 nType = GetBreakIterator()->getScriptType( str, i );
714 0 : if (nType == ::com::sun::star::i18n::ScriptType::COMPLEX)
715 0 : isComplex = true;
716 : }
717 0 : bool bPara = strspn("ParagraphElement", typeid(rParent).name());
718 0 : ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(&rParent);
719 0 : if (bPara && pPara && isComplex)
720 0 : pPara->bRtl = true;
721 0 : if( pNext )
722 : {
723 0 : const GraphicsContext& rCurGC = m_rProcessor.getGraphicsContext( pCur->GCId );
724 0 : 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 0 : if( (pCur->FontId == pNext->FontId || isSpaces(pNext)) &&
731 0 : rCurGC.FillColor.Red == rNextGC.FillColor.Red &&
732 0 : rCurGC.FillColor.Green == rNextGC.FillColor.Green &&
733 0 : rCurGC.FillColor.Blue == rNextGC.FillColor.Blue &&
734 0 : rCurGC.FillColor.Alpha == rNextGC.FillColor.Alpha &&
735 0 : (rCurGC.Transformation == rNextGC.Transformation || notTransformed(rNextGC))
736 : )
737 : {
738 0 : pCur->updateGeometryWith( pNext );
739 : // append text to current element
740 0 : pCur->Text.append( pNext->Text.getStr(), pNext->Text.getLength() );
741 :
742 0 : str = pCur->Text.getStr();
743 0 : for(int i=0; i< str.getLength(); i++)
744 : {
745 0 : sal_Int16 nType = GetBreakIterator()->getScriptType( str, i );
746 0 : if (nType == ::com::sun::star::i18n::ScriptType::COMPLEX)
747 0 : isComplex = true;
748 : }
749 0 : 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 0 : pCur->Children.splice( pCur->Children.end(), pNext->Children );
755 : // get rid of the now useless element
756 0 : rParent.Children.erase( next );
757 0 : delete pNext;
758 0 : bConcat = true;
759 : }
760 0 : }
761 : }
762 0 : else if( dynamic_cast<HyperlinkElement*>(*it) )
763 0 : optimizeTextElements( **it );
764 0 : if ( bConcat )
765 0 : next = it;
766 : else
767 0 : ++it;
768 0 : ++next;
769 : }
770 : }
771 :
772 0 : void DrawXmlOptimizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&)
773 : {
774 0 : elem.applyToChildren(*this);
775 0 : }
776 :
777 :
778 :
779 :
780 0 : void DrawXmlFinalizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
781 : {
782 : // xxx TODO copied from DrawElement
783 0 : const GraphicsContext& rGC = m_rProcessor.getGraphicsContext(elem.GCId );
784 0 : PropertyMap aProps;
785 0 : aProps[ "style:family" ] = "graphic";
786 0 : aProps[ "style:parent-style-name" ] = "standard";
787 : // generate standard graphic style if necessary
788 0 : m_rStyleContainer.getStandardStyleId( "graphic" );
789 :
790 0 : PropertyMap aGCProps;
791 :
792 : // TODO(F3): proper dash emulation
793 0 : if( elem.Action & PATH_STROKE )
794 : {
795 0 : aGCProps[ "draw:stroke" ] = rGC.DashArray.empty() ? OUString("solid") : OUString("dash");
796 0 : aGCProps[ "svg:stroke-color" ] = getColorString( rGC.LineColor );
797 0 : if( rGC.LineWidth != 0.0 )
798 : {
799 0 : ::basegfx::B2DVector aVec(rGC.LineWidth,0);
800 0 : aVec *= rGC.Transformation;
801 :
802 0 : aVec.setX ( convPx2mmPrec2( aVec.getX() )*100.0 );
803 0 : aVec.setY ( convPx2mmPrec2( aVec.getY() )*100.0 );
804 :
805 0 : aGCProps[ "svg:stroke-width" ] = OUString::number( aVec.getLength() );
806 : }
807 0 : OUString strokeLinejoinValue;
808 0 : OUString strokeLinecapValue;
809 0 : switch (rGC.LineJoin)
810 : {
811 : default:
812 : case rendering::PathJoinType::MITER:
813 0 : strokeLinejoinValue = "miter";
814 0 : break;
815 : case rendering::PathJoinType::ROUND:
816 0 : strokeLinejoinValue = "round";
817 0 : break;
818 : case rendering::PathJoinType::BEVEL:
819 0 : strokeLinejoinValue = "bevel";
820 0 : break;
821 : }
822 0 : switch (rGC.LineCap)
823 : {
824 : default:
825 : case rendering::PathCapType::BUTT:
826 0 : strokeLinecapValue = "butt";
827 0 : 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 0 : aGCProps[ "draw:stroke-linejoin" ] = strokeLinejoinValue;
836 0 : aGCProps[ "svg:stroke-linecap" ] = strokeLinecapValue;
837 : }
838 : else
839 : {
840 0 : aGCProps[ "draw:stroke" ] = "none";
841 : }
842 :
843 : // TODO(F1): check whether stuff could be emulated by gradient/bitmap/hatch
844 0 : if( elem.Action & (PATH_FILL | PATH_EOFILL) )
845 : {
846 0 : aGCProps[ "draw:fill" ] = "solid";
847 0 : aGCProps[ "draw:fill-color" ] = getColorString( rGC.FillColor );
848 : }
849 : else
850 : {
851 0 : aGCProps[ "draw:fill" ] = "none";
852 : }
853 :
854 0 : StyleContainer::Style aStyle( "style:style", aProps );
855 0 : StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps );
856 0 : aStyle.SubStyles.push_back( &aSubStyle );
857 :
858 0 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
859 0 : }
860 :
861 0 : void DrawXmlFinalizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& )
862 : {
863 0 : }
864 :
865 0 : void DrawXmlFinalizer::visit( TextElement& elem, const std::list< Element* >::const_iterator& )
866 : {
867 0 : const FontAttributes& rFont = m_rProcessor.getFont( elem.FontId );
868 0 : PropertyMap aProps;
869 0 : aProps[ "style:family" ] = "text";
870 :
871 0 : PropertyMap aFontProps;
872 :
873 : // family name
874 0 : aFontProps[ "fo:font-family" ] = rFont.familyName;
875 0 : aFontProps[ "style:font-family-complex" ] = rFont.familyName;
876 :
877 : // bold
878 0 : 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 0 : 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 0 : 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 0 : if( rFont.isOutline )
900 : {
901 0 : aFontProps[ "style:text-outline" ] = "true";
902 : }
903 : // size
904 0 : OUStringBuffer aBuf( 32 );
905 0 : aBuf.append( rFont.size*72/PDFI_OUTDEV_RESOLUTION );
906 0 : aBuf.appendAscii( "pt" );
907 0 : OUString aFSize = aBuf.makeStringAndClear();
908 0 : aFontProps[ "fo:font-size" ] = aFSize;
909 0 : aFontProps[ "style:font-size-asian" ] = aFSize;
910 0 : aFontProps[ "style:font-size-complex" ] = aFSize;
911 :
912 : // color
913 0 : const GraphicsContext& rGC = m_rProcessor.getGraphicsContext( elem.GCId );
914 0 : aFontProps[ "fo:color" ] = getColorString( rFont.isOutline ? rGC.LineColor : rGC.FillColor );
915 :
916 : // scale
917 : double fRotate, fShearX;
918 0 : basegfx::B2DTuple aScale, aTranslation;
919 0 : rGC.Transformation.decompose(aScale, aTranslation, fRotate, fShearX);
920 0 : double textScale = -100 * aScale.getX() / aScale.getY();
921 0 : if (((textScale >= 1) && (textScale <= 99)) ||
922 0 : ((textScale >= 101) && (textScale <= 999)))
923 : {
924 0 : aBuf.append(textScale);
925 0 : aBuf.appendAscii("%");
926 0 : aFontProps[ "style:text-scale" ] = aBuf.makeStringAndClear();
927 : }
928 :
929 0 : StyleContainer::Style aStyle( "style:style", aProps );
930 0 : StyleContainer::Style aSubStyle( "style:text-properties", aFontProps );
931 0 : aStyle.SubStyles.push_back( &aSubStyle );
932 0 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
933 0 : }
934 :
935 0 : void DrawXmlFinalizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& )
936 : {
937 :
938 0 : PropertyMap aProps;
939 0 : aProps[ "style:family" ] = "paragraph";
940 : // generate standard paragraph style if necessary
941 0 : m_rStyleContainer.getStandardStyleId( "paragraph" );
942 :
943 0 : PropertyMap aParProps;
944 :
945 0 : aParProps[ "fo:text-align"] = "start";
946 0 : if (elem.bRtl)
947 0 : aParProps[ "style:writing-mode"] = "rl-tb";
948 : else
949 0 : aParProps[ "style:writing-mode"] = "lr-tb";
950 :
951 0 : StyleContainer::Style aStyle( "style:style", aProps );
952 0 : StyleContainer::Style aSubStyle( "style:paragraph-properties", aParProps );
953 0 : aStyle.SubStyles.push_back( &aSubStyle );
954 :
955 0 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
956 :
957 0 : elem.applyToChildren(*this);
958 0 : }
959 :
960 0 : void DrawXmlFinalizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator&)
961 : {
962 0 : PropertyMap aProps;
963 0 : aProps[ "style:family" ] = "graphic";
964 0 : aProps[ "style:parent-style-name" ] = "standard";
965 : // generate standard graphic style if necessary
966 0 : m_rStyleContainer.getStandardStyleId( "graphic" );
967 :
968 0 : PropertyMap aGCProps;
969 :
970 0 : aGCProps[ "draw:stroke" ] = "none";
971 0 : aGCProps[ "draw:fill" ] = "none";
972 0 : aGCProps[ "draw:auto-grow-height" ] = "true";
973 0 : aGCProps[ "draw:auto-grow-width" ] = "true";
974 0 : aGCProps[ "draw:textarea-horizontal-align" ] = "left";
975 0 : aGCProps[ "draw:textarea-vertical-align" ] = "top";
976 0 : aGCProps[ "fo:min-height"] = "0cm";
977 0 : aGCProps[ "fo:min-width"] = "0cm";
978 0 : aGCProps[ "fo:padding-top" ] = "0cm";
979 0 : aGCProps[ "fo:padding-left" ] = "0cm";
980 0 : aGCProps[ "fo:padding-right" ] = "0cm";
981 0 : aGCProps[ "fo:padding-bottom" ] = "0cm";
982 :
983 : // remark: vertical mirroring is done in current OOO by
984 : // mirroring horzontally and rotating 180 degrees
985 : // this is quaint, but unfortunately it seems
986 : // mirror=vertical is defined but not implemented in current code
987 0 : if( elem.MirrorVertical )
988 0 : aGCProps[ "style:mirror" ] = "horizontal";
989 :
990 0 : StyleContainer::Style aStyle( "style:style", aProps );
991 0 : StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps );
992 0 : aStyle.SubStyles.push_back( &aSubStyle );
993 :
994 0 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
995 0 : elem.applyToChildren(*this);
996 0 : }
997 :
998 0 : void DrawXmlFinalizer::visit( ImageElement&, const std::list< Element* >::const_iterator& )
999 : {
1000 0 : }
1001 :
1002 0 : void DrawXmlFinalizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
1003 : {
1004 0 : if( m_rProcessor.getStatusIndicator().is() )
1005 0 : m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber );
1006 :
1007 : // transform from pixel to mm
1008 0 : double page_width = convPx2mm( elem.w ), page_height = convPx2mm( elem.h );
1009 :
1010 : // calculate page margins out of the relevant children (paragraphs)
1011 0 : elem.TopMargin = elem.h, elem.BottomMargin = 0, elem.LeftMargin = elem.w, elem.RightMargin = 0;
1012 :
1013 0 : for( std::list< Element* >::const_iterator it = elem.Children.begin(); it != elem.Children.end(); ++it )
1014 : {
1015 0 : if( (*it)->x < elem.LeftMargin )
1016 0 : elem.LeftMargin = (*it)->x;
1017 0 : if( (*it)->y < elem.TopMargin )
1018 0 : elem.TopMargin = (*it)->y;
1019 0 : if( (*it)->x + (*it)->w > elem.RightMargin )
1020 0 : elem.RightMargin = ((*it)->x + (*it)->w);
1021 0 : if( (*it)->y + (*it)->h > elem.BottomMargin )
1022 0 : elem.BottomMargin = ((*it)->y + (*it)->h);
1023 : }
1024 :
1025 : // transform margins to mm
1026 0 : double left_margin = convPx2mm( elem.LeftMargin );
1027 0 : double right_margin = convPx2mm( elem.RightMargin );
1028 0 : double top_margin = convPx2mm( elem.TopMargin );
1029 0 : double bottom_margin = convPx2mm( elem.BottomMargin );
1030 :
1031 : // round left/top margin to nearest mm
1032 0 : left_margin = rtl_math_round( left_margin, 0, rtl_math_RoundingMode_Floor );
1033 0 : top_margin = rtl_math_round( top_margin, 0, rtl_math_RoundingMode_Floor );
1034 : // round (fuzzy) right/bottom margin to nearest cm
1035 0 : right_margin = rtl_math_round( right_margin, right_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor );
1036 0 : bottom_margin = rtl_math_round( bottom_margin, bottom_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor );
1037 :
1038 : // set reasonable default in case of way too large margins
1039 : // e.g. no paragraph case
1040 0 : if( left_margin > page_width/2.0 - 10 )
1041 0 : left_margin = 10;
1042 0 : if( right_margin > page_width/2.0 - 10 )
1043 0 : right_margin = 10;
1044 0 : if( top_margin > page_height/2.0 - 10 )
1045 0 : top_margin = 10;
1046 0 : if( bottom_margin > page_height/2.0 - 10 )
1047 0 : bottom_margin = 10;
1048 :
1049 : // catch the weird cases
1050 0 : if( left_margin < 0 )
1051 0 : left_margin = 0;
1052 0 : if( right_margin < 0 )
1053 0 : right_margin = 0;
1054 0 : if( top_margin < 0 )
1055 0 : top_margin = 0;
1056 0 : if( bottom_margin < 0 )
1057 0 : bottom_margin = 0;
1058 :
1059 : // widely differing margins are unlikely to be correct
1060 0 : if( right_margin > left_margin*1.5 )
1061 0 : right_margin = left_margin;
1062 :
1063 0 : elem.LeftMargin = convmm2Px( left_margin );
1064 0 : elem.RightMargin = convmm2Px( right_margin );
1065 0 : elem.TopMargin = convmm2Px( top_margin );
1066 0 : elem.BottomMargin = convmm2Px( bottom_margin );
1067 :
1068 : // get styles for paragraphs
1069 0 : PropertyMap aPageProps;
1070 0 : PropertyMap aPageLayoutProps;
1071 0 : aPageLayoutProps[ "fo:margin-top" ] = unitMMString( top_margin );
1072 0 : aPageLayoutProps[ "fo:margin-bottom" ] = unitMMString( bottom_margin );
1073 0 : aPageLayoutProps[ "fo:margin-left" ] = unitMMString( left_margin );
1074 0 : aPageLayoutProps[ "fo:margin-right" ] = unitMMString( right_margin );
1075 0 : aPageLayoutProps[ "fo:page-width" ] = unitMMString( page_width );
1076 0 : aPageLayoutProps[ "fo:page-height" ] = unitMMString( page_height );
1077 0 : aPageLayoutProps[ "style:print-orientation" ]= elem.w < elem.h ? OUString("portrait") : OUString("landscape");
1078 0 : aPageLayoutProps[ "style:writing-mode" ]= "lr-tb";
1079 :
1080 0 : StyleContainer::Style aStyle( "style:page-layout", aPageProps);
1081 0 : StyleContainer::Style aSubStyle( "style:page-layout-properties", aPageLayoutProps);
1082 0 : aStyle.SubStyles.push_back(&aSubStyle);
1083 0 : sal_Int32 nPageStyle = m_rStyleContainer.impl_getStyleId( aStyle, false );
1084 :
1085 : // create master page
1086 0 : OUString aMasterPageLayoutName = m_rStyleContainer.getStyleName( nPageStyle );
1087 0 : aPageProps[ "style:page-layout-name" ] = aMasterPageLayoutName;
1088 :
1089 0 : StyleContainer::Style aMPStyle( "style:master-page", aPageProps);
1090 :
1091 0 : StyleContainer::Style aHeaderStyle( "style:header", PropertyMap() );
1092 0 : StyleContainer::Style aFooterStyle( "style:footer", PropertyMap() );
1093 :
1094 0 : elem.StyleId = m_rStyleContainer.impl_getStyleId( aMPStyle,false );
1095 :
1096 : // create styles for children
1097 0 : elem.applyToChildren(*this);
1098 0 : }
1099 :
1100 0 : void DrawXmlFinalizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator& )
1101 : {
1102 0 : elem.applyToChildren(*this);
1103 0 : }
1104 :
1105 : }
1106 :
1107 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|