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: mtftools.cxx,v $
10 * $Revision: 1.14.6.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_cppcanvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <canvas/verbosetrace.hxx>
38 #include <com/sun/star/rendering/RenderState.hpp>
39 #include <com/sun/star/rendering/XCanvas.hpp>
41 #include <basegfx/numeric/ftools.hxx>
42 #include <basegfx/tools/canvastools.hxx>
43 #include <basegfx/polygon/b2dpolygontools.hxx>
44 #include <basegfx/polygon/b2dpolygon.hxx>
45 #include <basegfx/range/b2drectangle.hxx>
46 #include <basegfx/vector/b2dvector.hxx>
47 #include <canvas/canvastools.hxx>
49 #include <vcl/gdimtf.hxx>
50 #include <vcl/metaact.hxx>
51 #include <vcl/virdev.hxx>
52 #include <vcl/metric.hxx>
53 #include <tools/poly.hxx>
55 #include "mtftools.hxx"
56 #include "outdevstate.hxx"
57 #include "polypolyaction.hxx"
61 using namespace ::com::sun::star
;
67 void initRenderState( rendering::RenderState
& renderState
,
68 const ::cppcanvas::internal::OutDevState
& outdevState
)
70 ::canvas::tools::initRenderState( renderState
);
71 ::canvas::tools::setRenderStateTransform( renderState
,
72 outdevState
.transform
);
73 renderState
.Clip
= outdevState
.xClipPoly
;
76 ::Size
getBaselineOffset( const ::cppcanvas::internal::OutDevState
& outdevState
,
77 const VirtualDevice
& rVDev
)
79 const ::FontMetric
& aMetric
= rVDev
.GetFontMetric();
81 // calc offset for text output, the XCanvas always renders
83 switch( outdevState
.textReferencePoint
)
87 aMetric
.GetIntLeading() + aMetric
.GetAscent() );
90 ENSURE_OR_THROW( false,
91 "tools::getBaselineOffset(): Unexpected TextAlign value" );
92 // FALLTHROUGH intended (to calm compiler warning - case won't happen)
94 return ::Size( 0, 0 );
98 -aMetric
.GetDescent() );
103 ::basegfx::B2DHomMatrix
& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix
& o_rMatrix
,
104 const VirtualDevice
& rVDev
)
106 // select size value in the middle of the available range,
107 // to have headroom both when map mode scales up, and when
109 const ::Size
aSizeLogic( 0x00010000L
,
112 const ::Size
aSizePixel( rVDev
.LogicToPixel( aSizeLogic
) );
114 o_rMatrix
.identity();
115 o_rMatrix
.scale( aSizePixel
.Width() / (double)aSizeLogic
.Width(),
116 aSizePixel
.Height() / (double)aSizeLogic
.Height() );
121 ::basegfx::B2DHomMatrix
& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix
& o_rMatrix
,
122 const VirtualDevice
& rVDev
)
125 calcLogic2PixelLinearTransform(o_rMatrix
, rVDev
);
127 // translate according to curr map mode/pref map mode offset
128 const ::Point aEmptyPoint
;
129 const ::Point
& rTranslatedPoint(
130 rVDev
.LogicToPixel( aEmptyPoint
));
132 o_rMatrix
.translate(rTranslatedPoint
.X(),
133 rTranslatedPoint
.Y());
138 bool modifyClip( rendering::RenderState
& o_rRenderState
,
139 const struct ::cppcanvas::internal::OutDevState
& rOutdevState
,
140 const CanvasSharedPtr
& rCanvas
,
141 const ::basegfx::B2DPoint
& rOffset
,
142 const ::basegfx::B2DVector
* pScaling
,
143 const double* pRotation
)
145 const ::Point aEmptyPoint
;
147 const bool bOffsetting( !rOffset
.equalZero() );
148 const bool bScaling( pScaling
&&
149 pScaling
->getX() != 1.0 &&
150 pScaling
->getY() != 1.0 );
151 const bool bRotation( pRotation
&&
154 if( !bOffsetting
&& !bScaling
&& !bRotation
)
155 return false; // nothing to do
157 if( rOutdevState
.clip
.count() )
159 // general polygon case
161 ::basegfx::B2DPolyPolygon
aLocalClip( rOutdevState
.clip
);
162 ::basegfx::B2DHomMatrix aTransform
;
165 aTransform
.translate( -rOffset
.getX(),
168 aTransform
.scale( 1.0/pScaling
->getX(), 1.0/pScaling
->getY() );
171 aTransform
.rotate( - *pRotation
);
173 aLocalClip
.transform( aTransform
);
175 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
176 rCanvas
->getUNOCanvas()->getDevice(),
181 else if( !rOutdevState
.clipRect
.IsEmpty() )
185 const ::Rectangle
aLocalClipRect( rOutdevState
.clipRect
);
189 // rotation involved - convert to polygon first,
190 // then transform that
191 ::basegfx::B2DPolygon
aLocalClip(
192 ::basegfx::tools::createPolygonFromRect(
193 ::basegfx::B2DRectangle(
194 (double)(aLocalClipRect
.Left()),
195 (double)(aLocalClipRect
.Top()),
196 (double)(aLocalClipRect
.Right()),
197 (double)(aLocalClipRect
.Bottom()) ) ) );
198 ::basegfx::B2DHomMatrix aTransform
;
201 aTransform
.translate( -rOffset
.getX(),
204 aTransform
.scale( 1.0/pScaling
->getX(), 1.0/pScaling
->getY() );
206 aTransform
.rotate( - *pRotation
);
208 aLocalClip
.transform( aTransform
);
210 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
211 rCanvas
->getUNOCanvas()->getDevice(),
212 ::basegfx::B2DPolyPolygon( aLocalClip
) );
216 // scale and offset - do it on the fly, have to
217 // convert to float anyway.
218 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
219 rCanvas
->getUNOCanvas()->getDevice(),
220 ::basegfx::B2DPolyPolygon(
221 ::basegfx::tools::createPolygonFromRect(
222 ::basegfx::B2DRectangle(
223 (double)(aLocalClipRect
.Left() - rOffset
.getX())/pScaling
->getX(),
224 (double)(aLocalClipRect
.Top() - rOffset
.getY())/pScaling
->getY(),
225 (double)(aLocalClipRect
.Right() - rOffset
.getX())/pScaling
->getX(),
226 (double)(aLocalClipRect
.Bottom() - rOffset
.getY())/pScaling
->getY() ) ) ) );
230 // offset only - do it on the fly, have to convert
232 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
233 rCanvas
->getUNOCanvas()->getDevice(),
234 ::basegfx::B2DPolyPolygon(
235 ::basegfx::tools::createPolygonFromRect(
236 ::basegfx::B2DRectangle( aLocalClipRect
.Left() - rOffset
.getX(),
237 aLocalClipRect
.Top() - rOffset
.getY(),
238 aLocalClipRect
.Right() - rOffset
.getX(),
239 aLocalClipRect
.Bottom() - rOffset
.getY() ) ) ) );
245 // empty clip, nothing to do
249 bool modifyClip( rendering::RenderState
& o_rRenderState
,
250 const struct ::cppcanvas::internal::OutDevState
& rOutdevState
,
251 const CanvasSharedPtr
& rCanvas
,
252 const ::Point
& rOffset
,
253 const ::basegfx::B2DVector
* pScaling
,
254 const double* pRotation
)
256 return modifyClip( o_rRenderState
,
259 ::basegfx::B2DPoint( rOffset
.X(),
265 bool modifyClip( rendering::RenderState
& o_rRenderState
,
266 const struct ::cppcanvas::internal::OutDevState
& rOutdevState
,
267 const CanvasSharedPtr
& rCanvas
,
268 const ::basegfx::B2DHomMatrix
& rTransform
)
270 if( !rTransform
.isIdentity() ||
271 !rTransform
.isInvertible() )
272 return false; // nothing to do
274 ::basegfx::B2DPolyPolygon aLocalClip
;
276 if( rOutdevState
.clip
.count() )
278 aLocalClip
= rOutdevState
.clip
;
280 else if( !rOutdevState
.clipRect
.IsEmpty() )
282 const ::Rectangle
aLocalClipRect( rOutdevState
.clipRect
);
284 aLocalClip
= ::basegfx::B2DPolyPolygon(
285 ::basegfx::tools::createPolygonFromRect(
286 ::basegfx::B2DRectangle(
287 aLocalClipRect
.Left(),
288 aLocalClipRect
.Top(),
289 aLocalClipRect
.Right(),
290 aLocalClipRect
.Bottom() ) ) );
294 // empty clip, nothing to do
298 // invert transformation and modify
299 ::basegfx::B2DHomMatrix
aLocalTransform( rTransform
);
300 aLocalTransform
.invert();
302 aLocalClip
.transform( aLocalTransform
);
304 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
305 rCanvas
->getUNOCanvas()->getDevice(),
311 // create overline/underline/strikeout line info struct
312 TextLineInfo
createTextLineInfo( const ::VirtualDevice
& rVDev
,
313 const ::cppcanvas::internal::OutDevState
& rState
)
315 const BOOL
bOldMode( rVDev
.IsMapModeEnabled() );
317 // #i68512# Force metric regeneration with mapmode enabled
318 // (prolly OutDev bug)
319 rVDev
.GetFontMetric();
321 // will restore map mode below
322 const_cast< ::VirtualDevice
& >(rVDev
).EnableMapMode( FALSE
);
324 const ::FontMetric aMetric
= rVDev
.GetFontMetric();
326 TextLineInfo
aTextInfo(
327 (aMetric
.GetDescent() + 2) / 4.0,
328 ((aMetric
.GetIntLeading() + 1.5) / 3.0),
329 (aMetric
.GetIntLeading() / 2.0) - aMetric
.GetAscent(),
330 aMetric
.GetDescent() / 2.0,
331 (aMetric
.GetIntLeading() - aMetric
.GetAscent()) / 3.0,
332 rState
.textOverlineStyle
,
333 rState
.textUnderlineStyle
,
334 rState
.textStrikeoutStyle
);
336 const_cast< ::VirtualDevice
& >(rVDev
).EnableMapMode( bOldMode
);
343 void appendRect( ::basegfx::B2DPolyPolygon
& o_rPoly
,
344 const ::basegfx::B2DPoint
& rStartPos
,
350 const double x( rStartPos
.getX() );
351 const double y( rStartPos
.getY() );
354 ::basegfx::tools::createPolygonFromRect(
355 ::basegfx::B2DRectangle( x
+ nX1
, y
+ nY1
, x
+ nX2
, y
+ nY2
) ) );
358 void appendRect( ::basegfx::B2DPolyPolygon
& o_rPoly
,
365 ::basegfx::tools::createPolygonFromRect(
366 ::basegfx::B2DRectangle( nX1
, nY1
, nX2
, nY2
) ) );
369 void appendDashes( ::basegfx::B2DPolyPolygon
& o_rPoly
,
372 const double nLineWidth
,
373 const double nLineHeight
,
374 const double nDashWidth
,
375 const double nDashSkip
)
377 const sal_Int32
nNumLoops(
378 static_cast< sal_Int32
>(
380 nLineWidth
/ nDashSkip
) + .5) );
383 for( sal_Int32 i
=0; i
<nNumLoops
; ++i
)
387 x
+ nDashWidth
, nY
+ nLineHeight
);
394 // create line actions for text such as underline and
396 ::basegfx::B2DPolyPolygon
createTextLinesPolyPolygon( const ::basegfx::B2DPoint rStartPos
,
397 const double& rLineWidth
,
398 const TextLineInfo
& rTextLineInfo
)
400 // fill the polypolygon with all text lines
401 ::basegfx::B2DPolyPolygon aTextLinesPolyPoly
;
403 switch( rTextLineInfo
.mnOverlineStyle
)
405 case UNDERLINE_NONE
: // nothing to do
406 // FALLTHROUGH intended
407 case UNDERLINE_DONTKNOW
:
410 case UNDERLINE_SMALLWAVE
: // TODO(F3): NYI
411 // FALLTHROUGH intended
412 case UNDERLINE_WAVE
: // TODO(F3): NYI
413 // FALLTHROUGH intended
414 case UNDERLINE_SINGLE
:
419 rTextLineInfo
.mnOverlineOffset
,
421 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
);
424 case UNDERLINE_BOLDDOTTED
: // TODO(F3): NYI
425 // FALLTHROUGH intended
426 case UNDERLINE_BOLDDASH
: // TODO(F3): NYI
427 // FALLTHROUGH intended
428 case UNDERLINE_BOLDLONGDASH
: // TODO(F3): NYI
429 // FALLTHROUGH intended
430 case UNDERLINE_BOLDDASHDOT
: // TODO(F3): NYI
431 // FALLTHROUGH intended
432 case UNDERLINE_BOLDDASHDOTDOT
:// TODO(F3): NYI
433 // FALLTHROUGH intended
434 case UNDERLINE_BOLDWAVE
: // TODO(F3): NYI
435 // FALLTHROUGH intended
441 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
,
443 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
);
446 case UNDERLINE_DOUBLEWAVE
: // TODO(F3): NYI
447 // FALLTHROUGH intended
448 case UNDERLINE_DOUBLE
:
453 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
* 2.0 ,
455 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
);
461 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
,
463 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
* 2.0 );
466 case UNDERLINE_DASHDOTDOT
: // TODO(F3): NYI
467 // FALLTHROUGH intended
468 case UNDERLINE_DOTTED
:
472 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
474 rTextLineInfo
.mnOverlineHeight
,
475 rTextLineInfo
.mnOverlineHeight
,
476 2*rTextLineInfo
.mnOverlineHeight
);
479 case UNDERLINE_DASHDOT
: // TODO(F3): NYI
480 // FALLTHROUGH intended
485 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
487 rTextLineInfo
.mnOverlineHeight
,
488 3*rTextLineInfo
.mnOverlineHeight
,
489 6*rTextLineInfo
.mnOverlineHeight
);
492 case UNDERLINE_LONGDASH
:
496 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
498 rTextLineInfo
.mnOverlineHeight
,
499 6*rTextLineInfo
.mnOverlineHeight
,
500 12*rTextLineInfo
.mnOverlineHeight
);
504 ENSURE_OR_THROW( false,
505 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
508 switch( rTextLineInfo
.mnUnderlineStyle
)
510 case UNDERLINE_NONE
: // nothing to do
511 // FALLTHROUGH intended
512 case UNDERLINE_DONTKNOW
:
515 case UNDERLINE_SMALLWAVE
: // TODO(F3): NYI
516 // FALLTHROUGH intended
517 case UNDERLINE_WAVE
: // TODO(F3): NYI
518 // FALLTHROUGH intended
519 case UNDERLINE_SINGLE
:
524 rTextLineInfo
.mnUnderlineOffset
,
526 rTextLineInfo
.mnUnderlineOffset
+ rTextLineInfo
.mnLineHeight
);
529 case UNDERLINE_BOLDDOTTED
: // TODO(F3): NYI
530 // FALLTHROUGH intended
531 case UNDERLINE_BOLDDASH
: // TODO(F3): NYI
532 // FALLTHROUGH intended
533 case UNDERLINE_BOLDLONGDASH
: // TODO(F3): NYI
534 // FALLTHROUGH intended
535 case UNDERLINE_BOLDDASHDOT
: // TODO(F3): NYI
536 // FALLTHROUGH intended
537 case UNDERLINE_BOLDDASHDOTDOT
:// TODO(F3): NYI
538 // FALLTHROUGH intended
539 case UNDERLINE_BOLDWAVE
: // TODO(F3): NYI
540 // FALLTHROUGH intended
546 rTextLineInfo
.mnUnderlineOffset
,
548 rTextLineInfo
.mnUnderlineOffset
+ 2*rTextLineInfo
.mnLineHeight
);
551 case UNDERLINE_DOUBLEWAVE
: // TODO(F3): NYI
552 // FALLTHROUGH intended
553 case UNDERLINE_DOUBLE
:
558 rTextLineInfo
.mnUnderlineOffset
- rTextLineInfo
.mnLineHeight
,
560 rTextLineInfo
.mnUnderlineOffset
);
566 rTextLineInfo
.mnUnderlineOffset
+ 2*rTextLineInfo
.mnLineHeight
,
568 rTextLineInfo
.mnUnderlineOffset
+ 3*rTextLineInfo
.mnLineHeight
);
571 case UNDERLINE_DASHDOTDOT
: // TODO(F3): NYI
572 // FALLTHROUGH intended
573 case UNDERLINE_DOTTED
:
577 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
579 rTextLineInfo
.mnLineHeight
,
580 rTextLineInfo
.mnLineHeight
,
581 2*rTextLineInfo
.mnLineHeight
);
584 case UNDERLINE_DASHDOT
: // TODO(F3): NYI
585 // FALLTHROUGH intended
590 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
592 rTextLineInfo
.mnLineHeight
,
593 3*rTextLineInfo
.mnLineHeight
,
594 6*rTextLineInfo
.mnLineHeight
);
597 case UNDERLINE_LONGDASH
:
601 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
603 rTextLineInfo
.mnLineHeight
,
604 6*rTextLineInfo
.mnLineHeight
,
605 12*rTextLineInfo
.mnLineHeight
);
609 ENSURE_OR_THROW( false,
610 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
613 switch( rTextLineInfo
.mnStrikeoutStyle
)
615 case STRIKEOUT_NONE
: // nothing to do
616 // FALLTHROUGH intended
617 case STRIKEOUT_DONTKNOW
:
620 case STRIKEOUT_SLASH
: // TODO(Q1): we should handle this in the text layer
621 // FALLTHROUGH intended
625 case STRIKEOUT_SINGLE
:
630 rTextLineInfo
.mnStrikeoutOffset
,
632 rTextLineInfo
.mnStrikeoutOffset
+ rTextLineInfo
.mnLineHeight
);
640 rTextLineInfo
.mnStrikeoutOffset
,
642 rTextLineInfo
.mnStrikeoutOffset
+ 2*rTextLineInfo
.mnLineHeight
);
645 case STRIKEOUT_DOUBLE
:
650 rTextLineInfo
.mnStrikeoutOffset
- rTextLineInfo
.mnLineHeight
,
652 rTextLineInfo
.mnStrikeoutOffset
);
658 rTextLineInfo
.mnStrikeoutOffset
+ 2*rTextLineInfo
.mnLineHeight
,
660 rTextLineInfo
.mnStrikeoutOffset
+ 3*rTextLineInfo
.mnLineHeight
);
664 ENSURE_OR_THROW( false,
665 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
668 return aTextLinesPolyPoly
;
671 ::basegfx::B2DRange
calcDevicePixelBounds( const ::basegfx::B2DRange
& rBounds
,
672 const rendering::ViewState
& viewState
,
673 const rendering::RenderState
& renderState
)
675 ::basegfx::B2DHomMatrix aTransform
;
676 ::canvas::tools::mergeViewAndRenderTransform( aTransform
,
680 ::basegfx::B2DRange aTransformedBounds
;
681 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds
,
686 // create line actions for text such as underline and
688 ::basegfx::B2DPolyPolygon
createTextLinesPolyPolygon( const double& rStartOffset
,
689 const double& rLineWidth
,
690 const TextLineInfo
& rTextLineInfo
)
692 return createTextLinesPolyPolygon(
693 ::basegfx::B2DPoint( rStartOffset
,