merge the formfield patch from ooo-build
[ooovba.git] / sdext / source / pdfimport / tree / pdfiprocessor.cxx
blob404567646883aa533d6cf2709fb58f152b365b7e
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"
40 #include "style.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;
64 namespace pdfi
67 PDFIProcessor::PDFIProcessor( const uno::Reference< task::XStatusIndicator >& xStat ) :
68 fYPrevTextPosition(-10000.0),
69 fPrevTextHeight(0.0),
70 fXPrevTextPosition(0.0),
71 fPrevTextWidth(0.0),
72 m_pElFactory( new ElementFactory() ),
73 m_pDocument( m_pElFactory->createDocumentElement() ),
74 m_pCurPage(0),
75 m_pCurElement(0),
76 m_nNextFontId( 1 ),
77 m_aIdToFont(),
78 m_aFontToId(),
79 m_aGCStack(),
80 m_nNextGCId( 1 ),
81 m_aIdToGC(),
82 m_aGCToId(),
83 m_aImages(),
84 m_eTextDirection( LrTb ),
85 m_nPages(0),
86 m_nNextZOrder( 1 ),
87 m_fWordSpace(0.0),
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 )
113 m_nPages = 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,
136 rMatrix );
139 void PDFIProcessor::setLineDash( const uno::Sequence<double>& dashes,
140 double /*start*/ )
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;
191 else
193 m_aFontToId[ aChangedFont ] = m_nNextFontId;
194 m_aIdToFont[ m_nNextFontId ] = aChangedFont;
195 rGC.FontId = m_nNextFontId;
196 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;
216 return nFont;
219 // line diagnose block - start
220 void PDFIProcessor::processGlyphLine()
222 if( m_GlyphsList.size()<1 )
223 return;
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();
247 nSpaceCount++;
251 if( nSpaceCount!=0 )
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 )
258 if (
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 )
281 if (
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();
288 if(temp!=0.0)
290 if( temp< 0.0)
291 temp= temp* -1.0;
293 fAvarageDiffCharSpaceValue+=temp;
294 nDiffSpaceCount++;
301 if (
302 ( nNullSpaceBreakerCount>0 )
305 fNullSpaceBreakerAvaregeSpaceValue=fNullSpaceBreakerAvaregeSpaceValue/nNullSpaceBreakerCount;
308 if (
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 );
326 processGlyph( 0,
327 m_GlyphsList[0],
328 pPara,
329 pFrame,
330 m_bIsWhiteSpaceInLine );
336 preSpaceNull=false;
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)
348 preSpaceNull=false;
349 if( fNullSpaceBreakerAvaregeSpaceValue > m_GlyphsList[i].getPrevGlyphsSpace() )
351 processGlyph( 0,
352 m_GlyphsList[i],
353 pPara,
354 pFrame,
355 m_bIsWhiteSpaceInLine );
358 else
360 processGlyph( 1,
361 m_GlyphsList[i],
362 pPara,
363 pFrame,
364 m_bIsWhiteSpaceInLine );
369 else
371 if (
372 ( ( m_GlyphsList[i].getPrevGlyphsSpace()<= fPreAvarageSpaceValue )&&
373 ( fPrevDiffCharSpace<=fAvarageDiffCharSpaceValue )&&
374 ( fPostDiffCharSpace<=fAvarageDiffCharSpaceValue )
375 ) ||
376 ( m_GlyphsList[i].getPrevGlyphsSpace() == 0.0 )
379 preSpaceNull=true;
381 processGlyph( 0,
382 m_GlyphsList[i],
383 pPara,
384 pFrame,
385 m_bIsWhiteSpaceInLine );
388 else
390 processGlyph( 1,
391 m_GlyphsList[i],
392 pPara,
393 pFrame,
394 m_bIsWhiteSpaceInLine );
402 if(m_GlyphsList.size()>1)
403 processGlyph( 0,
404 m_GlyphsList[m_GlyphsList.size()-1],
405 pPara,
406 pFrame,
407 m_bIsWhiteSpaceInLine );
409 m_GlyphsList.clear();
412 void PDFIProcessor::processGlyph( double fPreAvarageSpaceValue,
413 CharGlyph& aGlyph,
414 ParagraphElement* pPara,
415 FrameElement* pFrame,
416 bool bIsWhiteSpaceInLine
419 if( !bIsWhiteSpaceInLine )
421 bool flag=( 0 < fPreAvarageSpaceValue );
423 drawCharGlyphs( aGlyph.getGlyph(),
424 aGlyph.getRect(),
425 aGlyph.getFontMatrix(),
426 aGlyph.getGC(),
427 aGlyph.getCurElement(),
428 pPara,
429 pFrame,
430 flag);
432 else
434 drawCharGlyphs( aGlyph.getGlyph(),
435 aGlyph.getRect(),
436 aGlyph.getFontMatrix(),
437 aGlyph.getGC(),
438 aGlyph.getCurElement(),
439 pPara,
440 pFrame,
441 false );
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 )
457 processGlyphLine();
460 CharGlyph aGlyph;
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(
499 aFontMatrix,
500 rFontMatrix );
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& ,
519 GraphicsContext aGC,
520 Element* ,
521 ParagraphElement* pPara,
522 FrameElement* pFrame,
523 bool bSpaceFlag )
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,
537 getGCId(aGC),
538 aGC.FontId );
539 if( bSpaceFlag )
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 );
551 if( pFrame )
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);
565 if( pText )
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 ?
580 if( fRotate != 0.0 )
583 // try to create a Transformation that corrects for the wrong rotation
584 aTrans.identity();
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;
599 if( nQuadrant < 0 )
600 nQuadrant += 4;
601 if( nQuadrant == 1 )
603 aTranslation.setX( aTranslation.getX() + aRect.getHeight() + aRect.getWidth());
604 aTranslation.setY( aTranslation.getY() + aRect.getHeight() );
606 if( nQuadrant == 3 )
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,
640 bool /*bInvert*/ )
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(
681 m_pCurElement,
682 getGCId(getCurrentContext()),
683 aPoly,
684 PATH_STROKE );
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(
695 m_pCurElement,
696 getGCId(getCurrentContext()),
697 aPoly,
698 PATH_FILL );
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(
709 m_pCurElement,
710 getGCId(getCurrentContext()),
711 aPoly,
712 PATH_EOFILL );
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,
750 rURI );
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 );
763 return it->second;
766 sal_Int32 PDFIProcessor::getGCId( const GraphicsContext& rGC )
768 sal_Int32 nGCId = 0;
769 GCToIdMap::const_iterator it = m_aGCToId.find( rGC );
770 if( it != m_aGCToId.end() )
771 nGCId = it->second;
772 else
774 m_aGCToId[ rGC ] = m_nNextGCId;
775 m_aIdToGC[ m_nNextGCId ] = rGC;
776 nGCId = m_nNextGCId;
777 m_nNextGCId++;
780 return nGCId;
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 );
788 return it->second;
791 void PDFIProcessor::endPage()
793 processGlyphLine(); // draw last line
794 if( m_xStatusIndicator.is()
795 && m_pCurPage
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;
819 m_nNextZOrder = 1;
824 void PDFIProcessor::emit( XmlEmitter& rEmitter,
825 const TreeVisitorFactory& rVisitorFactory )
827 #if OSL_DEBUG_LEVEL > 1
828 m_pDocument->emitStructure( 0 );
829 #endif
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 );
839 #endif
841 // get styles
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));
853 PropertyMap aProps;
854 // document prolog
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 );
879 // emit style list
880 aStyles.emit( aContext, *aEmittingVisitor );
882 m_pDocument->visitedBy( *aEmittingVisitor, std::list<Element*>::iterator() );
883 aContext.rEmitter.endTag( "office:document" );
884 endIndicator();
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&&
899 pText[i] == '%' &&
900 pText[i+1] == 'd'
903 aStr.append( nElements );
904 i++;
906 else
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) )
938 fudge_factor = 0.9;
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 )
943 return true;
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 )
947 return false;
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 )
955 return true;
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 )
959 return false;
961 // here we have established vertical and horizontal overlap
962 // so sort left first, top second
963 if( pLeft->x < pRight->x )
964 return true;
965 if( pRight->x < pLeft->x )
966 return false;
967 if( pLeft->y < pRight->y )
968 return true;
970 return false;
973 void PDFIProcessor::sortElements( Element* pEle, bool bDeep )
975 if( pEle->Children.empty() )
976 return;
978 if( bDeep )
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
992 // copy them back.
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 )
1001 case LrTb:
1002 default:
1003 std::stable_sort( aChildren.begin(), aChildren.end(), lr_tb_sort );
1004 break;
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 )
1016 outRect.reset();
1018 if( inRect.isEmpty() )
1019 return outRect;
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 );
1046 // over and out.
1047 return outRect;