1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <tools/diagnose_ex.h>
22 #include <com/sun/star/rendering/RenderState.hpp>
23 #include <com/sun/star/rendering/XCanvas.hpp>
24 #include <basegfx/numeric/ftools.hxx>
25 #include <basegfx/tools/canvastools.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/polygon/b2dpolygon.hxx>
28 #include <basegfx/range/b2drectangle.hxx>
29 #include <basegfx/vector/b2dvector.hxx>
30 #include <canvas/canvastools.hxx>
31 #include <vcl/gdimtf.hxx>
32 #include <vcl/metaact.hxx>
33 #include <vcl/virdev.hxx>
34 #include <vcl/metric.hxx>
35 #include <tools/poly.hxx>
36 #include "mtftools.hxx"
37 #include "outdevstate.hxx"
38 #include "polypolyaction.hxx"
39 #include <basegfx/matrix/b2dhommatrixtools.hxx>
42 using namespace ::com::sun::star
;
48 void initRenderState( rendering::RenderState
& renderState
,
49 const ::cppcanvas::internal::OutDevState
& outdevState
)
51 ::canvas::tools::initRenderState( renderState
);
52 ::canvas::tools::setRenderStateTransform( renderState
,
53 outdevState
.transform
);
54 renderState
.Clip
= outdevState
.xClipPoly
;
57 ::Size
getBaselineOffset( const ::cppcanvas::internal::OutDevState
& outdevState
,
58 const VirtualDevice
& rVDev
)
60 const ::FontMetric
& aMetric
= rVDev
.GetFontMetric();
62 // calc offset for text output, the XCanvas always renders
64 switch( outdevState
.textReferencePoint
)
68 aMetric
.GetInternalLeading() + aMetric
.GetAscent() );
71 ENSURE_OR_THROW( false,
72 "tools::getBaselineOffset(): Unexpected TextAlign value" );
73 // FALLTHROUGH intended (to calm compiler warning - case won't happen)
75 return ::Size( 0, 0 );
79 -aMetric
.GetDescent() );
84 ::basegfx::B2DHomMatrix
& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix
& o_rMatrix
,
85 const VirtualDevice
& rVDev
)
87 // select size value in the middle of the available range,
88 // to have headroom both when map mode scales up, and when
90 const ::Size
aSizeLogic( 0x00010000L
,
93 const ::Size
aSizePixel( rVDev
.LogicToPixel( aSizeLogic
) );
95 o_rMatrix
= basegfx::tools::createScaleB2DHomMatrix(
96 aSizePixel
.Width() / (double)aSizeLogic
.Width(),
97 aSizePixel
.Height() / (double)aSizeLogic
.Height() );
102 ::basegfx::B2DHomMatrix
& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix
& o_rMatrix
,
103 const VirtualDevice
& rVDev
)
106 calcLogic2PixelLinearTransform(o_rMatrix
, rVDev
);
108 // translate according to curr map mode/pref map mode offset
109 const ::Point aEmptyPoint
;
110 const ::Point
& rTranslatedPoint(
111 rVDev
.LogicToPixel( aEmptyPoint
));
113 o_rMatrix
.translate(rTranslatedPoint
.X(),
114 rTranslatedPoint
.Y());
119 bool modifyClip( rendering::RenderState
& o_rRenderState
,
120 const struct ::cppcanvas::internal::OutDevState
& rOutdevState
,
121 const CanvasSharedPtr
& rCanvas
,
122 const ::basegfx::B2DPoint
& rOffset
,
123 const ::basegfx::B2DVector
* pScaling
,
124 const double* pRotation
)
126 const bool bOffsetting( !rOffset
.equalZero() );
127 const bool bScaling( pScaling
&&
128 !rtl::math::approxEqual(pScaling
->getX(), 1.0) &&
129 !rtl::math::approxEqual(pScaling
->getY(), 1.0) );
130 const bool bRotation( pRotation
&&
133 if( !bOffsetting
&& !bScaling
&& !bRotation
)
134 return false; // nothing to do
136 if( rOutdevState
.clip
.count() )
138 // general polygon case
140 ::basegfx::B2DPolyPolygon
aLocalClip( rOutdevState
.clip
);
141 ::basegfx::B2DHomMatrix aTransform
;
144 aTransform
.translate( -rOffset
.getX(),
147 aTransform
.scale( 1.0/pScaling
->getX(), 1.0/pScaling
->getY() );
150 aTransform
.rotate( - *pRotation
);
152 aLocalClip
.transform( aTransform
);
154 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
155 rCanvas
->getUNOCanvas()->getDevice(),
160 else if( !rOutdevState
.clipRect
.IsEmpty() )
164 const ::Rectangle
aLocalClipRect( rOutdevState
.clipRect
);
168 // rotation involved - convert to polygon first,
169 // then transform that
170 ::basegfx::B2DPolygon
aLocalClip(
171 ::basegfx::tools::createPolygonFromRect(
172 ::basegfx::B2DRectangle(
173 (double)(aLocalClipRect
.Left()),
174 (double)(aLocalClipRect
.Top()),
175 (double)(aLocalClipRect
.Right()),
176 (double)(aLocalClipRect
.Bottom()) ) ) );
177 ::basegfx::B2DHomMatrix aTransform
;
180 aTransform
.translate( -rOffset
.getX(),
183 aTransform
.scale( 1.0/pScaling
->getX(), 1.0/pScaling
->getY() );
185 aTransform
.rotate( - *pRotation
);
187 aLocalClip
.transform( aTransform
);
189 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
190 rCanvas
->getUNOCanvas()->getDevice(),
191 ::basegfx::B2DPolyPolygon( aLocalClip
) );
195 // scale and offset - do it on the fly, have to
196 // convert to float anyway.
197 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
198 rCanvas
->getUNOCanvas()->getDevice(),
199 ::basegfx::B2DPolyPolygon(
200 ::basegfx::tools::createPolygonFromRect(
201 ::basegfx::B2DRectangle(
202 (double)(aLocalClipRect
.Left() - rOffset
.getX())/pScaling
->getX(),
203 (double)(aLocalClipRect
.Top() - rOffset
.getY())/pScaling
->getY(),
204 (double)(aLocalClipRect
.Right() - rOffset
.getX())/pScaling
->getX(),
205 (double)(aLocalClipRect
.Bottom() - rOffset
.getY())/pScaling
->getY() ) ) ) );
209 // offset only - do it on the fly, have to convert
211 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
212 rCanvas
->getUNOCanvas()->getDevice(),
213 ::basegfx::B2DPolyPolygon(
214 ::basegfx::tools::createPolygonFromRect(
215 ::basegfx::B2DRectangle( aLocalClipRect
.Left() - rOffset
.getX(),
216 aLocalClipRect
.Top() - rOffset
.getY(),
217 aLocalClipRect
.Right() - rOffset
.getX(),
218 aLocalClipRect
.Bottom() - rOffset
.getY() ) ) ) );
224 // empty clip, nothing to do
228 // create overline/underline/strikeout line info struct
229 TextLineInfo
createTextLineInfo( const ::VirtualDevice
& rVDev
,
230 const ::cppcanvas::internal::OutDevState
& rState
)
232 const bool bOldMode( rVDev
.IsMapModeEnabled() );
234 // #i68512# Force metric regeneration with mapmode enabled
235 // (prolly OutDev bug)
236 rVDev
.GetFontMetric();
238 // will restore map mode below
239 const_cast< ::VirtualDevice
& >(rVDev
).EnableMapMode( false );
241 const ::FontMetric aMetric
= rVDev
.GetFontMetric();
243 TextLineInfo
aTextInfo(
244 (aMetric
.GetDescent() + 2) / 4.0,
245 ((aMetric
.GetInternalLeading() + 1.5) / 3.0),
246 (aMetric
.GetInternalLeading() / 2.0) - aMetric
.GetAscent(),
247 aMetric
.GetDescent() / 2.0,
248 (aMetric
.GetInternalLeading() - aMetric
.GetAscent()) / 3.0,
249 rState
.textOverlineStyle
,
250 rState
.textUnderlineStyle
,
251 rState
.textStrikeoutStyle
);
253 const_cast< ::VirtualDevice
& >(rVDev
).EnableMapMode( bOldMode
);
260 void appendRect( ::basegfx::B2DPolyPolygon
& o_rPoly
,
261 const ::basegfx::B2DPoint
& rStartPos
,
267 const double x( rStartPos
.getX() );
268 const double y( rStartPos
.getY() );
271 ::basegfx::tools::createPolygonFromRect(
272 ::basegfx::B2DRectangle( x
+ nX1
, y
+ nY1
, x
+ nX2
, y
+ nY2
) ) );
275 void appendRect( ::basegfx::B2DPolyPolygon
& o_rPoly
,
282 ::basegfx::tools::createPolygonFromRect(
283 ::basegfx::B2DRectangle( nX1
, nY1
, nX2
, nY2
) ) );
286 void appendDashes( ::basegfx::B2DPolyPolygon
& o_rPoly
,
289 const double nLineWidth
,
290 const double nLineHeight
,
291 const double nDashWidth
,
292 const double nDashSkip
)
294 const sal_Int32
nNumLoops(
295 static_cast< sal_Int32
>(
297 nLineWidth
/ nDashSkip
) + .5) );
300 for( sal_Int32 i
=0; i
<nNumLoops
; ++i
)
304 x
+ nDashWidth
, nY
+ nLineHeight
);
311 // create line actions for text such as underline and
313 ::basegfx::B2DPolyPolygon
createTextLinesPolyPolygon( const ::basegfx::B2DPoint
& rStartPos
,
314 const double& rLineWidth
,
315 const TextLineInfo
& rTextLineInfo
)
317 // fill the polypolygon with all text lines
318 ::basegfx::B2DPolyPolygon aTextLinesPolyPoly
;
320 switch( rTextLineInfo
.mnOverlineStyle
)
322 case LINESTYLE_NONE
: // nothing to do
323 // FALLTHROUGH intended
324 case LINESTYLE_DONTKNOW
:
327 case LINESTYLE_SMALLWAVE
: // TODO(F3): NYI
328 // FALLTHROUGH intended
329 case LINESTYLE_WAVE
: // TODO(F3): NYI
330 // FALLTHROUGH intended
331 case LINESTYLE_SINGLE
:
336 rTextLineInfo
.mnOverlineOffset
,
338 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
);
341 case LINESTYLE_BOLDDOTTED
: // TODO(F3): NYI
342 // FALLTHROUGH intended
343 case LINESTYLE_BOLDDASH
: // TODO(F3): NYI
344 // FALLTHROUGH intended
345 case LINESTYLE_BOLDLONGDASH
: // TODO(F3): NYI
346 // FALLTHROUGH intended
347 case LINESTYLE_BOLDDASHDOT
: // TODO(F3): NYI
348 // FALLTHROUGH intended
349 case LINESTYLE_BOLDDASHDOTDOT
:// TODO(F3): NYI
350 // FALLTHROUGH intended
351 case LINESTYLE_BOLDWAVE
: // TODO(F3): NYI
352 // FALLTHROUGH intended
358 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
,
360 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
);
363 case LINESTYLE_DOUBLEWAVE
: // TODO(F3): NYI
364 // FALLTHROUGH intended
365 case LINESTYLE_DOUBLE
:
370 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
* 2.0 ,
372 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
);
378 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
,
380 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
* 2.0 );
383 case LINESTYLE_DASHDOTDOT
: // TODO(F3): NYI
384 // FALLTHROUGH intended
385 case LINESTYLE_DOTTED
:
389 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
391 rTextLineInfo
.mnOverlineHeight
,
392 rTextLineInfo
.mnOverlineHeight
,
393 2*rTextLineInfo
.mnOverlineHeight
);
396 case LINESTYLE_DASHDOT
: // TODO(F3): NYI
397 // FALLTHROUGH intended
402 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
404 rTextLineInfo
.mnOverlineHeight
,
405 3*rTextLineInfo
.mnOverlineHeight
,
406 6*rTextLineInfo
.mnOverlineHeight
);
409 case LINESTYLE_LONGDASH
:
413 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
415 rTextLineInfo
.mnOverlineHeight
,
416 6*rTextLineInfo
.mnOverlineHeight
,
417 12*rTextLineInfo
.mnOverlineHeight
);
421 ENSURE_OR_THROW( false,
422 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
425 switch( rTextLineInfo
.mnUnderlineStyle
)
427 case LINESTYLE_NONE
: // nothing to do
428 // FALLTHROUGH intended
429 case LINESTYLE_DONTKNOW
:
432 case LINESTYLE_SMALLWAVE
: // TODO(F3): NYI
433 // FALLTHROUGH intended
434 case LINESTYLE_WAVE
: // TODO(F3): NYI
435 // FALLTHROUGH intended
436 case LINESTYLE_SINGLE
:
441 rTextLineInfo
.mnUnderlineOffset
,
443 rTextLineInfo
.mnUnderlineOffset
+ rTextLineInfo
.mnLineHeight
);
446 case LINESTYLE_BOLDDOTTED
: // TODO(F3): NYI
447 // FALLTHROUGH intended
448 case LINESTYLE_BOLDDASH
: // TODO(F3): NYI
449 // FALLTHROUGH intended
450 case LINESTYLE_BOLDLONGDASH
: // TODO(F3): NYI
451 // FALLTHROUGH intended
452 case LINESTYLE_BOLDDASHDOT
: // TODO(F3): NYI
453 // FALLTHROUGH intended
454 case LINESTYLE_BOLDDASHDOTDOT
:// TODO(F3): NYI
455 // FALLTHROUGH intended
456 case LINESTYLE_BOLDWAVE
: // TODO(F3): NYI
457 // FALLTHROUGH intended
463 rTextLineInfo
.mnUnderlineOffset
,
465 rTextLineInfo
.mnUnderlineOffset
+ 2*rTextLineInfo
.mnLineHeight
);
468 case LINESTYLE_DOUBLEWAVE
: // TODO(F3): NYI
469 // FALLTHROUGH intended
470 case LINESTYLE_DOUBLE
:
475 rTextLineInfo
.mnUnderlineOffset
- rTextLineInfo
.mnLineHeight
,
477 rTextLineInfo
.mnUnderlineOffset
);
483 rTextLineInfo
.mnUnderlineOffset
+ 2*rTextLineInfo
.mnLineHeight
,
485 rTextLineInfo
.mnUnderlineOffset
+ 3*rTextLineInfo
.mnLineHeight
);
488 case LINESTYLE_DASHDOTDOT
: // TODO(F3): NYI
489 // FALLTHROUGH intended
490 case LINESTYLE_DOTTED
:
494 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
496 rTextLineInfo
.mnLineHeight
,
497 rTextLineInfo
.mnLineHeight
,
498 2*rTextLineInfo
.mnLineHeight
);
501 case LINESTYLE_DASHDOT
: // TODO(F3): NYI
502 // FALLTHROUGH intended
507 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
509 rTextLineInfo
.mnLineHeight
,
510 3*rTextLineInfo
.mnLineHeight
,
511 6*rTextLineInfo
.mnLineHeight
);
514 case LINESTYLE_LONGDASH
:
518 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
520 rTextLineInfo
.mnLineHeight
,
521 6*rTextLineInfo
.mnLineHeight
,
522 12*rTextLineInfo
.mnLineHeight
);
526 ENSURE_OR_THROW( false,
527 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
530 switch( rTextLineInfo
.mnStrikeoutStyle
)
532 case STRIKEOUT_NONE
: // nothing to do
533 // FALLTHROUGH intended
534 case STRIKEOUT_DONTKNOW
:
537 case STRIKEOUT_SLASH
: // TODO(Q1): we should handle this in the text layer
538 // FALLTHROUGH intended
542 case STRIKEOUT_SINGLE
:
547 rTextLineInfo
.mnStrikeoutOffset
,
549 rTextLineInfo
.mnStrikeoutOffset
+ rTextLineInfo
.mnLineHeight
);
557 rTextLineInfo
.mnStrikeoutOffset
,
559 rTextLineInfo
.mnStrikeoutOffset
+ 2*rTextLineInfo
.mnLineHeight
);
562 case STRIKEOUT_DOUBLE
:
567 rTextLineInfo
.mnStrikeoutOffset
- rTextLineInfo
.mnLineHeight
,
569 rTextLineInfo
.mnStrikeoutOffset
);
575 rTextLineInfo
.mnStrikeoutOffset
+ 2*rTextLineInfo
.mnLineHeight
,
577 rTextLineInfo
.mnStrikeoutOffset
+ 3*rTextLineInfo
.mnLineHeight
);
581 ENSURE_OR_THROW( false,
582 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
585 return aTextLinesPolyPoly
;
588 ::basegfx::B2DRange
calcDevicePixelBounds( const ::basegfx::B2DRange
& rBounds
,
589 const rendering::ViewState
& viewState
,
590 const rendering::RenderState
& renderState
)
592 ::basegfx::B2DHomMatrix aTransform
;
593 ::canvas::tools::mergeViewAndRenderTransform( aTransform
,
597 ::basegfx::B2DRange aTransformedBounds
;
598 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds
,
603 // create line actions for text such as underline and
605 ::basegfx::B2DPolyPolygon
createTextLinesPolyPolygon( const double& rStartOffset
,
606 const double& rLineWidth
,
607 const TextLineInfo
& rTextLineInfo
)
609 return createTextLinesPolyPolygon(
610 ::basegfx::B2DPoint( rStartOffset
,
618 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */