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 <canvas/debug.hxx>
22 #include <tools/diagnose_ex.h>
23 #include <canvas/verbosetrace.hxx>
24 #include <com/sun/star/rendering/RenderState.hpp>
25 #include <com/sun/star/rendering/XCanvas.hpp>
26 #include <basegfx/numeric/ftools.hxx>
27 #include <basegfx/tools/canvastools.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include <basegfx/range/b2drectangle.hxx>
31 #include <basegfx/vector/b2dvector.hxx>
32 #include <canvas/canvastools.hxx>
33 #include <vcl/gdimtf.hxx>
34 #include <vcl/metaact.hxx>
35 #include <vcl/virdev.hxx>
36 #include <vcl/metric.hxx>
37 #include <tools/poly.hxx>
38 #include "mtftools.hxx"
39 #include "outdevstate.hxx"
40 #include "polypolyaction.hxx"
41 #include <basegfx/matrix/b2dhommatrixtools.hxx>
45 using namespace ::com::sun::star
;
51 void initRenderState( rendering::RenderState
& renderState
,
52 const ::cppcanvas::internal::OutDevState
& outdevState
)
54 ::canvas::tools::initRenderState( renderState
);
55 ::canvas::tools::setRenderStateTransform( renderState
,
56 outdevState
.transform
);
57 renderState
.Clip
= outdevState
.xClipPoly
;
60 ::Size
getBaselineOffset( const ::cppcanvas::internal::OutDevState
& outdevState
,
61 const VirtualDevice
& rVDev
)
63 const ::FontMetric
& aMetric
= rVDev
.GetFontMetric();
65 // calc offset for text output, the XCanvas always renders
67 switch( outdevState
.textReferencePoint
)
71 aMetric
.GetIntLeading() + aMetric
.GetAscent() );
74 ENSURE_OR_THROW( false,
75 "tools::getBaselineOffset(): Unexpected TextAlign value" );
76 // FALLTHROUGH intended (to calm compiler warning - case won't happen)
78 return ::Size( 0, 0 );
82 -aMetric
.GetDescent() );
87 ::basegfx::B2DHomMatrix
& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix
& o_rMatrix
,
88 const VirtualDevice
& rVDev
)
90 // select size value in the middle of the available range,
91 // to have headroom both when map mode scales up, and when
93 const ::Size
aSizeLogic( 0x00010000L
,
96 const ::Size
aSizePixel( rVDev
.LogicToPixel( aSizeLogic
) );
98 o_rMatrix
= basegfx::tools::createScaleB2DHomMatrix(
99 aSizePixel
.Width() / (double)aSizeLogic
.Width(),
100 aSizePixel
.Height() / (double)aSizeLogic
.Height() );
105 ::basegfx::B2DHomMatrix
& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix
& o_rMatrix
,
106 const VirtualDevice
& rVDev
)
109 calcLogic2PixelLinearTransform(o_rMatrix
, rVDev
);
111 // translate according to curr map mode/pref map mode offset
112 const ::Point aEmptyPoint
;
113 const ::Point
& rTranslatedPoint(
114 rVDev
.LogicToPixel( aEmptyPoint
));
116 o_rMatrix
.translate(rTranslatedPoint
.X(),
117 rTranslatedPoint
.Y());
122 bool modifyClip( rendering::RenderState
& o_rRenderState
,
123 const struct ::cppcanvas::internal::OutDevState
& rOutdevState
,
124 const CanvasSharedPtr
& rCanvas
,
125 const ::basegfx::B2DPoint
& rOffset
,
126 const ::basegfx::B2DVector
* pScaling
,
127 const double* pRotation
)
129 const bool bOffsetting( !rOffset
.equalZero() );
130 const bool bScaling( pScaling
&&
131 pScaling
->getX() != 1.0 &&
132 pScaling
->getY() != 1.0 );
133 const bool bRotation( pRotation
&&
136 if( !bOffsetting
&& !bScaling
&& !bRotation
)
137 return false; // nothing to do
139 if( rOutdevState
.clip
.count() )
141 // general polygon case
143 ::basegfx::B2DPolyPolygon
aLocalClip( rOutdevState
.clip
);
144 ::basegfx::B2DHomMatrix aTransform
;
147 aTransform
.translate( -rOffset
.getX(),
150 aTransform
.scale( 1.0/pScaling
->getX(), 1.0/pScaling
->getY() );
153 aTransform
.rotate( - *pRotation
);
155 aLocalClip
.transform( aTransform
);
157 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
158 rCanvas
->getUNOCanvas()->getDevice(),
163 else if( !rOutdevState
.clipRect
.IsEmpty() )
167 const ::Rectangle
aLocalClipRect( rOutdevState
.clipRect
);
171 // rotation involved - convert to polygon first,
172 // then transform that
173 ::basegfx::B2DPolygon
aLocalClip(
174 ::basegfx::tools::createPolygonFromRect(
175 ::basegfx::B2DRectangle(
176 (double)(aLocalClipRect
.Left()),
177 (double)(aLocalClipRect
.Top()),
178 (double)(aLocalClipRect
.Right()),
179 (double)(aLocalClipRect
.Bottom()) ) ) );
180 ::basegfx::B2DHomMatrix aTransform
;
183 aTransform
.translate( -rOffset
.getX(),
186 aTransform
.scale( 1.0/pScaling
->getX(), 1.0/pScaling
->getY() );
188 aTransform
.rotate( - *pRotation
);
190 aLocalClip
.transform( aTransform
);
192 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
193 rCanvas
->getUNOCanvas()->getDevice(),
194 ::basegfx::B2DPolyPolygon( aLocalClip
) );
198 // scale and offset - do it on the fly, have to
199 // convert to float anyway.
200 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
201 rCanvas
->getUNOCanvas()->getDevice(),
202 ::basegfx::B2DPolyPolygon(
203 ::basegfx::tools::createPolygonFromRect(
204 ::basegfx::B2DRectangle(
205 (double)(aLocalClipRect
.Left() - rOffset
.getX())/pScaling
->getX(),
206 (double)(aLocalClipRect
.Top() - rOffset
.getY())/pScaling
->getY(),
207 (double)(aLocalClipRect
.Right() - rOffset
.getX())/pScaling
->getX(),
208 (double)(aLocalClipRect
.Bottom() - rOffset
.getY())/pScaling
->getY() ) ) ) );
212 // offset only - do it on the fly, have to convert
214 o_rRenderState
.Clip
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
215 rCanvas
->getUNOCanvas()->getDevice(),
216 ::basegfx::B2DPolyPolygon(
217 ::basegfx::tools::createPolygonFromRect(
218 ::basegfx::B2DRectangle( aLocalClipRect
.Left() - rOffset
.getX(),
219 aLocalClipRect
.Top() - rOffset
.getY(),
220 aLocalClipRect
.Right() - rOffset
.getX(),
221 aLocalClipRect
.Bottom() - rOffset
.getY() ) ) ) );
227 // empty clip, nothing to do
231 // create overline/underline/strikeout line info struct
232 TextLineInfo
createTextLineInfo( const ::VirtualDevice
& rVDev
,
233 const ::cppcanvas::internal::OutDevState
& rState
)
235 const bool bOldMode( rVDev
.IsMapModeEnabled() );
237 // #i68512# Force metric regeneration with mapmode enabled
238 // (prolly OutDev bug)
239 rVDev
.GetFontMetric();
241 // will restore map mode below
242 const_cast< ::VirtualDevice
& >(rVDev
).EnableMapMode( false );
244 const ::FontMetric aMetric
= rVDev
.GetFontMetric();
246 TextLineInfo
aTextInfo(
247 (aMetric
.GetDescent() + 2) / 4.0,
248 ((aMetric
.GetIntLeading() + 1.5) / 3.0),
249 (aMetric
.GetIntLeading() / 2.0) - aMetric
.GetAscent(),
250 aMetric
.GetDescent() / 2.0,
251 (aMetric
.GetIntLeading() - aMetric
.GetAscent()) / 3.0,
252 rState
.textOverlineStyle
,
253 rState
.textUnderlineStyle
,
254 rState
.textStrikeoutStyle
);
256 const_cast< ::VirtualDevice
& >(rVDev
).EnableMapMode( bOldMode
);
263 void appendRect( ::basegfx::B2DPolyPolygon
& o_rPoly
,
264 const ::basegfx::B2DPoint
& rStartPos
,
270 const double x( rStartPos
.getX() );
271 const double y( rStartPos
.getY() );
274 ::basegfx::tools::createPolygonFromRect(
275 ::basegfx::B2DRectangle( x
+ nX1
, y
+ nY1
, x
+ nX2
, y
+ nY2
) ) );
278 void appendRect( ::basegfx::B2DPolyPolygon
& o_rPoly
,
285 ::basegfx::tools::createPolygonFromRect(
286 ::basegfx::B2DRectangle( nX1
, nY1
, nX2
, nY2
) ) );
289 void appendDashes( ::basegfx::B2DPolyPolygon
& o_rPoly
,
292 const double nLineWidth
,
293 const double nLineHeight
,
294 const double nDashWidth
,
295 const double nDashSkip
)
297 const sal_Int32
nNumLoops(
298 static_cast< sal_Int32
>(
300 nLineWidth
/ nDashSkip
) + .5) );
303 for( sal_Int32 i
=0; i
<nNumLoops
; ++i
)
307 x
+ nDashWidth
, nY
+ nLineHeight
);
314 // create line actions for text such as underline and
316 ::basegfx::B2DPolyPolygon
createTextLinesPolyPolygon( const ::basegfx::B2DPoint
& rStartPos
,
317 const double& rLineWidth
,
318 const TextLineInfo
& rTextLineInfo
)
320 // fill the polypolygon with all text lines
321 ::basegfx::B2DPolyPolygon aTextLinesPolyPoly
;
323 switch( rTextLineInfo
.mnOverlineStyle
)
325 case UNDERLINE_NONE
: // nothing to do
326 // FALLTHROUGH intended
327 case UNDERLINE_DONTKNOW
:
330 case UNDERLINE_SMALLWAVE
: // TODO(F3): NYI
331 // FALLTHROUGH intended
332 case UNDERLINE_WAVE
: // TODO(F3): NYI
333 // FALLTHROUGH intended
334 case UNDERLINE_SINGLE
:
339 rTextLineInfo
.mnOverlineOffset
,
341 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
);
344 case UNDERLINE_BOLDDOTTED
: // TODO(F3): NYI
345 // FALLTHROUGH intended
346 case UNDERLINE_BOLDDASH
: // TODO(F3): NYI
347 // FALLTHROUGH intended
348 case UNDERLINE_BOLDLONGDASH
: // TODO(F3): NYI
349 // FALLTHROUGH intended
350 case UNDERLINE_BOLDDASHDOT
: // TODO(F3): NYI
351 // FALLTHROUGH intended
352 case UNDERLINE_BOLDDASHDOTDOT
:// TODO(F3): NYI
353 // FALLTHROUGH intended
354 case UNDERLINE_BOLDWAVE
: // TODO(F3): NYI
355 // FALLTHROUGH intended
361 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
,
363 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
);
366 case UNDERLINE_DOUBLEWAVE
: // TODO(F3): NYI
367 // FALLTHROUGH intended
368 case UNDERLINE_DOUBLE
:
373 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
* 2.0 ,
375 rTextLineInfo
.mnOverlineOffset
- rTextLineInfo
.mnOverlineHeight
);
381 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
,
383 rTextLineInfo
.mnOverlineOffset
+ rTextLineInfo
.mnOverlineHeight
* 2.0 );
386 case UNDERLINE_DASHDOTDOT
: // TODO(F3): NYI
387 // FALLTHROUGH intended
388 case UNDERLINE_DOTTED
:
392 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
394 rTextLineInfo
.mnOverlineHeight
,
395 rTextLineInfo
.mnOverlineHeight
,
396 2*rTextLineInfo
.mnOverlineHeight
);
399 case UNDERLINE_DASHDOT
: // TODO(F3): NYI
400 // FALLTHROUGH intended
405 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
407 rTextLineInfo
.mnOverlineHeight
,
408 3*rTextLineInfo
.mnOverlineHeight
,
409 6*rTextLineInfo
.mnOverlineHeight
);
412 case UNDERLINE_LONGDASH
:
416 rStartPos
.getY() + rTextLineInfo
.mnOverlineOffset
,
418 rTextLineInfo
.mnOverlineHeight
,
419 6*rTextLineInfo
.mnOverlineHeight
,
420 12*rTextLineInfo
.mnOverlineHeight
);
424 ENSURE_OR_THROW( false,
425 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
428 switch( rTextLineInfo
.mnUnderlineStyle
)
430 case UNDERLINE_NONE
: // nothing to do
431 // FALLTHROUGH intended
432 case UNDERLINE_DONTKNOW
:
435 case UNDERLINE_SMALLWAVE
: // TODO(F3): NYI
436 // FALLTHROUGH intended
437 case UNDERLINE_WAVE
: // TODO(F3): NYI
438 // FALLTHROUGH intended
439 case UNDERLINE_SINGLE
:
444 rTextLineInfo
.mnUnderlineOffset
,
446 rTextLineInfo
.mnUnderlineOffset
+ rTextLineInfo
.mnLineHeight
);
449 case UNDERLINE_BOLDDOTTED
: // TODO(F3): NYI
450 // FALLTHROUGH intended
451 case UNDERLINE_BOLDDASH
: // TODO(F3): NYI
452 // FALLTHROUGH intended
453 case UNDERLINE_BOLDLONGDASH
: // TODO(F3): NYI
454 // FALLTHROUGH intended
455 case UNDERLINE_BOLDDASHDOT
: // TODO(F3): NYI
456 // FALLTHROUGH intended
457 case UNDERLINE_BOLDDASHDOTDOT
:// TODO(F3): NYI
458 // FALLTHROUGH intended
459 case UNDERLINE_BOLDWAVE
: // TODO(F3): NYI
460 // FALLTHROUGH intended
466 rTextLineInfo
.mnUnderlineOffset
,
468 rTextLineInfo
.mnUnderlineOffset
+ 2*rTextLineInfo
.mnLineHeight
);
471 case UNDERLINE_DOUBLEWAVE
: // TODO(F3): NYI
472 // FALLTHROUGH intended
473 case UNDERLINE_DOUBLE
:
478 rTextLineInfo
.mnUnderlineOffset
- rTextLineInfo
.mnLineHeight
,
480 rTextLineInfo
.mnUnderlineOffset
);
486 rTextLineInfo
.mnUnderlineOffset
+ 2*rTextLineInfo
.mnLineHeight
,
488 rTextLineInfo
.mnUnderlineOffset
+ 3*rTextLineInfo
.mnLineHeight
);
491 case UNDERLINE_DASHDOTDOT
: // TODO(F3): NYI
492 // FALLTHROUGH intended
493 case UNDERLINE_DOTTED
:
497 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
499 rTextLineInfo
.mnLineHeight
,
500 rTextLineInfo
.mnLineHeight
,
501 2*rTextLineInfo
.mnLineHeight
);
504 case UNDERLINE_DASHDOT
: // TODO(F3): NYI
505 // FALLTHROUGH intended
510 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
512 rTextLineInfo
.mnLineHeight
,
513 3*rTextLineInfo
.mnLineHeight
,
514 6*rTextLineInfo
.mnLineHeight
);
517 case UNDERLINE_LONGDASH
:
521 rStartPos
.getY() + rTextLineInfo
.mnUnderlineOffset
,
523 rTextLineInfo
.mnLineHeight
,
524 6*rTextLineInfo
.mnLineHeight
,
525 12*rTextLineInfo
.mnLineHeight
);
529 ENSURE_OR_THROW( false,
530 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
533 switch( rTextLineInfo
.mnStrikeoutStyle
)
535 case STRIKEOUT_NONE
: // nothing to do
536 // FALLTHROUGH intended
537 case STRIKEOUT_DONTKNOW
:
540 case STRIKEOUT_SLASH
: // TODO(Q1): we should handle this in the text layer
541 // FALLTHROUGH intended
545 case STRIKEOUT_SINGLE
:
550 rTextLineInfo
.mnStrikeoutOffset
,
552 rTextLineInfo
.mnStrikeoutOffset
+ rTextLineInfo
.mnLineHeight
);
560 rTextLineInfo
.mnStrikeoutOffset
,
562 rTextLineInfo
.mnStrikeoutOffset
+ 2*rTextLineInfo
.mnLineHeight
);
565 case STRIKEOUT_DOUBLE
:
570 rTextLineInfo
.mnStrikeoutOffset
- rTextLineInfo
.mnLineHeight
,
572 rTextLineInfo
.mnStrikeoutOffset
);
578 rTextLineInfo
.mnStrikeoutOffset
+ 2*rTextLineInfo
.mnLineHeight
,
580 rTextLineInfo
.mnStrikeoutOffset
+ 3*rTextLineInfo
.mnLineHeight
);
584 ENSURE_OR_THROW( false,
585 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
588 return aTextLinesPolyPoly
;
591 ::basegfx::B2DRange
calcDevicePixelBounds( const ::basegfx::B2DRange
& rBounds
,
592 const rendering::ViewState
& viewState
,
593 const rendering::RenderState
& renderState
)
595 ::basegfx::B2DHomMatrix aTransform
;
596 ::canvas::tools::mergeViewAndRenderTransform( aTransform
,
600 ::basegfx::B2DRange aTransformedBounds
;
601 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds
,
606 // create line actions for text such as underline and
608 ::basegfx::B2DPolyPolygon
createTextLinesPolyPolygon( const double& rStartOffset
,
609 const double& rLineWidth
,
610 const TextLineInfo
& rTextLineInfo
)
612 return createTextLinesPolyPolygon(
613 ::basegfx::B2DPoint( rStartOffset
,
621 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */