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 .
20 #include <sal/config.h>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/polygon/b2dpolypolygon.hxx>
26 #include <basegfx/utils/canvastools.hxx>
27 #include <com/sun/star/rendering/FontRequest.hpp>
28 #include <com/sun/star/rendering/PanoseProportion.hpp>
29 #include <com/sun/star/rendering/XCanvasFont.hpp>
30 #include <comphelper/scopeguard.hxx>
31 #include <i18nlangtag/languagetag.hxx>
32 #include <rtl/math.hxx>
33 #include <tools/color.hxx>
34 #include <comphelper/diagnose_ex.hxx>
35 #include <tools/poly.hxx>
36 #include <vcl/canvastools.hxx>
37 #include <vcl/kernarray.hxx>
38 #include <vcl/metric.hxx>
39 #include <vcl/sysdata.hxx>
40 #include <vcl/virdev.hxx>
42 #include <canvas/canvastools.hxx>
44 #include "dx_bitmap.hxx"
45 #include "dx_canvasfont.hxx"
46 #include "dx_impltools.hxx"
47 #include "dx_textlayout_drawhelper.hxx"
49 using namespace ::com::sun::star
;
54 TextLayoutDrawHelper::TextLayoutDrawHelper(
55 const uno::Reference
< rendering::XGraphicDevice
>& xGraphicDevice
) :
56 mxGraphicDevice(xGraphicDevice
)
60 TextLayoutDrawHelper::~TextLayoutDrawHelper()
64 void TextLayoutDrawHelper::drawText(
65 const std::shared_ptr
<Gdiplus::Graphics
>& rGraphics
,
66 const css::rendering::ViewState
& rViewState
,
67 const css::rendering::RenderState
& rRenderState
,
68 const ::basegfx::B2ISize
& rOutputOffset
,
69 const css::rendering::StringContext
& rText
,
70 const css::uno::Sequence
< double >& rLogicalAdvancements
,
71 const css::uno::Sequence
< sal_Bool
>& rKashidaPositions
,
72 const css::uno::Reference
<
73 css::rendering::XCanvasFont
>& rCanvasFont
,
74 const css::geometry::Matrix2D
& rFontMatrix
,
78 HDC hdc
= rGraphics
->GetHDC();
80 // issue a ReleaseHDC() when leaving the scope
81 const ::comphelper::ScopeGuard
aGuard(
82 [&rGraphics
, &hdc
]() mutable { rGraphics
->ReleaseHDC(hdc
); } );
84 SystemGraphicsData aSystemGraphicsData
;
85 aSystemGraphicsData
.nSize
= sizeof(SystemGraphicsData
);
86 aSystemGraphicsData
.hDC
= reinterpret_cast< ::HDC
>(hdc
);
87 ScopedVclPtrInstance
<VirtualDevice
> xVirtualDevice(aSystemGraphicsData
, Size(1, 1), DeviceFormat::WITHOUT_ALPHA
);
89 // disable font antialiasing - GDI does not handle alpha
92 xVirtualDevice
->SetAntialiasing(AntialiasingFlags::DisableText
);
96 bool test
= mxGraphicDevice
.is();
97 ENSURE_OR_THROW( test
,
98 "TextLayoutDrawHelper::drawText(): Invalid GraphicDevice" );
100 // set text color. Make sure to remove transparence part first.
101 Color
aColor( COL_WHITE
);
103 if( rRenderState
.DeviceColor
.getLength() > 2 )
104 aColor
= vcl::unotools::doubleSequenceToColor(
105 rRenderState
.DeviceColor
,
106 mxGraphicDevice
->getDeviceColorSpace());
107 aColor
.SetAlpha(255);
108 xVirtualDevice
->SetTextColor(aColor
);
111 const css::rendering::FontRequest
& rFontRequest
= rCanvasFont
->getFontRequest();
113 rFontRequest
.FontDescription
.FamilyName
,
114 rFontRequest
.FontDescription
.StyleName
,
115 Size( 0, ::basegfx::fround
<::tools::Long
>(rFontRequest
.CellSize
)));
117 aFont
.SetAlignment( ALIGN_BASELINE
);
118 aFont
.SetCharSet( (rFontRequest
.FontDescription
.IsSymbolFont
==css::util::TriState_YES
) ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
119 aFont
.SetVertical( rFontRequest
.FontDescription
.IsVertical
==css::util::TriState_YES
);
120 aFont
.SetWeight( static_cast<FontWeight
>(rFontRequest
.FontDescription
.FontDescription
.Weight
) );
121 aFont
.SetItalic( (rFontRequest
.FontDescription
.FontDescription
.Letterform
<=8) ? ITALIC_NONE
: ITALIC_NORMAL
);
123 rFontRequest
.FontDescription
.FontDescription
.Proportion
== rendering::PanoseProportion::MONO_SPACED
124 ? PITCH_FIXED
: PITCH_VARIABLE
);
126 aFont
.SetLanguage(LanguageTag::convertToLanguageType(rFontRequest
.Locale
));
129 aFont
.SetColor( aColor
);
130 aFont
.SetFillColor( aColor
);
132 CanvasFont::ImplRef
pFont(tools::canvasFontFromXFont(rCanvasFont
));
133 if (pFont
.is() && pFont
->getEmphasisMark())
134 aFont
.SetEmphasisMark(FontEmphasisMark(pFont
->getEmphasisMark()));
136 // adjust to stretched font
137 if(!::rtl::math::approxEqual(rFontMatrix
.m00
, rFontMatrix
.m11
))
139 const Size aSize
= xVirtualDevice
->GetFontMetric( aFont
).GetFontSize();
140 const double fDividend( rFontMatrix
.m10
+ rFontMatrix
.m11
);
141 double fStretch
= rFontMatrix
.m00
+ rFontMatrix
.m01
;
143 if( !::basegfx::fTools::equalZero( fDividend
) )
144 fStretch
/= fDividend
;
146 const sal_Int32 nNewWidth
= ::basegfx::fround( aSize
.Width() * fStretch
);
148 aFont
.SetAverageFontWidth( nNewWidth
);
152 xVirtualDevice
->SetFont(aFont
);
154 // create world transformation matrix
155 ::basegfx::B2DHomMatrix aWorldTransform
;
156 ::canvas::tools::mergeViewAndRenderTransform(aWorldTransform
, rViewState
, rRenderState
);
158 if(!rOutputOffset
.equalZero())
160 aWorldTransform
.translate(rOutputOffset
.getWidth(), rOutputOffset
.getHeight());
163 // set ViewState clipping
164 if(rViewState
.Clip
.is())
166 ::basegfx::B2DPolyPolygon
aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rViewState
.Clip
));
167 ::basegfx::B2DHomMatrix aMatrix
;
168 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix
, rViewState
.AffineTransform
);
170 if(!rOutputOffset
.equalZero())
172 aMatrix
.translate(rOutputOffset
.getWidth(), rOutputOffset
.getHeight());
175 aClipPoly
.transform(aMatrix
);
176 const vcl::Region
& rClipRegion
= vcl::Region(::tools::PolyPolygon(aClipPoly
));
177 xVirtualDevice
->IntersectClipRegion(rClipRegion
);
180 if(rRenderState
.Clip
.is())
182 ::basegfx::B2DPolyPolygon
aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rRenderState
.Clip
));
183 aClipPoly
.transform(aWorldTransform
);
184 const vcl::Region
& rClipRegion
= vcl::Region(::tools::PolyPolygon(aClipPoly
));
185 xVirtualDevice
->IntersectClipRegion(rClipRegion
);
188 // set world transform
190 aXForm
.eM11
= static_cast<FLOAT
>(aWorldTransform
.get(0, 0));
191 aXForm
.eM12
= static_cast<FLOAT
>(aWorldTransform
.get(1, 0));
192 aXForm
.eM21
= static_cast<FLOAT
>(aWorldTransform
.get(0, 1));
193 aXForm
.eM22
= static_cast<FLOAT
>(aWorldTransform
.get(1, 1));
194 aXForm
.eDx
= static_cast<FLOAT
>(aWorldTransform
.get(0, 2));
195 aXForm
.eDy
= static_cast<FLOAT
>(aWorldTransform
.get(1, 2));
197 // TODO(F3): This is NOT supported on 95/98/ME!
198 SetGraphicsMode(hdc
, GM_ADVANCED
);
199 SetTextAlign(hdc
, TA_BASELINE
);
200 SetWorldTransform(hdc
, &aXForm
);
202 // use an empty StartPosition for text rendering
203 const Point
aEmptyPoint(0, 0);
206 const OUString
aText(rText
.Text
);
208 if( rLogicalAdvancements
.getLength() )
210 // create the DXArray
211 const sal_Int32
nLen( rLogicalAdvancements
.getLength() );
213 DXArray
.reserve(nLen
);
214 for( sal_Int32 i
=0; i
<nLen
; ++i
)
215 DXArray
.push_back(basegfx::fround(rLogicalAdvancements
[i
]));
217 std::span
<const sal_Bool
> aKashidaArray(rKashidaPositions
.getConstArray(), rKashidaPositions
.getLength());
220 xVirtualDevice
->DrawTextArray( aEmptyPoint
,
226 bIsRTL
? SalLayoutFlags::BiDiRtl
: SalLayoutFlags::NONE
);
231 xVirtualDevice
->DrawText( aEmptyPoint
,
239 geometry::RealRectangle2D
TextLayoutDrawHelper::queryTextBounds( const rendering::StringContext
& rText
,
240 const uno::Sequence
< double >& rLogicalAdvancements
,
241 const uno::Reference
< rendering::XCanvasFont
>& rCanvasFont
,
242 const geometry::Matrix2D
& rFontMatrix
)
245 return geometry::RealRectangle2D();
247 // TODO(F1): Fetching default screen DC here, will yield wrong
248 // metrics when e.g. formatting for a printer!
249 SystemGraphicsData aSystemGraphicsData
;
250 aSystemGraphicsData
.nSize
= sizeof(SystemGraphicsData
);
251 aSystemGraphicsData
.hDC
= reinterpret_cast< ::HDC
>(GetDC( nullptr ));
252 ScopedVclPtrInstance
<VirtualDevice
> xVirtualDevice(aSystemGraphicsData
, Size(1, 1), DeviceFormat::WITHOUT_ALPHA
);
255 const css::rendering::FontRequest
& rFontRequest
= rCanvasFont
->getFontRequest();
257 rFontRequest
.FontDescription
.FamilyName
,
258 rFontRequest
.FontDescription
.StyleName
,
259 Size( 0, ::basegfx::fround
<::tools::Long
>(rFontRequest
.CellSize
)));
261 aFont
.SetAlignment( ALIGN_BASELINE
);
262 aFont
.SetCharSet( (rFontRequest
.FontDescription
.IsSymbolFont
==css::util::TriState_YES
) ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
263 aFont
.SetVertical( rFontRequest
.FontDescription
.IsVertical
==css::util::TriState_YES
);
264 aFont
.SetWeight( static_cast<FontWeight
>(rFontRequest
.FontDescription
.FontDescription
.Weight
) );
265 aFont
.SetItalic( (rFontRequest
.FontDescription
.FontDescription
.Letterform
<=8) ? ITALIC_NONE
: ITALIC_NORMAL
);
267 rFontRequest
.FontDescription
.FontDescription
.Proportion
== rendering::PanoseProportion::MONO_SPACED
268 ? PITCH_FIXED
: PITCH_VARIABLE
);
270 // adjust to stretched font
271 if(!::rtl::math::approxEqual(rFontMatrix
.m00
, rFontMatrix
.m11
))
273 const Size aSize
= xVirtualDevice
->GetFontMetric( aFont
).GetFontSize();
274 const double fDividend( rFontMatrix
.m10
+ rFontMatrix
.m11
);
275 double fStretch
= rFontMatrix
.m00
+ rFontMatrix
.m01
;
277 if( !::basegfx::fTools::equalZero( fDividend
) )
278 fStretch
/= fDividend
;
280 const sal_Int32 nNewWidth
= ::basegfx::fround( aSize
.Width() * fStretch
);
282 aFont
.SetAverageFontWidth( nNewWidth
);
285 CanvasFont::ImplRef
pFont(tools::canvasFontFromXFont(rCanvasFont
));
286 if (pFont
.is() && pFont
->getEmphasisMark())
287 aFont
.SetEmphasisMark(FontEmphasisMark(pFont
->getEmphasisMark()));
290 xVirtualDevice
->SetFont(aFont
);
292 // need metrics for Y offset, the XCanvas always renders
293 // relative to baseline
294 const ::FontMetric
& aMetric( xVirtualDevice
->GetFontMetric() );
296 const sal_Int32
nAboveBaseline( -aMetric
.GetInternalLeading() - aMetric
.GetAscent() );
297 const sal_Int32
nBelowBaseline( aMetric
.GetDescent() );
299 if( rLogicalAdvancements
.getLength() )
301 return geometry::RealRectangle2D( 0, nAboveBaseline
,
302 rLogicalAdvancements
[ rLogicalAdvancements
.getLength()-1 ],
307 return geometry::RealRectangle2D( 0, nAboveBaseline
,
308 xVirtualDevice
->GetTextWidth(
310 ::canvas::tools::numeric_cast
<sal_uInt16
>(rText
.StartPosition
),
311 ::canvas::tools::numeric_cast
<sal_uInt16
>(rText
.Length
) ),
317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */