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: pdfiprocessor.cxx,v $
11 * $Revision: 1.3.4.1 $
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"
39 #include "genericelements.hxx"
41 #include "treevisiting.hxx"
43 #include <rtl/string.hxx>
44 #include <rtl/strbuf.hxx>
46 #include <comphelper/sequence.hxx>
47 #include <basegfx/polygon/b2dpolypolygontools.hxx>
48 #include <basegfx/polygon/b2dpolygonclipper.hxx>
49 #include <basegfx/polygon/b2dpolygontools.hxx>
50 #include <basegfx/tools/canvastools.hxx>
51 #include <basegfx/matrix/b2dhommatrix.hxx>
52 #include <basegfx/range/b2irange.hxx>
53 #include <basegfx/range/b2drectangle.hxx>
55 #include <com/sun/star/rendering/XVolatileBitmap.hpp>
56 #include <com/sun/star/geometry/RealSize2D.hpp>
57 #include <com/sun/star/geometry/RealPoint2D.hpp>
58 #include <com/sun/star/geometry/RealRectangle2D.hpp>
61 using namespace com::sun::star
;
67 PDFIProcessor::PDFIProcessor( const uno::Reference
< task::XStatusIndicator
>& xStat
) :
68 fYPrevTextPosition(-10000.0),
70 fXPrevTextPosition(0.0),
72 m_pElFactory( new ElementFactory() ),
73 m_pDocument( m_pElFactory
->createDocumentElement() ),
84 m_eTextDirection( LrTb
),
88 m_bIsWhiteSpaceInLine( false ),
89 m_xStatusIndicator( xStat
),
90 m_bHaveTextOnDocLevel(false)
92 FontAttributes aDefFont
;
93 aDefFont
.familyName
= USTR("Helvetica");
94 aDefFont
.isBold
= false;
95 aDefFont
.isItalic
= false;
96 aDefFont
.size
= 10*PDFI_OUTDEV_RESOLUTION
/72;
97 m_aIdToFont
[ 0 ] = aDefFont
;
98 m_aFontToId
[ aDefFont
] = 0;
100 GraphicsContext aDefGC
;
101 m_aGCStack
.push_back( aDefGC
);
102 m_aIdToGC
[ 0 ] = aDefGC
;
103 m_aGCToId
[ aDefGC
] = 0;
106 void PDFIProcessor::enableToplevelText()
108 m_bHaveTextOnDocLevel
= true;
111 void PDFIProcessor::setPageNum( sal_Int32 nPages
)
117 void PDFIProcessor::pushState()
119 m_aGCStack
.push_back( m_aGCStack
.back() );
122 void PDFIProcessor::popState()
124 m_aGCStack
.pop_back();
127 void PDFIProcessor::setFlatness( double value
)
129 getCurrentContext().Flatness
= value
;
132 void PDFIProcessor::setTransformation( const geometry::AffineMatrix2D
& rMatrix
)
134 basegfx::unotools::homMatrixFromAffineMatrix(
135 getCurrentContext().Transformation
,
139 void PDFIProcessor::setLineDash( const uno::Sequence
<double>& dashes
,
142 // TODO(F2): factor in start offset
143 GraphicsContext
& rContext( getCurrentContext() );
144 comphelper::sequenceToContainer(rContext
.DashArray
,dashes
);
147 void PDFIProcessor::setLineJoin(sal_Int8 nJoin
)
149 getCurrentContext().LineJoin
= nJoin
;
152 void PDFIProcessor::setLineCap(sal_Int8 nCap
)
154 getCurrentContext().LineCap
= nCap
;
157 void PDFIProcessor::setMiterLimit(double)
159 OSL_TRACE("PDFIProcessor::setMiterLimit(): not supported by ODF");
162 void PDFIProcessor::setLineWidth(double nWidth
)
164 getCurrentContext().LineWidth
= nWidth
;
167 void PDFIProcessor::setFillColor( const rendering::ARGBColor
& rColor
)
169 getCurrentContext().FillColor
= rColor
;
172 void PDFIProcessor::setStrokeColor( const rendering::ARGBColor
& rColor
)
174 getCurrentContext().LineColor
= rColor
;
177 void PDFIProcessor::setBlendMode(sal_Int8
)
179 OSL_TRACE("PDFIProcessor::setBlendMode(): not supported by ODF");
182 void PDFIProcessor::setFont( const FontAttributes
& i_rFont
)
184 FontAttributes
aChangedFont( i_rFont
);
185 GraphicsContext
& rGC
=getCurrentContext();
186 // for text render modes, please see PDF reference manual
187 aChangedFont
.isOutline
= ( (rGC
.TextRenderMode
== 1) || (rGC
. TextRenderMode
== 2) );
188 FontToIdMap::const_iterator it
= m_aFontToId
.find( aChangedFont
);
189 if( it
!= m_aFontToId
.end() )
190 rGC
.FontId
= it
->second
;
193 m_aFontToId
[ aChangedFont
] = m_nNextFontId
;
194 m_aIdToFont
[ m_nNextFontId
] = aChangedFont
;
195 rGC
.FontId
= m_nNextFontId
;
200 void PDFIProcessor::setTextRenderMode( sal_Int32 i_nMode
)
202 GraphicsContext
& rGC
=getCurrentContext();
203 rGC
.TextRenderMode
= i_nMode
;
204 IdToFontMap::iterator it
= m_aIdToFont
.find( rGC
.FontId
);
205 if( it
!= m_aIdToFont
.end() )
206 setFont( it
->second
);
209 sal_Int32
PDFIProcessor::getFontId( const FontAttributes
& rAttr
) const
211 const sal_Int32 nCurFont
= getCurrentContext().FontId
;
212 const_cast<PDFIProcessor
*>(this)->setFont( rAttr
);
213 const sal_Int32 nFont
= getCurrentContext().FontId
;
214 const_cast<PDFIProcessor
*>(this)->getCurrentContext().FontId
= nCurFont
;
219 // line diagnose block - start
220 void PDFIProcessor::processGlyphLine()
222 if( m_GlyphsList
.size()<1 )
225 double fPreAvarageSpaceValue
= 0.0;
226 double fAvarageDiffCharSpaceValue
= 0.0;
227 double fMinPreSpaceValue
= 0.0;
228 double fMaxPreSpaceValue
= 0.0;
229 double fNullSpaceBreakerAvaregeSpaceValue
= 0.0;
231 unsigned int nSpaceCount( 0 );
232 unsigned int nDiffSpaceCount( 0 );
233 unsigned int nNullSpaceBreakerCount
=0;
234 bool preSpaceNull(true);
236 for ( unsigned int i
=0; i
<m_GlyphsList
.size()-1; i
++ ) // i=1 because the first glyph doesn't have a prevGlyphSpace value
238 if( m_GlyphsList
[i
].getPrevGlyphsSpace()>0.0 )
240 if( fMinPreSpaceValue
>m_GlyphsList
[i
].getPrevGlyphsSpace() )
241 fMinPreSpaceValue
=m_GlyphsList
[i
].getPrevGlyphsSpace();
243 if( fMaxPreSpaceValue
<m_GlyphsList
[i
].getPrevGlyphsSpace() )
244 fMaxPreSpaceValue
=m_GlyphsList
[i
].getPrevGlyphsSpace();
246 fPreAvarageSpaceValue
+= m_GlyphsList
[i
].getPrevGlyphsSpace();
252 fPreAvarageSpaceValue
= fPreAvarageSpaceValue
/( nSpaceCount
);
254 for ( unsigned int i
=0; i
<m_GlyphsList
.size()-1; i
++ ) // i=1 because the first glyph doesn't have a prevGlyphSpace value
256 if ( m_GlyphsList
[i
].getPrevGlyphsSpace()==0.0 )
259 ( m_GlyphsList
[i
+1].getPrevGlyphsSpace()>0.0)&&
260 ( fPreAvarageSpaceValue
>m_GlyphsList
[i
+1].getPrevGlyphsSpace())
263 fNullSpaceBreakerAvaregeSpaceValue
+=m_GlyphsList
[i
+1].getPrevGlyphsSpace();
264 nNullSpaceBreakerCount
++;
269 if( ( fNullSpaceBreakerAvaregeSpaceValue
!= 0.0 )&&
270 ( fNullSpaceBreakerAvaregeSpaceValue
< fPreAvarageSpaceValue
)
273 fPreAvarageSpaceValue
= fNullSpaceBreakerAvaregeSpaceValue
;
276 for ( unsigned int i
=0; i
<m_GlyphsList
.size()-1; i
++ ) // i=1 cose the first Glypth dont have prevGlyphSpace value
278 if ( ( m_GlyphsList
[i
].getPrevGlyphsSpace()>0.0 )
282 ( m_GlyphsList
[i
].getPrevGlyphsSpace() <= fPreAvarageSpaceValue
)&&
283 ( m_GlyphsList
[i
+1].getPrevGlyphsSpace()<= fPreAvarageSpaceValue
)
286 double temp
= m_GlyphsList
[i
].getPrevGlyphsSpace()-m_GlyphsList
[i
+1].getPrevGlyphsSpace();
293 fAvarageDiffCharSpaceValue
+=temp
;
302 ( nNullSpaceBreakerCount
>0 )
305 fNullSpaceBreakerAvaregeSpaceValue
=fNullSpaceBreakerAvaregeSpaceValue
/nNullSpaceBreakerCount
;
309 ( nDiffSpaceCount
>0 )&&(fAvarageDiffCharSpaceValue
>0)
312 fAvarageDiffCharSpaceValue
= fAvarageDiffCharSpaceValue
/ nDiffSpaceCount
;
315 ParagraphElement
* pPara
= NULL
;
316 FrameElement
* pFrame
= NULL
;
318 if(m_GlyphsList
.size()>0)
320 pFrame
= m_pElFactory
->createFrameElement( m_GlyphsList
[0].getCurElement(), getGCId( getTransformGlyphContext( m_GlyphsList
[0])) );
321 pFrame
->ZOrder
= m_nNextZOrder
++;
322 pPara
= m_pElFactory
->createParagraphElement( pFrame
);
330 m_bIsWhiteSpaceInLine
);
338 for ( unsigned int i
=1; i
<m_GlyphsList
.size()-1; i
++ )
340 double fPrevDiffCharSpace
= m_GlyphsList
[i
].getPrevGlyphsSpace()-m_GlyphsList
[i
-1].getPrevGlyphsSpace();
341 double fPostDiffCharSpace
= m_GlyphsList
[i
].getPrevGlyphsSpace()-m_GlyphsList
[i
+1].getPrevGlyphsSpace();
345 preSpaceNull
&& (m_GlyphsList
[i
].getPrevGlyphsSpace()!= 0.0)
349 if( fNullSpaceBreakerAvaregeSpaceValue
> m_GlyphsList
[i
].getPrevGlyphsSpace() )
355 m_bIsWhiteSpaceInLine
);
364 m_bIsWhiteSpaceInLine
);
372 ( ( m_GlyphsList
[i
].getPrevGlyphsSpace()<= fPreAvarageSpaceValue
)&&
373 ( fPrevDiffCharSpace
<=fAvarageDiffCharSpaceValue
)&&
374 ( fPostDiffCharSpace
<=fAvarageDiffCharSpaceValue
)
376 ( m_GlyphsList
[i
].getPrevGlyphsSpace() == 0.0 )
385 m_bIsWhiteSpaceInLine
);
394 m_bIsWhiteSpaceInLine
);
402 if(m_GlyphsList
.size()>1)
404 m_GlyphsList
[m_GlyphsList
.size()-1],
407 m_bIsWhiteSpaceInLine
);
409 m_GlyphsList
.clear();
412 void PDFIProcessor::processGlyph( double fPreAvarageSpaceValue
,
414 ParagraphElement
* pPara
,
415 FrameElement
* pFrame
,
416 bool bIsWhiteSpaceInLine
419 if( !bIsWhiteSpaceInLine
)
421 bool flag
=( 0 < fPreAvarageSpaceValue
);
423 drawCharGlyphs( aGlyph
.getGlyph(),
425 aGlyph
.getFontMatrix(),
427 aGlyph
.getCurElement(),
434 drawCharGlyphs( aGlyph
.getGlyph(),
436 aGlyph
.getFontMatrix(),
438 aGlyph
.getCurElement(),
445 void PDFIProcessor::drawGlyphLine( const rtl::OUString
& rGlyphs
,
446 const geometry::RealRectangle2D
& rRect
,
447 const geometry::Matrix2D
& rFontMatrix
)
449 double isFirstLine
= fYPrevTextPosition
+ fXPrevTextPosition
+ fPrevTextHeight
+ fPrevTextWidth
;
451 ( ( ( fYPrevTextPosition
!= rRect
.Y1
) ) ||
452 ( ( fXPrevTextPosition
> rRect
.X2
) ) ||
453 ( ( fXPrevTextPosition
+fPrevTextWidth
*1.3)<rRect
.X1
)
454 ) && ( isFirstLine
> 0.0 )
462 aGlyph
.setGlyph ( rGlyphs
);
463 aGlyph
.setRect ( rRect
);
464 aGlyph
.setFontMatrix ( rFontMatrix
);
465 aGlyph
.setGraphicsContext ( getCurrentContext() );
466 getGCId(getCurrentContext());
467 aGlyph
.setCurElement( m_pCurElement
);
469 aGlyph
.setYPrevGlyphPosition( fYPrevTextPosition
);
470 aGlyph
.setXPrevGlyphPosition( fXPrevTextPosition
);
471 aGlyph
.setPrevGlyphHeight ( fPrevTextHeight
);
472 aGlyph
.setPrevGlyphWidth ( fPrevTextWidth
);
474 m_GlyphsList
.push_back( aGlyph
);
476 fYPrevTextPosition
= rRect
.Y1
;
477 fXPrevTextPosition
= rRect
.X2
;
478 fPrevTextHeight
= rRect
.Y2
-rRect
.Y1
;
479 fPrevTextWidth
= rRect
.X2
-rRect
.X1
;
481 if( !m_bIsWhiteSpaceInLine
)
483 static rtl::OUString
tempWhiteSpaceStr( 0x20 );
484 static rtl::OUString
tempWhiteSpaceNonBreakingStr( 0xa0 );
485 m_bIsWhiteSpaceInLine
=(rGlyphs
.equals( tempWhiteSpaceStr
) || rGlyphs
.equals( tempWhiteSpaceNonBreakingStr
));
489 GraphicsContext
& PDFIProcessor::getTransformGlyphContext( CharGlyph
& rGlyph
)
491 geometry::RealRectangle2D rRect
= rGlyph
.getRect();
492 geometry::Matrix2D rFontMatrix
= rGlyph
.getFontMatrix();
494 rtl::OUString
tempStr( 32 );
495 geometry::RealRectangle2D
aRect(rRect
);
497 basegfx::B2DHomMatrix aFontMatrix
;
498 basegfx::unotools::homMatrixFromMatrix(
502 FontAttributes aFontAttrs
= m_aIdToFont
[ rGlyph
.getGC().FontId
];
504 // add transformation to GC
505 basegfx::B2DHomMatrix aFontTransform
;
506 aFontTransform
.translate( -rRect
.X1
, -rRect
.Y1
);
507 aFontTransform
*= aFontMatrix
;
508 aFontTransform
.translate( rRect
.X1
, rRect
.Y1
);
511 rGlyph
.getGC().Transformation
= rGlyph
.getGC().Transformation
* aFontTransform
;
512 getGCId(rGlyph
.getGC());
514 return rGlyph
.getGC();
516 void PDFIProcessor::drawCharGlyphs( rtl::OUString
& rGlyphs
,
517 geometry::RealRectangle2D
& rRect
,
518 geometry::Matrix2D
& ,
521 ParagraphElement
* pPara
,
522 FrameElement
* pFrame
,
527 rtl::OUString
tempStr( 32 );
528 geometry::RealRectangle2D
aRect(rRect
);
530 ::basegfx::B2DRange aRect2
;
531 calcTransformedRectBounds( aRect2
,
532 ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aRect
),
533 aGC
.Transformation
);
534 // check whether there was a previous draw frame
536 TextElement
* pText
= m_pElFactory
->createTextElement( pPara
,
540 pText
->Text
.append( tempStr
);
542 pText
->Text
.append( rGlyphs
);
544 pText
->x
= aRect2
.getMinX() ;
545 pText
->y
= aRect2
.getMinY() ;
546 pText
->w
= 0.0; // ToDO P2: 1.1 is a hack for solving of size auto-grow problem
547 pText
->h
= aRect2
.getHeight(); // ToDO P2: 1.1 is a hack for solving of size auto-grow problem
549 pPara
->updateGeometryWith( pText
);
552 pFrame
->updateGeometryWith( pPara
);
555 void PDFIProcessor::drawGlyphs( const rtl::OUString
& rGlyphs
,
556 const geometry::RealRectangle2D
& rRect
,
557 const geometry::Matrix2D
& rFontMatrix
)
559 drawGlyphLine( rGlyphs
, rRect
, rFontMatrix
);
562 void PDFIProcessor::endText()
564 TextElement
* pText
= dynamic_cast<TextElement
*>(m_pCurElement
);
566 m_pCurElement
= pText
->Parent
;
569 void PDFIProcessor::setupImage(ImageId nImage
)
571 const GraphicsContext
& rGC( getCurrentContext() );
573 basegfx::B2DHomMatrix
aTrans( rGC
.Transformation
);
575 // check for rotation, which is the other way around in ODF
576 basegfx::B2DTuple aScale
, aTranslation
;
577 double fRotate
, fShearX
;
578 rGC
.Transformation
.decompose( aScale
, aTranslation
, fRotate
, fShearX
);
579 // TODDO(F4): correcting rotation when fShearX != 0 ?
583 // try to create a Transformation that corrects for the wrong rotation
585 aTrans
.scale( aScale
.getX(), aScale
.getY() );
586 aTrans
.rotate( -fRotate
);
588 basegfx::B2DRange
aRect( 0, 0, 1, 1 );
589 aRect
.transform( aTrans
);
591 // TODO(F3) treat translation correctly
592 // the corrections below work for multiples of 90 degree
593 // which is a common case (landscape/portrait/seascape)
594 // we need a general solution here; however this needs to
595 // work in sync with DrawXmlEmitter::fillFrameProps and WriterXmlEmitter::fillFrameProps
596 // admittedly this is a lame workaround and fails for arbitrary rotation
597 double fQuadrant
= fmod( fRotate
, 2.0*M_PI
) / M_PI_2
;
598 int nQuadrant
= (int)fQuadrant
;
603 aTranslation
.setX( aTranslation
.getX() + aRect
.getHeight() + aRect
.getWidth());
604 aTranslation
.setY( aTranslation
.getY() + aRect
.getHeight() );
607 aTranslation
.setX( aTranslation
.getX() - aRect
.getHeight() );
609 aTrans
.translate( aTranslation
.getX(),
610 aTranslation
.getY() );
613 bool bMirrorVertical
= aScale
.getY() > 0;
615 // transform unit rect to determine view box
616 basegfx::B2DRange
aRect( 0, 0, 1, 1 );
617 aRect
.transform( aTrans
);
619 // TODO(F3): Handle clip
620 const sal_Int32 nGCId
= getGCId(rGC
);
621 FrameElement
* pFrame
= m_pElFactory
->createFrameElement( m_pCurElement
, nGCId
);
622 ImageElement
* pImageElement
= m_pElFactory
->createImageElement( pFrame
, nGCId
, nImage
);
623 pFrame
->x
= pImageElement
->x
= aRect
.getMinX();
624 pFrame
->y
= pImageElement
->y
= aRect
.getMinY();
625 pFrame
->w
= pImageElement
->w
= aRect
.getWidth();
626 pFrame
->h
= pImageElement
->h
= aRect
.getHeight();
627 pFrame
->ZOrder
= m_nNextZOrder
++;
629 if( bMirrorVertical
)
631 pFrame
->MirrorVertical
= pImageElement
->MirrorVertical
= true;
632 pFrame
->x
+= aRect
.getWidth();
633 pImageElement
->x
+= aRect
.getWidth();
634 pFrame
->y
+= aRect
.getHeight();
635 pImageElement
->y
+= aRect
.getHeight();
639 void PDFIProcessor::drawMask(const uno::Sequence
<beans::PropertyValue
>& xBitmap
,
642 // TODO(F3): Handle mask and inversion
643 setupImage( m_aImages
.addImage(xBitmap
) );
646 void PDFIProcessor::drawImage(const uno::Sequence
<beans::PropertyValue
>& xBitmap
)
648 setupImage( m_aImages
.addImage(xBitmap
) );
651 void PDFIProcessor::drawColorMaskedImage(const uno::Sequence
<beans::PropertyValue
>& xBitmap
,
652 const uno::Sequence
<uno::Any
>& /*xMaskColors*/ )
654 // TODO(F3): Handle mask colors
655 setupImage( m_aImages
.addImage(xBitmap
) );
658 void PDFIProcessor::drawMaskedImage(const uno::Sequence
<beans::PropertyValue
>& xBitmap
,
659 const uno::Sequence
<beans::PropertyValue
>& /*xMask*/,
660 bool /*bInvertMask*/)
662 // TODO(F3): Handle mask and inversion
663 setupImage( m_aImages
.addImage(xBitmap
) );
666 void PDFIProcessor::drawAlphaMaskedImage(const uno::Sequence
<beans::PropertyValue
>& xBitmap
,
667 const uno::Sequence
<beans::PropertyValue
>& /*xMask*/)
669 // TODO(F3): Handle mask
671 setupImage( m_aImages
.addImage(xBitmap
) );
675 void PDFIProcessor::strokePath( const uno::Reference
< rendering::XPolyPolygon2D
>& rPath
)
677 basegfx::B2DPolyPolygon aPoly
=basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath
);
678 aPoly
.transform(getCurrentContext().Transformation
);
680 PolyPolyElement
* pPoly
= m_pElFactory
->createPolyPolyElement(
682 getGCId(getCurrentContext()),
685 pPoly
->updateGeometry();
686 pPoly
->ZOrder
= m_nNextZOrder
++;
689 void PDFIProcessor::fillPath( const uno::Reference
< rendering::XPolyPolygon2D
>& rPath
)
691 basegfx::B2DPolyPolygon aPoly
=basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath
);
692 aPoly
.transform(getCurrentContext().Transformation
);
694 PolyPolyElement
* pPoly
= m_pElFactory
->createPolyPolyElement(
696 getGCId(getCurrentContext()),
699 pPoly
->updateGeometry();
700 pPoly
->ZOrder
= m_nNextZOrder
++;
703 void PDFIProcessor::eoFillPath( const uno::Reference
< rendering::XPolyPolygon2D
>& rPath
)
705 basegfx::B2DPolyPolygon aPoly
=basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath
);
706 aPoly
.transform(getCurrentContext().Transformation
);
708 PolyPolyElement
* pPoly
= m_pElFactory
->createPolyPolyElement(
710 getGCId(getCurrentContext()),
713 pPoly
->updateGeometry();
714 pPoly
->ZOrder
= m_nNextZOrder
++;
717 void PDFIProcessor::intersectClip(const uno::Reference
< rendering::XPolyPolygon2D
>& rPath
)
719 // TODO(F3): interpret fill mode
720 basegfx::B2DPolyPolygon aNewClip
= basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath
);
721 aNewClip
.transform(getCurrentContext().Transformation
);
722 basegfx::B2DPolyPolygon aCurClip
= getCurrentContext().Clip
;
724 if( aCurClip
.count() ) // #i92985# adapted API from (..., false, false) to (..., true, false)
725 aNewClip
= basegfx::tools::clipPolyPolygonOnPolyPolygon( aCurClip
, aNewClip
, true, false );
727 getCurrentContext().Clip
= aNewClip
;
730 void PDFIProcessor::intersectEoClip(const uno::Reference
< rendering::XPolyPolygon2D
>& rPath
)
732 // TODO(F3): interpret fill mode
733 basegfx::B2DPolyPolygon aNewClip
= basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath
);
734 aNewClip
.transform(getCurrentContext().Transformation
);
735 basegfx::B2DPolyPolygon aCurClip
= getCurrentContext().Clip
;
737 if( aCurClip
.count() ) // #i92985# adapted API from (..., false, false) to (..., true, false)
738 aNewClip
= basegfx::tools::clipPolyPolygonOnPolyPolygon( aCurClip
, aNewClip
, true, false );
740 getCurrentContext().Clip
= aNewClip
;
743 void PDFIProcessor::hyperLink( const geometry::RealRectangle2D
& rBounds
,
744 const ::rtl::OUString
& rURI
)
746 if( rURI
.getLength() )
748 HyperlinkElement
* pLink
= m_pElFactory
->createHyperlinkElement(
749 &m_pCurPage
->Hyperlinks
,
751 pLink
->x
= rBounds
.X1
;
752 pLink
->y
= rBounds
.Y1
;
753 pLink
->w
= rBounds
.X2
-rBounds
.X1
;
754 pLink
->h
= rBounds
.Y2
-rBounds
.Y1
;
758 const FontAttributes
& PDFIProcessor::getFont( sal_Int32 nFontId
) const
760 IdToFontMap::const_iterator it
= m_aIdToFont
.find( nFontId
);
761 if( it
== m_aIdToFont
.end() )
762 it
= m_aIdToFont
.find( 0 );
766 sal_Int32
PDFIProcessor::getGCId( const GraphicsContext
& rGC
)
769 GCToIdMap::const_iterator it
= m_aGCToId
.find( rGC
);
770 if( it
!= m_aGCToId
.end() )
774 m_aGCToId
[ rGC
] = m_nNextGCId
;
775 m_aIdToGC
[ m_nNextGCId
] = rGC
;
783 const GraphicsContext
& PDFIProcessor::getGraphicsContext( sal_Int32 nGCId
) const
785 IdToGCMap::const_iterator it
= m_aIdToGC
.find( nGCId
);
786 if( it
== m_aIdToGC
.end() )
787 it
= m_aIdToGC
.find( 0 );
791 void PDFIProcessor::endPage()
793 processGlyphLine(); // draw last line
794 if( m_xStatusIndicator
.is()
796 && m_pCurPage
->PageNumber
== m_nPages
798 m_xStatusIndicator
->end();
801 void PDFIProcessor::startPage( const geometry::RealSize2D
& rSize
)
803 // initial clip is to page bounds
804 getCurrentContext().Clip
= basegfx::B2DPolyPolygon(
805 basegfx::tools::createPolygonFromRect(
806 basegfx::B2DRange( 0, 0, rSize
.Width
, rSize
.Height
)));
808 sal_Int32 nNextPageNr
= m_pCurPage
? m_pCurPage
->PageNumber
+1 : 1;
809 if( m_xStatusIndicator
.is() )
811 if( nNextPageNr
== 1 )
812 startIndicator( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " " ) ) );
813 m_xStatusIndicator
->setValue( nNextPageNr
);
815 m_pCurPage
= m_pElFactory
->createPageElement(m_pDocument
.get(), nNextPageNr
);
816 m_pCurElement
= m_pCurPage
;
817 m_pCurPage
->w
= rSize
.Width
;
818 m_pCurPage
->h
= rSize
.Height
;
824 void PDFIProcessor::emit( XmlEmitter
& rEmitter
,
825 const TreeVisitorFactory
& rVisitorFactory
)
827 #if OSL_DEBUG_LEVEL > 1
828 m_pDocument
->emitStructure( 0 );
831 ElementTreeVisitorSharedPtr
optimizingVisitor(
832 rVisitorFactory
.createOptimizingVisitor(*this));
833 // FIXME: localization
834 startIndicator( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( " " ) ) );
835 m_pDocument
->visitedBy( *optimizingVisitor
, std::list
<Element
*>::iterator());
837 #if OSL_DEBUG_LEVEL > 1
838 m_pDocument
->emitStructure( 0 );
842 StyleContainer aStyles
;
843 ElementTreeVisitorSharedPtr
finalizingVisitor(
844 rVisitorFactory
.createStyleCollectingVisitor(aStyles
,*this));
845 // FIXME: localization
847 m_pDocument
->visitedBy( *finalizingVisitor
, std::list
<Element
*>::iterator() );
849 EmitContext
aContext( rEmitter
, aStyles
, m_aImages
, *this, m_xStatusIndicator
);
850 ElementTreeVisitorSharedPtr
aEmittingVisitor(
851 rVisitorFactory
.createEmittingVisitor(aContext
));
855 #define OASIS_STR "urn:oasis:names:tc:opendocument:xmlns:"
856 aProps
[ USTR( "xmlns:office" ) ] = USTR( OASIS_STR
"office:1.0" );
857 aProps
[ USTR( "xmlns:style" ) ] = USTR( OASIS_STR
"style:1.0" );
858 aProps
[ USTR( "xmlns:text" ) ] = USTR( OASIS_STR
"text:1.0" );
859 aProps
[ USTR( "xmlns:svg" ) ] = USTR( OASIS_STR
"svg-compatible:1.0" );
860 aProps
[ USTR( "xmlns:table" ) ] = USTR( OASIS_STR
"table:1.0" );
861 aProps
[ USTR( "xmlns:draw" ) ] = USTR( OASIS_STR
"drawing:1.0" );
862 aProps
[ USTR( "xmlns:fo" ) ] = USTR( OASIS_STR
"xsl-fo-compatible:1.0" );
863 aProps
[ USTR( "xmlns:xlink" )] = USTR( "http://www.w3.org/1999/xlink" );
864 aProps
[ USTR( "xmlns:dc" )] = USTR( "http://purl.org/dc/elements/1.1/" );
865 aProps
[ USTR( "xmlns:number" )] = USTR( OASIS_STR
"datastyle:1.0" );
866 aProps
[ USTR( "xmlns:presentation" )] = USTR( OASIS_STR
"presentation:1.0" );
867 aProps
[ USTR( "xmlns:math" )] = USTR( "http://www.w3.org/1998/Math/MathML" );
868 aProps
[ USTR( "xmlns:form" )] = USTR( OASIS_STR
"form:1.0" );
869 aProps
[ USTR( "xmlns:script" )] = USTR( OASIS_STR
"script:1.0" );
870 aProps
[ USTR( "xmlns:dom" )] = USTR( "http://www.w3.org/2001/xml-events" );
871 aProps
[ USTR( "xmlns:xforms" )] = USTR( "http://www.w3.org/2002/xforms" );
872 aProps
[ USTR( "xmlns:xsd" )] = USTR( "http://www.w3.org/2001/XMLSchema" );
873 aProps
[ USTR( "xmlns:xsi" )] = USTR( "http://www.w3.org/2001/XMLSchema-instance" );
874 aProps
[ USTR( "office:version" ) ] = USTR( "1.0" );
875 aProps
[ USTR( "office:version" ) ] = USTR( "1.0" );
877 aContext
.rEmitter
.beginTag( "office:document", aProps
);
880 aStyles
.emit( aContext
, *aEmittingVisitor
);
882 m_pDocument
->visitedBy( *aEmittingVisitor
, std::list
<Element
*>::iterator() );
883 aContext
.rEmitter
.endTag( "office:document" );
887 void PDFIProcessor::startIndicator( const rtl::OUString
& rText
, sal_Int32 nElements
)
889 if( nElements
== -1 )
890 nElements
= m_nPages
;
891 if( m_xStatusIndicator
.is() )
893 sal_Int32 nUnicodes
= rText
.getLength();
894 rtl::OUStringBuffer
aStr( nUnicodes
*2 );
895 const sal_Unicode
* pText
= rText
.getStr();
896 for( int i
= 0; i
< nUnicodes
; i
++ )
898 if( nUnicodes
-i
> 1&&
903 aStr
.append( nElements
);
907 aStr
.append( pText
[i
] );
909 m_xStatusIndicator
->start( aStr
.makeStringAndClear(), nElements
);
913 void PDFIProcessor::endIndicator()
915 if( m_xStatusIndicator
.is() )
916 m_xStatusIndicator
->end();
919 void PDFIProcessor::sortDocument( bool bDeep
)
921 for( std::list
< Element
* >::iterator it
= m_pDocument
->Children
.begin();
922 it
!= m_pDocument
->Children
.end(); ++it
)
924 if( dynamic_cast<PageElement
*>(*it
) != NULL
)
925 sortElements( *it
, bDeep
);
929 static bool lr_tb_sort( Element
* pLeft
, Element
* pRight
)
931 // first: top-bottom sorting
933 // Note: allow for 10% overlap on text lines since text lines are usually
934 // of the same order as font height whereas the real paint area
935 // of text is usually smaller
936 double fudge_factor
= 1.0;
937 if( dynamic_cast< TextElement
* >(pLeft
) || dynamic_cast< TextElement
* >(pRight
) )
940 // if left's lower boundary is above right's upper boundary
941 // then left is smaller
942 if( pLeft
->y
+pLeft
->h
*fudge_factor
< pRight
->y
)
944 // if right's lower boundary is above left's upper boundary
945 // then left is definitely not smaller
946 if( pRight
->y
+pRight
->h
*fudge_factor
< pLeft
->y
)
949 // by now we have established that left and right are inside
950 // a "line", that is they have vertical overlap
951 // second: left-right sorting
952 // if left's right boundary is left to right's left boundary
953 // then left is smaller
954 if( pLeft
->x
+pLeft
->w
< pRight
->x
)
956 // if right's right boundary is left to left's left boundary
957 // then left is definitely not smaller
958 if( pRight
->x
+pRight
->w
< pLeft
->x
)
961 // here we have established vertical and horizontal overlap
962 // so sort left first, top second
963 if( pLeft
->x
< pRight
->x
)
965 if( pRight
->x
< pLeft
->x
)
967 if( pLeft
->y
< pRight
->y
)
973 void PDFIProcessor::sortElements( Element
* pEle
, bool bDeep
)
975 if( pEle
->Children
.empty() )
980 for( std::list
< Element
* >::iterator it
= pEle
->Children
.begin();
981 it
!= pEle
->Children
.end(); ++it
)
983 sortElements( *it
, bDeep
);
986 // HACK: the stable sort member on std::list that takes a
987 // strict weak ordering requires member templates - which we
988 // do not have on all compilers. so we need to use std::stable_sort
989 // here - which does need random access iterators which the
990 // list iterators are not.
991 // so we need to copy the Element* to an array, stable sort that and
993 std::vector
<Element
*> aChildren
;
994 while( ! pEle
->Children
.empty() )
996 aChildren
.push_back( pEle
->Children
.front() );
997 pEle
->Children
.pop_front();
999 switch( m_eTextDirection
)
1003 std::stable_sort( aChildren
.begin(), aChildren
.end(), lr_tb_sort
);
1006 int nChildren
= aChildren
.size();
1007 for( int i
= 0; i
< nChildren
; i
++ )
1008 pEle
->Children
.push_back( aChildren
[i
] );
1012 ::basegfx::B2DRange
& PDFIProcessor::calcTransformedRectBounds( ::basegfx::B2DRange
& outRect
,
1013 const ::basegfx::B2DRange
& inRect
,
1014 const ::basegfx::B2DHomMatrix
& transformation
)
1018 if( inRect
.isEmpty() )
1021 // transform all four extremal points of the rectangle,
1022 // take bounding rect of those.
1024 // transform left-top point
1025 outRect
.expand( transformation
* inRect
.getMinimum() );
1027 // transform bottom-right point
1028 outRect
.expand( transformation
* inRect
.getMaximum() );
1030 ::basegfx::B2DPoint aPoint
;
1032 // transform top-right point
1033 aPoint
.setX( inRect
.getMaxX() );
1034 aPoint
.setY( inRect
.getMinY() );
1036 aPoint
*= transformation
;
1037 outRect
.expand( aPoint
);
1039 // transform bottom-left point
1040 aPoint
.setX( inRect
.getMinX() );
1041 aPoint
.setY( inRect
.getMaxY() );
1043 aPoint
*= transformation
;
1044 outRect
.expand( aPoint
);