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