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/polygon/b2dpolypolygon.hxx>
25 #include <basegfx/utils/canvastools.hxx>
26 #include <com/sun/star/rendering/FontRequest.hpp>
27 #include <com/sun/star/rendering/PanoseProportion.hpp>
28 #include <com/sun/star/rendering/XCanvasFont.hpp>
29 #include <comphelper/scopeguard.hxx>
30 #include <i18nlangtag/languagetag.hxx>
31 #include <rtl/math.hxx>
32 #include <tools/color.hxx>
33 #include <tools/diagnose_ex.h>
34 #include <tools/poly.hxx>
35 #include <vcl/canvastools.hxx>
36 #include <vcl/metric.hxx>
37 #include <vcl/sysdata.hxx>
38 #include <vcl/virdev.hxx>
40 #include <canvas/canvastools.hxx>
42 #include "dx_bitmap.hxx"
43 #include "dx_canvasfont.hxx"
44 #include "dx_impltools.hxx"
45 #include "dx_textlayout_drawhelper.hxx"
47 using namespace ::com::sun::star
;
52 TextLayoutDrawHelper::TextLayoutDrawHelper(
53 const uno::Reference
< rendering::XGraphicDevice
>& xGraphicDevice
) :
54 mxGraphicDevice(xGraphicDevice
)
58 TextLayoutDrawHelper::~TextLayoutDrawHelper()
62 void TextLayoutDrawHelper::drawText(
63 const std::shared_ptr
<Gdiplus::Graphics
>& rGraphics
,
64 const css::rendering::ViewState
& rViewState
,
65 const css::rendering::RenderState
& rRenderState
,
66 const ::basegfx::B2ISize
& rOutputOffset
,
67 const css::rendering::StringContext
& rText
,
68 const css::uno::Sequence
< double >& rLogicalAdvancements
,
69 const css::uno::Reference
<
70 css::rendering::XCanvasFont
>& rCanvasFont
,
71 const css::geometry::Matrix2D
& rFontMatrix
,
75 HDC hdc
= rGraphics
->GetHDC();
77 // issue a ReleaseHDC() when leaving the scope
78 const ::comphelper::ScopeGuard
aGuard(
79 [&rGraphics
, &hdc
]() mutable { rGraphics
->ReleaseHDC(hdc
); } );
81 SystemGraphicsData aSystemGraphicsData
;
82 aSystemGraphicsData
.nSize
= sizeof(SystemGraphicsData
);
83 aSystemGraphicsData
.hDC
= reinterpret_cast< ::HDC
>(hdc
);
84 ScopedVclPtrInstance
<VirtualDevice
> xVirtualDevice(aSystemGraphicsData
, Size(1, 1), DeviceFormat::DEFAULT
);
86 // disable font antialiasing - GDI does not handle alpha
89 xVirtualDevice
->SetAntialiasing(AntialiasingFlags::DisableText
);
93 bool test
= mxGraphicDevice
.is();
94 ENSURE_OR_THROW( test
,
95 "TextLayoutDrawHelper::drawText(): Invalid GraphicDevice" );
97 // set text color. Make sure to remove transparence part first.
98 Color
aColor( COL_WHITE
);
100 if( rRenderState
.DeviceColor
.getLength() > 2 )
101 aColor
= vcl::unotools::doubleSequenceToColor(
102 rRenderState
.DeviceColor
,
103 mxGraphicDevice
->getDeviceColorSpace());
104 aColor
.SetAlpha(255);
105 xVirtualDevice
->SetTextColor(aColor
);
108 const css::rendering::FontRequest
& rFontRequest
= rCanvasFont
->getFontRequest();
110 rFontRequest
.FontDescription
.FamilyName
,
111 rFontRequest
.FontDescription
.StyleName
,
112 Size( 0, ::basegfx::fround(rFontRequest
.CellSize
)));
114 aFont
.SetAlignment( ALIGN_BASELINE
);
115 aFont
.SetCharSet( (rFontRequest
.FontDescription
.IsSymbolFont
==css::util::TriState_YES
) ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
116 aFont
.SetVertical( rFontRequest
.FontDescription
.IsVertical
==css::util::TriState_YES
);
117 aFont
.SetWeight( static_cast<FontWeight
>(rFontRequest
.FontDescription
.FontDescription
.Weight
) );
118 aFont
.SetItalic( (rFontRequest
.FontDescription
.FontDescription
.Letterform
<=8) ? ITALIC_NONE
: ITALIC_NORMAL
);
120 rFontRequest
.FontDescription
.FontDescription
.Proportion
== rendering::PanoseProportion::MONO_SPACED
121 ? PITCH_FIXED
: PITCH_VARIABLE
);
123 aFont
.SetLanguage(LanguageTag::convertToLanguageType(rFontRequest
.Locale
));
126 aFont
.SetColor( aColor
);
127 aFont
.SetFillColor( aColor
);
129 CanvasFont::ImplRef
pFont(tools::canvasFontFromXFont(rCanvasFont
));
130 if (pFont
.is() && pFont
->getEmphasisMark())
131 aFont
.SetEmphasisMark(FontEmphasisMark(pFont
->getEmphasisMark()));
133 // adjust to stretched font
134 if(!::rtl::math::approxEqual(rFontMatrix
.m00
, rFontMatrix
.m11
))
136 const Size aSize
= xVirtualDevice
->GetFontMetric( aFont
).GetFontSize();
137 const double fDividend( rFontMatrix
.m10
+ rFontMatrix
.m11
);
138 double fStretch
= rFontMatrix
.m00
+ rFontMatrix
.m01
;
140 if( !::basegfx::fTools::equalZero( fDividend
) )
141 fStretch
/= fDividend
;
143 const sal_Int32 nNewWidth
= ::basegfx::fround( aSize
.Width() * fStretch
);
145 aFont
.SetAverageFontWidth( nNewWidth
);
149 xVirtualDevice
->SetFont(aFont
);
151 // create world transformation matrix
152 ::basegfx::B2DHomMatrix aWorldTransform
;
153 ::canvas::tools::mergeViewAndRenderTransform(aWorldTransform
, rViewState
, rRenderState
);
155 if(!rOutputOffset
.equalZero())
157 aWorldTransform
.translate(rOutputOffset
.getX(), rOutputOffset
.getY());
160 // set ViewState clipping
161 if(rViewState
.Clip
.is())
163 ::basegfx::B2DPolyPolygon
aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rViewState
.Clip
));
164 ::basegfx::B2DHomMatrix aMatrix
;
165 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix
, rViewState
.AffineTransform
);
167 if(!rOutputOffset
.equalZero())
169 aMatrix
.translate(rOutputOffset
.getX(), rOutputOffset
.getY());
172 aClipPoly
.transform(aMatrix
);
173 const vcl::Region
& rClipRegion
= vcl::Region(::tools::PolyPolygon(aClipPoly
));
174 xVirtualDevice
->IntersectClipRegion(rClipRegion
);
177 if(rRenderState
.Clip
.is())
179 ::basegfx::B2DPolyPolygon
aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rRenderState
.Clip
));
180 aClipPoly
.transform(aWorldTransform
);
181 const vcl::Region
& rClipRegion
= vcl::Region(::tools::PolyPolygon(aClipPoly
));
182 xVirtualDevice
->IntersectClipRegion(rClipRegion
);
185 // set world transform
187 aXForm
.eM11
= static_cast<FLOAT
>(aWorldTransform
.get(0, 0));
188 aXForm
.eM12
= static_cast<FLOAT
>(aWorldTransform
.get(1, 0));
189 aXForm
.eM21
= static_cast<FLOAT
>(aWorldTransform
.get(0, 1));
190 aXForm
.eM22
= static_cast<FLOAT
>(aWorldTransform
.get(1, 1));
191 aXForm
.eDx
= static_cast<FLOAT
>(aWorldTransform
.get(0, 2));
192 aXForm
.eDy
= static_cast<FLOAT
>(aWorldTransform
.get(1, 2));
194 // TODO(F3): This is NOT supported on 95/98/ME!
195 SetGraphicsMode(hdc
, GM_ADVANCED
);
196 SetTextAlign(hdc
, TA_BASELINE
);
197 SetWorldTransform(hdc
, &aXForm
);
199 // use an empty StartPosition for text rendering
200 const Point
aEmptyPoint(0, 0);
203 const OUString
aText(rText
.Text
);
205 if( rLogicalAdvancements
.getLength() )
207 // create the DXArray
208 const sal_Int32
nLen( rLogicalAdvancements
.getLength() );
209 std::unique_ptr
<::tools::Long
[]> pDXArray( new ::tools::Long
[nLen
] );
210 for( sal_Int32 i
=0; i
<nLen
; ++i
)
211 pDXArray
[i
] = basegfx::fround( rLogicalAdvancements
[i
] );
214 xVirtualDevice
->DrawTextArray( aEmptyPoint
,
219 bIsRTL
? SalLayoutFlags::BiDiRtl
: SalLayoutFlags::NONE
);
224 xVirtualDevice
->DrawText( aEmptyPoint
,
232 geometry::RealRectangle2D
TextLayoutDrawHelper::queryTextBounds( const rendering::StringContext
& rText
,
233 const uno::Sequence
< double >& rLogicalAdvancements
,
234 const uno::Reference
< rendering::XCanvasFont
>& rCanvasFont
,
235 const geometry::Matrix2D
& rFontMatrix
)
238 return geometry::RealRectangle2D();
240 // TODO(F1): Fetching default screen DC here, will yield wrong
241 // metrics when e.g. formatting for a printer!
242 SystemGraphicsData aSystemGraphicsData
;
243 aSystemGraphicsData
.nSize
= sizeof(SystemGraphicsData
);
244 aSystemGraphicsData
.hDC
= reinterpret_cast< ::HDC
>(GetDC( nullptr ));
245 ScopedVclPtrInstance
<VirtualDevice
> xVirtualDevice(aSystemGraphicsData
, Size(1, 1), DeviceFormat::DEFAULT
);
248 const css::rendering::FontRequest
& rFontRequest
= rCanvasFont
->getFontRequest();
250 rFontRequest
.FontDescription
.FamilyName
,
251 rFontRequest
.FontDescription
.StyleName
,
252 Size( 0, ::basegfx::fround(rFontRequest
.CellSize
)));
254 aFont
.SetAlignment( ALIGN_BASELINE
);
255 aFont
.SetCharSet( (rFontRequest
.FontDescription
.IsSymbolFont
==css::util::TriState_YES
) ? RTL_TEXTENCODING_SYMBOL
: RTL_TEXTENCODING_UNICODE
);
256 aFont
.SetVertical( rFontRequest
.FontDescription
.IsVertical
==css::util::TriState_YES
);
257 aFont
.SetWeight( static_cast<FontWeight
>(rFontRequest
.FontDescription
.FontDescription
.Weight
) );
258 aFont
.SetItalic( (rFontRequest
.FontDescription
.FontDescription
.Letterform
<=8) ? ITALIC_NONE
: ITALIC_NORMAL
);
260 rFontRequest
.FontDescription
.FontDescription
.Proportion
== rendering::PanoseProportion::MONO_SPACED
261 ? PITCH_FIXED
: PITCH_VARIABLE
);
263 // adjust to stretched font
264 if(!::rtl::math::approxEqual(rFontMatrix
.m00
, rFontMatrix
.m11
))
266 const Size aSize
= xVirtualDevice
->GetFontMetric( aFont
).GetFontSize();
267 const double fDividend( rFontMatrix
.m10
+ rFontMatrix
.m11
);
268 double fStretch
= rFontMatrix
.m00
+ rFontMatrix
.m01
;
270 if( !::basegfx::fTools::equalZero( fDividend
) )
271 fStretch
/= fDividend
;
273 const sal_Int32 nNewWidth
= ::basegfx::fround( aSize
.Width() * fStretch
);
275 aFont
.SetAverageFontWidth( nNewWidth
);
278 CanvasFont::ImplRef
pFont(tools::canvasFontFromXFont(rCanvasFont
));
279 if (pFont
.is() && pFont
->getEmphasisMark())
280 aFont
.SetEmphasisMark(FontEmphasisMark(pFont
->getEmphasisMark()));
283 xVirtualDevice
->SetFont(aFont
);
285 // need metrics for Y offset, the XCanvas always renders
286 // relative to baseline
287 const ::FontMetric
& aMetric( xVirtualDevice
->GetFontMetric() );
289 const sal_Int32
nAboveBaseline( -aMetric
.GetInternalLeading() - aMetric
.GetAscent() );
290 const sal_Int32
nBelowBaseline( aMetric
.GetDescent() );
292 if( rLogicalAdvancements
.getLength() )
294 return geometry::RealRectangle2D( 0, nAboveBaseline
,
295 rLogicalAdvancements
[ rLogicalAdvancements
.getLength()-1 ],
300 return geometry::RealRectangle2D( 0, nAboveBaseline
,
301 xVirtualDevice
->GetTextWidth(
303 ::canvas::tools::numeric_cast
<sal_uInt16
>(rText
.StartPosition
),
304 ::canvas::tools::numeric_cast
<sal_uInt16
>(rText
.Length
) ),
310 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */