1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: drawtreevisiting.cxx,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_sdext.hxx"
35 #include "pdfiprocessor.hxx"
36 #include "xmlemitter.hxx"
37 #include "pdfihelper.hxx"
38 #include "imagecontainer.hxx"
40 #include "drawtreevisiting.hxx"
41 #include "genericelements.hxx"
43 #include <basegfx/polygon/b2dpolypolygontools.hxx>
44 #include <basegfx/range/b2drange.hxx>
50 void DrawXmlEmitter::visit( HyperlinkElement
& elem
, const std::list
< Element
* >::const_iterator
& )
52 if( elem
.Children
.empty() )
55 const char* pType
= dynamic_cast<DrawElement
*>(elem
.Children
.front()) ? "draw:a" : "text:a";
58 aProps
[ USTR( "xlink:type" ) ] = USTR( "simple" );
59 aProps
[ USTR( "xlink:href" ) ] = elem
.URI
;
60 aProps
[ USTR( "office:target-frame-name" ) ] = USTR( "_blank" );
61 aProps
[ USTR( "xlink:show" ) ] = USTR( "new" );
63 m_rEmitContext
.rEmitter
.beginTag( pType
, aProps
);
64 std::list
< Element
* >::iterator this_it
= elem
.Children
.begin();
65 while( this_it
!=elem
.Children
.end() && *this_it
!= &elem
)
67 (*this_it
)->visitedBy( *this, this_it
);
70 m_rEmitContext
.rEmitter
.endTag( pType
);
73 void DrawXmlEmitter::visit( TextElement
& elem
, const std::list
< Element
* >::const_iterator
& )
75 if( ! elem
.Text
.getLength() )
78 rtl::OUString
strSpace(32);
79 rtl::OUString
tabSpace(0x09);
81 if( elem
.StyleId
!= -1 )
83 aProps
[ rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "text:style-name" ) ) ] =
84 m_rEmitContext
.rStyles
.getStyleName( elem
.StyleId
);
87 m_rEmitContext
.rEmitter
.beginTag( "text:span", aProps
);
89 rtl::OUString
str(elem
.Text
.getStr());
90 for(int i
=0; i
< elem
.Text
.getLength(); i
++)
92 rtl::OUString strToken
= str
.copy(i
,1) ;
93 if( strSpace
.equals(strToken
) )
95 aProps
[ USTR( "text:c" ) ] = USTR( "1" );
96 m_rEmitContext
.rEmitter
.beginTag( "text:s", aProps
);
97 m_rEmitContext
.rEmitter
.endTag( "text:s");
102 if( tabSpace
.equals(strToken
) )
105 m_rEmitContext
.rEmitter
.beginTag( "text:tab", aProps
);
106 m_rEmitContext
.rEmitter
.endTag( "text:tab");
111 m_rEmitContext
.rEmitter
.write( strToken
);
116 std::list
< Element
* >::iterator this_it
= elem
.Children
.begin();
117 while( this_it
!=elem
.Children
.end() && *this_it
!= &elem
)
119 (*this_it
)->visitedBy( *this, this_it
);
123 m_rEmitContext
.rEmitter
.endTag( "text:span" );
126 void DrawXmlEmitter::visit( ParagraphElement
& elem
, const std::list
< Element
* >::const_iterator
& )
129 if( elem
.StyleId
!= -1 )
131 aProps
[ USTR( "text:style-name" ) ] = m_rEmitContext
.rStyles
.getStyleName( elem
.StyleId
);
133 const char* pTagType
= "text:p";
134 if( elem
.Type
== elem
.Headline
)
136 m_rEmitContext
.rEmitter
.beginTag( pTagType
, aProps
);
138 std::list
< Element
* >::iterator this_it
= elem
.Children
.begin();
139 while( this_it
!=elem
.Children
.end() && *this_it
!= &elem
)
141 (*this_it
)->visitedBy( *this, this_it
);
145 m_rEmitContext
.rEmitter
.endTag( pTagType
);
148 void DrawXmlEmitter::fillFrameProps( DrawElement
& rElem
,
150 const EmitContext
& rEmitContext
)
152 double rel_x
= rElem
.x
, rel_y
= rElem
.y
;
154 rProps
[ USTR( "draw:z-index" ) ] = rtl::OUString::valueOf( rElem
.ZOrder
);
155 rProps
[ USTR( "draw:style-name" )] = rEmitContext
.rStyles
.getStyleName( rElem
.StyleId
);
156 rProps
[ USTR( "svg:width" ) ] = convertPixelToUnitString( rElem
.w
);
157 rProps
[ USTR( "svg:height" ) ] = convertPixelToUnitString( rElem
.h
);
159 const GraphicsContext
& rGC
=
160 rEmitContext
.rProcessor
.getGraphicsContext( rElem
.GCId
);
161 if( rGC
.Transformation
.isIdentity() )
163 rProps
[ USTR( "svg:x" ) ] = convertPixelToUnitString( rel_x
);
164 rProps
[ USTR( "svg:y" ) ] = convertPixelToUnitString( rel_y
);
168 basegfx::B2DTuple aScale
, aTranslation
;
169 double fRotate
, fShearX
;
171 rGC
.Transformation
.decompose( aScale
, aTranslation
, fRotate
, fShearX
);
173 rtl::OUStringBuffer
aBuf( 256 );
175 // TODO(F2): general transformation case missing; if implemented, note
176 // that ODF rotation is oriented the other way
178 // vertical mirroring is done by horizontally mirroring and rotaing 180 degree
180 if( rElem
.MirrorVertical
)
183 // build transformation string
186 aBuf
.appendAscii( "skewX( " );
187 aBuf
.append( fShearX
);
188 aBuf
.appendAscii( " )" );
192 if( aBuf
.getLength() > 0 )
193 aBuf
.append( sal_Unicode(' ') );
194 aBuf
.appendAscii( "rotate( " );
195 aBuf
.append( -fRotate
);
196 aBuf
.appendAscii( " )" );
199 if( aBuf
.getLength() > 0 )
200 aBuf
.append( sal_Unicode(' ') );
201 aBuf
.appendAscii( "translate( " );
202 aBuf
.append( convertPixelToUnitString( rel_x
) );
203 aBuf
.append( sal_Unicode(' ') );
204 aBuf
.append( convertPixelToUnitString( rel_y
) );
205 aBuf
.appendAscii( " )" );
207 rProps
[ USTR( "draw:transform" ) ] = aBuf
.makeStringAndClear();
211 void DrawXmlEmitter::visit( FrameElement
& elem
, const std::list
< Element
* >::const_iterator
& )
213 if( elem
.Children
.empty() )
216 bool bTextBox
= (dynamic_cast<ParagraphElement
*>(elem
.Children
.front()) != NULL
);
217 PropertyMap aFrameProps
;
218 fillFrameProps( elem
, aFrameProps
, m_rEmitContext
);
219 m_rEmitContext
.rEmitter
.beginTag( "draw:frame", aFrameProps
);
221 m_rEmitContext
.rEmitter
.beginTag( "draw:text-box", PropertyMap() );
223 std::list
< Element
* >::iterator this_it
= elem
.Children
.begin();
224 while( this_it
!=elem
.Children
.end() && *this_it
!= &elem
)
226 (*this_it
)->visitedBy( *this, this_it
);
231 m_rEmitContext
.rEmitter
.endTag( "draw:text-box" );
232 m_rEmitContext
.rEmitter
.endTag( "draw:frame" );
235 void DrawXmlEmitter::visit( PolyPolyElement
& elem
, const std::list
< Element
* >::const_iterator
& )
237 elem
.updateGeometry();
239 * aw recommends using 100dth of mm in all respects since the xml import
240 * (a) is buggy (see issue 37213)
241 * (b) is optimized for 100dth of mm and does not scale itself then,
242 * this does not gain us speed but makes for smaller rounding errors since
243 * the xml importer coordinates are integer based
245 for (sal_uInt32 i
= 0; i
< elem
.PolyPoly
.count(); i
++)
247 basegfx::B2DPolygon b2dPolygon
;
248 b2dPolygon
= elem
.PolyPoly
.getB2DPolygon( i
);
250 for ( sal_uInt32 j
= 0; j
< b2dPolygon
.count(); j
++ )
252 basegfx::B2DPoint point
;
253 basegfx::B2DPoint nextPoint
;
254 point
= b2dPolygon
.getB2DPoint( j
);
256 basegfx::B2DPoint prevPoint
;
257 prevPoint
= b2dPolygon
.getPrevControlPoint( j
) ;
259 point
.setX( convPx2mmPrec2( point
.getX() )*100.0 );
260 point
.setY( convPx2mmPrec2( point
.getY() )*100.0 );
262 if ( b2dPolygon
.isPrevControlPointUsed( j
) )
264 prevPoint
.setX( convPx2mmPrec2( prevPoint
.getX() )*100.0 );
265 prevPoint
.setY( convPx2mmPrec2( prevPoint
.getY() )*100.0 );
268 if ( b2dPolygon
.isNextControlPointUsed( j
) )
270 nextPoint
= b2dPolygon
.getNextControlPoint( j
) ;
271 nextPoint
.setX( convPx2mmPrec2( nextPoint
.getX() )*100.0 );
272 nextPoint
.setY( convPx2mmPrec2( nextPoint
.getY() )*100.0 );
275 b2dPolygon
.setB2DPoint( j
, point
);
277 if ( b2dPolygon
.isPrevControlPointUsed( j
) )
278 b2dPolygon
.setPrevControlPoint( j
, prevPoint
) ;
280 if ( b2dPolygon
.isNextControlPointUsed( j
) )
281 b2dPolygon
.setNextControlPoint( j
, nextPoint
) ;
284 elem
.PolyPoly
.setB2DPolygon( i
, b2dPolygon
);
288 fillFrameProps( elem
, aProps
, m_rEmitContext
);
289 rtl::OUStringBuffer
aBuf( 64 );
290 aBuf
.appendAscii( "0 0 " );
291 aBuf
.append( convPx2mmPrec2(elem
.w
)*100.0 );
292 aBuf
.append( sal_Unicode(' ') );
293 aBuf
.append( convPx2mmPrec2(elem
.h
)*100.0 );
294 aProps
[ USTR( "svg:viewBox" ) ] = aBuf
.makeStringAndClear();
295 aProps
[ USTR( "svg:d" ) ] = basegfx::tools::exportToSvgD( elem
.PolyPoly
);
297 m_rEmitContext
.rEmitter
.beginTag( "draw:path", aProps
);
298 m_rEmitContext
.rEmitter
.endTag( "draw:path" );
301 void DrawXmlEmitter::visit( ImageElement
& elem
, const std::list
< Element
* >::const_iterator
& )
303 PropertyMap aImageProps
;
304 m_rEmitContext
.rEmitter
.beginTag( "draw:image", aImageProps
);
305 m_rEmitContext
.rEmitter
.beginTag( "office:binary-data", PropertyMap() );
306 m_rEmitContext
.rImages
.writeBase64EncodedStream( elem
.Image
, m_rEmitContext
);
307 m_rEmitContext
.rEmitter
.endTag( "office:binary-data" );
308 m_rEmitContext
.rEmitter
.endTag( "draw:image" );
311 void DrawXmlEmitter::visit( PageElement
& elem
, const std::list
< Element
* >::const_iterator
& )
313 PropertyMap aPageProps
;
314 aPageProps
[ USTR( "draw:master-page-name" ) ] = m_rEmitContext
.rStyles
.getStyleName( elem
.StyleId
);
316 m_rEmitContext
.rEmitter
.beginTag("draw:page", aPageProps
);
318 if( m_rEmitContext
.xStatusIndicator
.is() )
319 m_rEmitContext
.xStatusIndicator
->setValue( elem
.PageNumber
);
321 std::list
< Element
* >::iterator this_it
= elem
.Children
.begin();
322 while( this_it
!=elem
.Children
.end() && *this_it
!= &elem
)
324 (*this_it
)->visitedBy( *this, this_it
);
328 m_rEmitContext
.rEmitter
.endTag("draw:page");
331 void DrawXmlEmitter::visit( DocumentElement
& elem
, const std::list
< Element
* >::const_iterator
&)
333 m_rEmitContext
.rEmitter
.beginTag( "office:body", PropertyMap() );
334 m_rEmitContext
.rEmitter
.beginTag( m_bWriteDrawDocument
? "office:drawing" : "office:presentation",
337 std::list
< Element
* >::iterator this_it
= elem
.Children
.begin();
338 while( this_it
!=elem
.Children
.end() && *this_it
!= &elem
)
340 (*this_it
)->visitedBy( *this, this_it
);
344 m_rEmitContext
.rEmitter
.endTag( m_bWriteDrawDocument
? "office:drawing" : "office:presentation" );
345 m_rEmitContext
.rEmitter
.endTag( "office:body" );
348 /////////////////////////////////////////////////////////////////
350 void DrawXmlOptimizer::visit( HyperlinkElement
&, const std::list
< Element
* >::const_iterator
& )
354 void DrawXmlOptimizer::visit( TextElement
&, const std::list
< Element
* >::const_iterator
&)
358 void DrawXmlOptimizer::visit( FrameElement
& elem
, const std::list
< Element
* >::const_iterator
& )
360 elem
.applyToChildren(*this);
363 void DrawXmlOptimizer::visit( ImageElement
&, const std::list
< Element
* >::const_iterator
& )
367 void DrawXmlOptimizer::visit( PolyPolyElement
& elem
, const std::list
< Element
* >::const_iterator
& )
369 /* note: optimize two consecutive PolyPolyElements that
370 * have the same path but one of which is a stroke while
371 * the other is a fill
375 // find following PolyPolyElement in parent's children list
376 std::list
< Element
* >::iterator this_it
= elem
.Parent
->Children
.begin();
377 while( this_it
!= elem
.Parent
->Children
.end() && *this_it
!= &elem
)
380 if( this_it
!= elem
.Parent
->Children
.end() )
382 std::list
< Element
* >::iterator next_it
= this_it
;
383 if( ++next_it
!= elem
.Parent
->Children
.end() )
385 PolyPolyElement
* pNext
= dynamic_cast<PolyPolyElement
*>(*next_it
);
387 // TODO(F2): this comparison fails for OOo-generated polygons with beziers.
388 if( pNext
&& pNext
->PolyPoly
== elem
.PolyPoly
)
390 const GraphicsContext
& rNextGC
=
391 m_rProcessor
.getGraphicsContext( pNext
->GCId
);
392 const GraphicsContext
& rThisGC
=
393 m_rProcessor
.getGraphicsContext( elem
.GCId
);
395 if( rThisGC
.BlendMode
== rNextGC
.BlendMode
&&
396 rThisGC
.Flatness
== rNextGC
.Flatness
&&
397 rThisGC
.Transformation
== rNextGC
.Transformation
&&
398 rThisGC
.Clip
== rNextGC
.Clip
&&
399 rThisGC
.FillColor
.Red
== rNextGC
.FillColor
.Red
&&
400 rThisGC
.FillColor
.Green
== rNextGC
.FillColor
.Green
&&
401 rThisGC
.FillColor
.Blue
== rNextGC
.FillColor
.Blue
&&
402 rThisGC
.FillColor
.Alpha
== rNextGC
.FillColor
.Alpha
&&
403 pNext
->Action
== PATH_STROKE
&&
404 (elem
.Action
== PATH_FILL
|| elem
.Action
== PATH_EOFILL
) )
406 GraphicsContext aGC
= rThisGC
;
407 aGC
.LineJoin
= rNextGC
.LineJoin
;
408 aGC
.LineCap
= rNextGC
.LineCap
;
409 aGC
.LineWidth
= rNextGC
.LineWidth
;
410 aGC
.MiterLimit
= rNextGC
.MiterLimit
;
411 aGC
.DashArray
= rNextGC
.DashArray
;
412 aGC
.LineColor
= rNextGC
.LineColor
;
413 elem
.GCId
= m_rProcessor
.getGCId( aGC
);
415 elem
.Action
|= pNext
->Action
;
417 elem
.Children
.splice( elem
.Children
.end(), pNext
->Children
);
418 elem
.Parent
->Children
.erase( next_it
);
427 void DrawXmlOptimizer::visit( ParagraphElement
& elem
, const std::list
< Element
* >::const_iterator
& )
429 optimizeTextElements( elem
);
431 elem
.applyToChildren(*this);
434 void DrawXmlOptimizer::visit( PageElement
& elem
, const std::list
< Element
* >::const_iterator
& )
436 if( m_rProcessor
.getStatusIndicator().is() )
437 m_rProcessor
.getStatusIndicator()->setValue( elem
.PageNumber
);
439 // resolve hyperlinks
440 elem
.resolveHyperlinks();
442 elem
.resolveFontStyles( m_rProcessor
); // underlines and such
444 // FIXME: until hyperlinks and font effects are adjusted for
445 // geometrical search handle them before sorting
446 m_rProcessor
.sortElements( &elem
);
448 // find paragraphs in text
449 ParagraphElement
* pCurPara
= NULL
;
450 std::list
< Element
* >::iterator page_element
, next_page_element
;
451 next_page_element
= elem
.Children
.begin();
452 double fCurLineHeight
= 0.0; // average height of text items in current para
453 int nCurLineElements
= 0; // number of line contributing elements in current para
454 double line_left
= elem
.w
, line_right
= 0.0;
455 double column_width
= elem
.w
*0.75; // estimate text width
456 // TODO: guess columns
457 while( next_page_element
!= elem
.Children
.end() )
459 page_element
= next_page_element
++;
460 ParagraphElement
* pPagePara
= dynamic_cast<ParagraphElement
*>(*page_element
);
463 pCurPara
= pPagePara
;
464 // adjust line height and text items
465 fCurLineHeight
= 0.0;
466 nCurLineElements
= 0;
467 for( std::list
< Element
* >::iterator it
= pCurPara
->Children
.begin();
468 it
!= pCurPara
->Children
.end(); ++it
)
470 TextElement
* pTestText
= dynamic_cast<TextElement
*>(*it
);
473 fCurLineHeight
= (fCurLineHeight
*double(nCurLineElements
) + pTestText
->h
)/double(nCurLineElements
+1);
480 HyperlinkElement
* pLink
= dynamic_cast<HyperlinkElement
*>(*page_element
);
481 DrawElement
* pDraw
= dynamic_cast<DrawElement
*>(*page_element
);
482 if( ! pDraw
&& pLink
&& ! pLink
->Children
.empty() )
483 pDraw
= dynamic_cast<DrawElement
*>(pLink
->Children
.front() );
486 // insert small drawing objects as character, else leave them page bound
488 bool bInsertToParagraph
= false;
489 // first check if this is either inside the paragraph
490 if( pCurPara
&& pDraw
->y
< pCurPara
->y
+ pCurPara
->h
)
492 if( pDraw
->h
< fCurLineHeight
* 1.5 )
494 bInsertToParagraph
= true;
495 fCurLineHeight
= (fCurLineHeight
*double(nCurLineElements
) + pDraw
->h
)/double(nCurLineElements
+1);
497 // mark draw element as character
498 pDraw
->isCharacter
= true;
501 // or perhaps the draw element begins a new paragraph
502 else if( next_page_element
!= elem
.Children
.end() )
504 TextElement
* pText
= dynamic_cast<TextElement
*>(*next_page_element
);
507 ParagraphElement
* pPara
= dynamic_cast<ParagraphElement
*>(*next_page_element
);
508 if( pPara
&& ! pPara
->Children
.empty() )
509 pText
= dynamic_cast<TextElement
*>(pPara
->Children
.front());
511 if( pText
&& // check there is a text
512 pDraw
->h
< pText
->h
*1.5 && // and it is approx the same height
513 // and either upper or lower edge of pDraw is inside text's vertical range
514 ( ( pDraw
->y
>= pText
->y
&& pDraw
->y
<= pText
->y
+pText
->h
) ||
515 ( pDraw
->y
+pDraw
->h
>= pText
->y
&& pDraw
->y
+pDraw
->h
<= pText
->y
+pText
->h
)
519 bInsertToParagraph
= true;
520 fCurLineHeight
= pDraw
->h
;
521 nCurLineElements
= 1;
522 line_left
= pDraw
->x
;
523 line_right
= pDraw
->x
+ pDraw
->w
;
524 // begin a new paragraph
526 // mark draw element as character
527 pDraw
->isCharacter
= true;
531 if( ! bInsertToParagraph
)
538 TextElement
* pText
= dynamic_cast<TextElement
*>(*page_element
);
539 if( ! pText
&& pLink
&& ! pLink
->Children
.empty() )
540 pText
= dynamic_cast<TextElement
*>(pLink
->Children
.front());
543 Element
* pGeo
= pLink
? static_cast<Element
*>(pLink
) :
544 static_cast<Element
*>(pText
);
547 // there was already a text element, check for a new paragraph
548 if( nCurLineElements
> 0 )
550 // if the new text is significantly distant from the paragraph
551 // begin a new paragraph
552 if( pGeo
->y
> pCurPara
->y
+ pCurPara
->h
+ fCurLineHeight
*0.5 )
553 pCurPara
= NULL
; // insert new paragraph
554 else if( pGeo
->y
> (pCurPara
->y
+pCurPara
->h
- fCurLineHeight
*0.05) )
556 // new paragraph if either the last line of the paragraph
557 // was significantly shorter than the paragraph as a whole
558 if( (line_right
- line_left
) < pCurPara
->w
*0.75 )
560 // or the last line was significantly smaller than the column width
561 else if( (line_right
- line_left
) < column_width
*0.75 )
570 // update line height/width
573 fCurLineHeight
= (fCurLineHeight
*double(nCurLineElements
) + pGeo
->h
)/double(nCurLineElements
+1);
575 if( pGeo
->x
< line_left
)
577 if( pGeo
->x
+pGeo
->w
> line_right
)
578 line_right
= pGeo
->x
+pGeo
->w
;
582 fCurLineHeight
= pGeo
->h
;
583 nCurLineElements
= 1;
585 line_right
= pGeo
->x
+ pGeo
->w
;
590 // move element to current paragraph
591 if (! pCurPara
) // new paragraph, insert one
593 pCurPara
= m_rProcessor
.getElementFactory()->createParagraphElement( NULL
);
595 pCurPara
->Parent
= &elem
;
596 //insert new paragraph before current element
597 page_element
= elem
.Children
.insert( page_element
, pCurPara
);
598 // forward iterator to current element again
600 // update next_element which is now invalid
601 next_page_element
= page_element
;
602 ++ next_page_element
;
604 Element
* pCurEle
= *page_element
;
605 pCurEle
->setParent( page_element
, pCurPara
);
606 OSL_ENSURE( !pText
|| pCurEle
== pText
|| pCurEle
== pLink
, "paragraph child list in disorder" );
608 pCurPara
->updateGeometryWith( pCurEle
);
612 elem
.applyToChildren(*this);
615 void DrawXmlOptimizer::optimizeTextElements(Element
& rParent
)
617 if( rParent
.Children
.empty() ) // this should not happen
619 OSL_ENSURE( 0, "empty paragraph optimized" );
623 bool bFirstTime
= true;
626 // concatenate child elements with same font id
627 std::list
< Element
* >::iterator next
= rParent
.Children
.begin();
628 std::list
< Element
* >::iterator it
= next
++;
629 FrameElement
* pFrame
= dynamic_cast<FrameElement
*>(rParent
.Parent
);
630 bool bRotatedFrame
= false;
633 const GraphicsContext
& rFrameGC
= m_rProcessor
.getGraphicsContext( pFrame
->GCId
);
634 if( rFrameGC
.isRotatedOrSkewed() )
635 bRotatedFrame
= true;
637 while( next
!= rParent
.Children
.end() )
639 bool bConcat
= false;
640 TextElement
* pCur
= dynamic_cast<TextElement
*>(*it
);
649 TextElement
* pNext
= dynamic_cast<TextElement
*>(*next
);
652 const GraphicsContext
& rCurGC
= m_rProcessor
.getGraphicsContext( pCur
->GCId
);
653 const GraphicsContext
& rNextGC
= m_rProcessor
.getGraphicsContext( pNext
->GCId
);
655 // line and space optimization; works only in strictly horizontal mode
658 // concatenate consecutive text elements unless there is a
659 // font or text color or matrix change, leave a new span in that case
660 if( pCur
->FontId
== pNext
->FontId
&&
661 rCurGC
.FillColor
.Red
== rNextGC
.FillColor
.Red
&&
662 rCurGC
.FillColor
.Green
== rNextGC
.FillColor
.Green
&&
663 rCurGC
.FillColor
.Blue
== rNextGC
.FillColor
.Blue
&&
664 rCurGC
.FillColor
.Alpha
== rNextGC
.FillColor
.Alpha
&&
665 rCurGC
.Transformation
== rNextGC
.Transformation
668 pCur
->updateGeometryWith( pNext
);
669 // append text to current element
670 pCur
->Text
.append( pNext
->Text
.getStr(), pNext
->Text
.getLength() );
671 // append eventual children to current element
672 // and clear children (else the children just
673 // appended to pCur would be destroyed)
674 pCur
->Children
.splice( pCur
->Children
.end(), pNext
->Children
);
675 // get rid of the now useless element
676 rParent
.Children
.erase( next
);
682 else if( dynamic_cast<HyperlinkElement
*>(*it
) )
683 optimizeTextElements( **it
);
697 void DrawXmlOptimizer::visit( DocumentElement
& elem
, const std::list
< Element
* >::const_iterator
&)
699 elem
.applyToChildren(*this);
702 //////////////////////////////////////////////////////////////////////////////////
705 void DrawXmlFinalizer::visit( PolyPolyElement
& elem
, const std::list
< Element
* >::const_iterator
& )
707 // xxx TODO copied from DrawElement
708 const GraphicsContext
& rGC
= m_rProcessor
.getGraphicsContext(elem
.GCId
);
710 aProps
[ USTR( "style:family" ) ] = USTR( "graphic" );
711 aProps
[ USTR( "style:parent-style-name") ] = USTR( "standard" );
712 // generate standard graphic style if necessary
713 m_rStyleContainer
.getStandardStyleId( "graphic" );
715 PropertyMap aGCProps
;
717 // TODO(F3): proper dash emulation
718 if( elem
.Action
& PATH_STROKE
)
720 aGCProps
[ USTR("draw:stroke") ] = rGC
.DashArray
.empty() ? USTR("solid") : USTR("dash");
721 aGCProps
[ USTR("svg:stroke-color") ] = getColorString( rGC
.LineColor
);
722 if( rGC
.LineWidth
!= 0.0 )
724 ::basegfx::B2DVector
aVec(rGC
.LineWidth
,0);
725 aVec
*= rGC
.Transformation
;
727 aVec
.setX ( convPx2mmPrec2( aVec
.getX() )*100.0 );
728 aVec
.setY ( convPx2mmPrec2( aVec
.getY() )*100.0 );
730 aGCProps
[ USTR("svg:stroke-width") ] = rtl::OUString::valueOf( aVec
.getLength() );
735 aGCProps
[ USTR("draw:stroke") ] = USTR("none");
738 // TODO(F1): check whether stuff could be emulated by gradient/bitmap/hatch
739 if( elem
.Action
& (PATH_FILL
| PATH_EOFILL
) )
741 aGCProps
[ USTR("draw:fill") ] = USTR("solid");
742 aGCProps
[ USTR("draw:fill-color") ] = getColorString( rGC
.FillColor
);
746 aGCProps
[ USTR("draw:fill") ] = USTR("none");
749 StyleContainer::Style
aStyle( "style:style", aProps
);
750 StyleContainer::Style
aSubStyle( "style:graphic-properties", aGCProps
);
751 aStyle
.SubStyles
.push_back( &aSubStyle
);
753 elem
.StyleId
= m_rStyleContainer
.getStyleId( aStyle
);
756 void DrawXmlFinalizer::visit( HyperlinkElement
&, const std::list
< Element
* >::const_iterator
& )
760 void DrawXmlFinalizer::visit( TextElement
& elem
, const std::list
< Element
* >::const_iterator
& )
762 const FontAttributes
& rFont
= m_rProcessor
.getFont( elem
.FontId
);
764 aProps
[ USTR( "style:family" ) ] = USTR( "text" );
766 PropertyMap aFontProps
;
769 aFontProps
[ USTR( "fo:font-family" ) ] = rFont
.familyName
;
773 aFontProps
[ USTR( "fo:font-weight" ) ] = USTR( "bold" );
774 aFontProps
[ USTR( "fo:font-weight-asian" ) ] = USTR( "bold" );
775 aFontProps
[ USTR( "fo:font-weight-complex" ) ] = USTR( "bold" );
780 aFontProps
[ USTR( "fo:font-style" ) ] = USTR( "italic" );
781 aFontProps
[ USTR( "fo:font-style-asian" ) ] = USTR( "italic" );
782 aFontProps
[ USTR( "fo:font-style-complex" ) ] = USTR( "italic" );
785 if( rFont
.isUnderline
)
787 aFontProps
[ USTR( "style:text-underline-style" ) ] = USTR( "solid" );
788 aFontProps
[ USTR( "style:text-underline-width" ) ] = USTR( "auto" );
789 aFontProps
[ USTR( "style:text-underline-color" ) ] = USTR( "font-color" );
792 if( rFont
.isOutline
)
794 aFontProps
[ USTR( "style:text-outline" ) ] = USTR( "true" );
797 rtl::OUStringBuffer
aBuf( 32 );
798 aBuf
.append( rFont
.size
*72/PDFI_OUTDEV_RESOLUTION
);
799 aBuf
.appendAscii( "pt" );
800 rtl::OUString aFSize
= aBuf
.makeStringAndClear();
801 aFontProps
[ USTR( "fo:font-size" ) ] = aFSize
;
802 aFontProps
[ USTR( "style:font-size-asian" ) ] = aFSize
;
803 aFontProps
[ USTR( "style:font-size-complex" ) ] = aFSize
;
805 const GraphicsContext
& rGC
= m_rProcessor
.getGraphicsContext( elem
.GCId
);
806 aFontProps
[ USTR( "fo:color" ) ] = getColorString( rFont
.isOutline
? rGC
.LineColor
: rGC
.FillColor
);
808 StyleContainer::Style
aStyle( "style:style", aProps
);
809 StyleContainer::Style
aSubStyle( "style:text-properties", aFontProps
);
810 aStyle
.SubStyles
.push_back( &aSubStyle
);
811 elem
.StyleId
= m_rStyleContainer
.getStyleId( aStyle
);
814 void DrawXmlFinalizer::visit( ParagraphElement
& elem
, const std::list
< Element
* >::const_iterator
& )
816 // update page boundaries
819 // check for center alignement
820 // criterion: paragraph is small relative to parent and distributed around its center
821 double p_x
= elem
.Parent
->x
;
822 double p_y
= elem
.Parent
->y
;
823 double p_w
= elem
.Parent
->w
;
824 double p_h
= elem
.Parent
->h
;
826 PageElement
* pPage
= dynamic_cast<PageElement
*>(elem
.Parent
);
829 p_x
+= pPage
->LeftMargin
;
830 p_y
+= pPage
->TopMargin
;
831 p_w
-= pPage
->LeftMargin
+pPage
->RightMargin
;
832 p_h
-= pPage
->TopMargin
+pPage
->BottomMargin
;
836 elem
.applyToChildren(*this);
839 void DrawXmlFinalizer::visit( FrameElement
& elem
, const std::list
< Element
* >::const_iterator
&)
842 aProps
[ USTR( "style:family" ) ] = USTR( "graphic" );
843 aProps
[ USTR( "style:parent-style-name") ] = USTR( "standard" );
844 // generate standard graphic style if necessary
845 m_rStyleContainer
.getStandardStyleId( "graphic" );
847 PropertyMap aGCProps
;
849 aGCProps
[ USTR("draw:stroke") ] = USTR("none");
850 aGCProps
[ USTR("draw:fill") ] = USTR("none");
851 aGCProps
[ USTR("draw:auto-grow-height") ] = USTR("true");
852 aGCProps
[ USTR("draw:auto-grow-width") ] = USTR("true");
853 aGCProps
[ USTR("draw:textarea-horizontal-align") ] = USTR("left");
854 aGCProps
[ USTR("draw:textarea-vertical-align") ] = USTR("top");
855 aGCProps
[ USTR("fo:min-height")] = USTR("0cm");
856 aGCProps
[ USTR("fo:min-width")] = USTR("0cm");
857 aGCProps
[ USTR("fo:padding-top") ] = USTR("0cm");
858 aGCProps
[ USTR("fo:padding-left") ] = USTR("0cm");
859 aGCProps
[ USTR("fo:padding-right") ] = USTR("0cm");
860 aGCProps
[ USTR("fo:padding-bottom") ] = USTR("0cm");
862 // remark: vertical mirroring is done in current OOO by
863 // mirroring horzontally and rotating 180 degrees
864 // this is quaint, but unfortunately it seems
865 // mirror=vertical is defined but not implemented in current code
866 if( elem
.MirrorVertical
)
867 aGCProps
[ USTR("style:mirror") ] = USTR("horizontal");
869 StyleContainer::Style
aStyle( "style:style", aProps
);
870 StyleContainer::Style
aSubStyle( "style:graphic-properties", aGCProps
);
871 aStyle
.SubStyles
.push_back( &aSubStyle
);
873 elem
.StyleId
= m_rStyleContainer
.getStyleId( aStyle
);
874 elem
.applyToChildren(*this);
877 void DrawXmlFinalizer::visit( ImageElement
&, const std::list
< Element
* >::const_iterator
& )
881 void DrawXmlFinalizer::visit( PageElement
& elem
, const std::list
< Element
* >::const_iterator
& )
883 if( m_rProcessor
.getStatusIndicator().is() )
884 m_rProcessor
.getStatusIndicator()->setValue( elem
.PageNumber
);
886 // transform from pixel to mm
887 double page_width
= convPx2mm( elem
.w
), page_height
= convPx2mm( elem
.h
);
889 // calculate page margins out of the relevant children (paragraphs)
890 elem
.TopMargin
= elem
.h
, elem
.BottomMargin
= 0, elem
.LeftMargin
= elem
.w
, elem
.RightMargin
= 0;
892 for( std::list
< Element
* >::const_iterator it
= elem
.Children
.begin(); it
!= elem
.Children
.end(); ++it
)
894 if( (*it
)->x
< elem
.LeftMargin
)
895 elem
.LeftMargin
= (*it
)->x
;
896 if( (*it
)->y
< elem
.TopMargin
)
897 elem
.TopMargin
= (*it
)->y
;
898 if( (*it
)->x
+ (*it
)->w
> elem
.RightMargin
)
899 elem
.RightMargin
= ((*it
)->x
+ (*it
)->w
);
900 if( (*it
)->y
+ (*it
)->h
> elem
.BottomMargin
)
901 elem
.BottomMargin
= ((*it
)->y
+ (*it
)->h
);
904 // transform margins to mm
905 double left_margin
= convPx2mm( elem
.LeftMargin
);
906 double right_margin
= convPx2mm( elem
.RightMargin
);
907 double top_margin
= convPx2mm( elem
.TopMargin
);
908 double bottom_margin
= convPx2mm( elem
.BottomMargin
);
910 // round left/top margin to nearest mm
911 left_margin
= rtl_math_round( left_margin
, 0, rtl_math_RoundingMode_Floor
);
912 top_margin
= rtl_math_round( top_margin
, 0, rtl_math_RoundingMode_Floor
);
913 // round (fuzzy) right/bottom margin to nearest cm
914 right_margin
= rtl_math_round( right_margin
, right_margin
>= 10 ? -1 : 0, rtl_math_RoundingMode_Floor
);
915 bottom_margin
= rtl_math_round( bottom_margin
, bottom_margin
>= 10 ? -1 : 0, rtl_math_RoundingMode_Floor
);
917 // set reasonable default in case of way too large margins
918 // e.g. no paragraph case
919 if( left_margin
> page_width
/2.0 - 10 )
921 if( right_margin
> page_width
/2.0 - 10 )
923 if( top_margin
> page_height
/2.0 - 10 )
925 if( bottom_margin
> page_height
/2.0 - 10 )
928 // catch the weird cases
929 if( left_margin
< 0 )
931 if( right_margin
< 0 )
935 if( bottom_margin
< 0 )
938 // widely differing margins are unlikely to be correct
939 if( right_margin
> left_margin
*1.5 )
940 right_margin
= left_margin
;
942 elem
.LeftMargin
= convmm2Px( left_margin
);
943 elem
.RightMargin
= convmm2Px( right_margin
);
944 elem
.TopMargin
= convmm2Px( top_margin
);
945 elem
.BottomMargin
= convmm2Px( bottom_margin
);
947 // get styles for paragraphs
948 PropertyMap aPageProps
;
949 PropertyMap aPageLayoutProps
;
950 rtl::OUStringBuffer
aBuf( 64 );
951 aPageLayoutProps
[ USTR( "fo:margin-top" ) ] = unitMMString( top_margin
);
952 aPageLayoutProps
[ USTR( "fo:margin-bottom" ) ] = unitMMString( bottom_margin
);
953 aPageLayoutProps
[ USTR( "fo:margin-left" ) ] = unitMMString( left_margin
);
954 aPageLayoutProps
[ USTR( "fo:margin-right" ) ] = unitMMString( right_margin
);
955 aPageLayoutProps
[ USTR( "fo:page-width" ) ] = unitMMString( page_width
);
956 aPageLayoutProps
[ USTR( "fo:page-height" ) ] = unitMMString( page_height
);
957 aPageLayoutProps
[ USTR( "style:print-orientation" ) ]= elem
.w
< elem
.h
? USTR( "portrait" ) : USTR( "landscape" );
958 aPageLayoutProps
[ USTR( "style:writing-mode" ) ]= USTR( "lr-tb" );
960 StyleContainer::Style
aStyle( "style:page-layout", aPageProps
);
961 StyleContainer::Style
aSubStyle( "style:page-layout-properties", aPageLayoutProps
);
962 aStyle
.SubStyles
.push_back(&aSubStyle
);
963 sal_Int32 nPageStyle
= m_rStyleContainer
.impl_getStyleId( aStyle
, false );
965 // create master page
966 rtl::OUString aMasterPageLayoutName
= m_rStyleContainer
.getStyleName( nPageStyle
);
967 aPageProps
[ USTR( "style:page-layout-name" ) ] = aMasterPageLayoutName
;
969 StyleContainer::Style
aMPStyle( "style:master-page", aPageProps
);
971 StyleContainer::Style
aHeaderStyle( "style:header", PropertyMap() );
972 StyleContainer::Style
aFooterStyle( "style:footer", PropertyMap() );
974 elem
.StyleId
= m_rStyleContainer
.impl_getStyleId( aMPStyle
,false );
977 rtl::OUString aMasterPageName
= m_rStyleContainer
.getStyleName( elem
.StyleId
);
979 // create styles for children
980 elem
.applyToChildren(*this);
983 void DrawXmlFinalizer::visit( DocumentElement
& elem
, const std::list
< Element
* >::const_iterator
& )
985 elem
.applyToChildren(*this);