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