LibreOffice Module canvas (master)  1
dx_textlayout_drawhelper.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <memory>
23 
27 #include <com/sun/star/rendering/FontRequest.hpp>
28 #include <com/sun/star/rendering/PanoseProportion.hpp>
29 #include <com/sun/star/rendering/XCanvasFont.hpp>
32 #include <rtl/math.hxx>
33 #include <tools/color.hxx>
34 #include <tools/diagnose_ex.h>
35 #include <tools/poly.hxx>
36 #include <vcl/canvastools.hxx>
37 #include <vcl/metric.hxx>
38 #include <vcl/sysdata.hxx>
39 #include <vcl/virdev.hxx>
40 
41 #include <canvas/canvastools.hxx>
42 
43 #include "dx_bitmap.hxx"
44 #include "dx_canvasfont.hxx"
45 #include "dx_impltools.hxx"
47 
48 using namespace ::com::sun::star;
49 
50 
51 namespace dxcanvas
52 {
54  const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice ) :
55  mxGraphicDevice(xGraphicDevice)
56  {
57  }
58 
60  {
61  }
62 
64  const std::shared_ptr<Gdiplus::Graphics>& rGraphics,
65  const css::rendering::ViewState& rViewState,
66  const css::rendering::RenderState& rRenderState,
67  const ::basegfx::B2ISize& rOutputOffset,
68  const css::rendering::StringContext& rText,
69  const css::uno::Sequence< double >& rLogicalAdvancements,
70  const css::uno::Reference<
71  css::rendering::XCanvasFont >& rCanvasFont,
72  const css::geometry::Matrix2D& rFontMatrix,
73  bool bAlphaSurface,
74  bool bIsRTL)
75  {
76  HDC hdc = rGraphics->GetHDC();
77 
78  // issue a ReleaseHDC() when leaving the scope
79  const ::comphelper::ScopeGuard aGuard(
80  [&rGraphics, &hdc]() mutable { rGraphics->ReleaseHDC(hdc); } );
81 
82  SystemGraphicsData aSystemGraphicsData;
83  aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
84  aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(hdc);
85  ScopedVclPtrInstance<VirtualDevice> xVirtualDevice(aSystemGraphicsData, Size(1, 1), DeviceFormat::DEFAULT);
86 
87  // disable font antialiasing - GDI does not handle alpha
88  // surfaces properly.
89  if( bAlphaSurface )
90  xVirtualDevice->SetAntialiasing(AntialiasingFlags::DisableText);
91 
92  if(rText.Length)
93  {
94  bool test = mxGraphicDevice.is();
95  ENSURE_OR_THROW( test,
96  "TextLayoutDrawHelper::drawText(): Invalid GraphicDevice" );
97 
98  // set text color. Make sure to remove transparence part first.
99  Color aColor( COL_WHITE );
100 
101  if( rRenderState.DeviceColor.getLength() > 2 )
103  rRenderState.DeviceColor,
104  mxGraphicDevice->getDeviceColorSpace());
105  aColor.SetAlpha(255);
106  xVirtualDevice->SetTextColor(aColor);
107 
108  // create the font
109  const css::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest();
110  vcl::Font aFont(
111  rFontRequest.FontDescription.FamilyName,
112  rFontRequest.FontDescription.StyleName,
113  Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
114 
115  aFont.SetAlignment( ALIGN_BASELINE );
116  aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
117  aFont.SetVertical( rFontRequest.FontDescription.IsVertical==css::util::TriState_YES );
118  aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
119  aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
120  aFont.SetPitch(
121  rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
123 
124  aFont.SetLanguage(LanguageTag::convertToLanguageType(rFontRequest.Locale));
125 
126  // setup font color
127  aFont.SetColor( aColor );
128  aFont.SetFillColor( aColor );
129 
131  if (pFont.is() && pFont->getEmphasisMark())
132  aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark()));
133 
134  // adjust to stretched font
135  if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
136  {
137  const Size aSize = xVirtualDevice->GetFontMetric( aFont ).GetFontSize();
138  const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
139  double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
140 
141  if( !::basegfx::fTools::equalZero( fDividend) )
142  fStretch /= fDividend;
143 
144  const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
145 
146  aFont.SetAverageFontWidth( nNewWidth );
147  }
148 
149  // set font
150  xVirtualDevice->SetFont(aFont);
151 
152  // create world transformation matrix
153  ::basegfx::B2DHomMatrix aWorldTransform;
154  ::canvas::tools::mergeViewAndRenderTransform(aWorldTransform, rViewState, rRenderState);
155 
156  if(!rOutputOffset.equalZero())
157  {
158  aWorldTransform.translate(rOutputOffset.getX(), rOutputOffset.getY());
159  }
160 
161  // set ViewState clipping
162  if(rViewState.Clip.is())
163  {
165  ::basegfx::B2DHomMatrix aMatrix;
166  ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix, rViewState.AffineTransform );
167 
168  if(!rOutputOffset.equalZero())
169  {
170  aMatrix.translate(rOutputOffset.getX(), rOutputOffset.getY());
171  }
172 
173  aClipPoly.transform(aMatrix);
174  const vcl::Region& rClipRegion = vcl::Region(::tools::PolyPolygon(aClipPoly));
175  xVirtualDevice->IntersectClipRegion(rClipRegion);
176  }
177 
178  if(rRenderState.Clip.is())
179  {
181  aClipPoly.transform(aWorldTransform);
182  const vcl::Region& rClipRegion = vcl::Region(::tools::PolyPolygon(aClipPoly));
183  xVirtualDevice->IntersectClipRegion(rClipRegion);
184  }
185 
186  // set world transform
187  XFORM aXForm;
188  aXForm.eM11 = static_cast<FLOAT>(aWorldTransform.get(0, 0));
189  aXForm.eM12 = static_cast<FLOAT>(aWorldTransform.get(1, 0));
190  aXForm.eM21 = static_cast<FLOAT>(aWorldTransform.get(0, 1));
191  aXForm.eM22 = static_cast<FLOAT>(aWorldTransform.get(1, 1));
192  aXForm.eDx = static_cast<FLOAT>(aWorldTransform.get(0, 2));
193  aXForm.eDy = static_cast<FLOAT>(aWorldTransform.get(1, 2));
194 
195  // TODO(F3): This is NOT supported on 95/98/ME!
196  SetGraphicsMode(hdc, GM_ADVANCED);
197  SetTextAlign(hdc, TA_BASELINE);
198  SetWorldTransform(hdc, &aXForm);
199 
200  // use an empty StartPosition for text rendering
201  const Point aEmptyPoint(0, 0);
202 
203  // create the String
204  const OUString aText(rText.Text);
205 
206  if( rLogicalAdvancements.getLength() )
207  {
208  // create the DXArray
209  const sal_Int32 nLen( rLogicalAdvancements.getLength() );
210  std::vector<sal_Int32> DXArray( nLen );
211  for( sal_Int32 i=0; i<nLen; ++i )
212  DXArray[i] = basegfx::fround( rLogicalAdvancements[i] );
213 
214  // draw the String
215  xVirtualDevice->DrawTextArray( aEmptyPoint,
216  aText,
217  DXArray,
218  rText.StartPosition,
219  rText.Length,
220  bIsRTL ? SalLayoutFlags::BiDiRtl : SalLayoutFlags::NONE);
221  }
222  else
223  {
224  // draw the String
225  xVirtualDevice->DrawText( aEmptyPoint,
226  aText,
227  rText.StartPosition,
228  rText.Length );
229  }
230  }
231  }
232 
233  geometry::RealRectangle2D TextLayoutDrawHelper::queryTextBounds( const rendering::StringContext& rText,
234  const uno::Sequence< double >& rLogicalAdvancements,
235  const uno::Reference< rendering::XCanvasFont >& rCanvasFont,
236  const geometry::Matrix2D& rFontMatrix )
237  {
238  if(!(rText.Length))
239  return geometry::RealRectangle2D();
240 
241  // TODO(F1): Fetching default screen DC here, will yield wrong
242  // metrics when e.g. formatting for a printer!
243  SystemGraphicsData aSystemGraphicsData;
244  aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
245  aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(GetDC( nullptr ));
246  ScopedVclPtrInstance<VirtualDevice> xVirtualDevice(aSystemGraphicsData, Size(1, 1), DeviceFormat::DEFAULT);
247 
248  // create the font
249  const css::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest();
250  vcl::Font aFont(
251  rFontRequest.FontDescription.FamilyName,
252  rFontRequest.FontDescription.StyleName,
253  Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
254 
255  aFont.SetAlignment( ALIGN_BASELINE );
256  aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
257  aFont.SetVertical( rFontRequest.FontDescription.IsVertical==css::util::TriState_YES );
258  aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
259  aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
260  aFont.SetPitch(
261  rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
263 
264  // adjust to stretched font
265  if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
266  {
267  const Size aSize = xVirtualDevice->GetFontMetric( aFont ).GetFontSize();
268  const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
269  double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
270 
271  if( !::basegfx::fTools::equalZero( fDividend) )
272  fStretch /= fDividend;
273 
274  const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
275 
276  aFont.SetAverageFontWidth( nNewWidth );
277  }
278 
280  if (pFont.is() && pFont->getEmphasisMark())
281  aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark()));
282 
283  // set font
284  xVirtualDevice->SetFont(aFont);
285 
286  // need metrics for Y offset, the XCanvas always renders
287  // relative to baseline
288  const ::FontMetric& aMetric( xVirtualDevice->GetFontMetric() );
289 
290  const sal_Int32 nAboveBaseline( -aMetric.GetInternalLeading() - aMetric.GetAscent() );
291  const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
292 
293  if( rLogicalAdvancements.getLength() )
294  {
295  return geometry::RealRectangle2D( 0, nAboveBaseline,
296  rLogicalAdvancements[ rLogicalAdvancements.getLength()-1 ],
297  nBelowBaseline );
298  }
299  else
300  {
301  return geometry::RealRectangle2D( 0, nAboveBaseline,
302  xVirtualDevice->GetTextWidth(
303  rText.Text,
304  ::canvas::tools::numeric_cast<sal_uInt16>(rText.StartPosition),
305  ::canvas::tools::numeric_cast<sal_uInt16>(rText.Length) ),
306  nBelowBaseline );
307  }
308  }
309 }
310 
311 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetFillColor(const Color &)
void SetAlpha(sal_uInt8 nAlpha)
void SetAverageFontWidth(tools::Long nWidth)
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
bool equalZero(const T &rfVal)
static LanguageType convertToLanguageType(const css::lang::Locale &rLocale, bool bResolveSystem=true)
FontEmphasisMark
void SetWeight(FontWeight)
void SetCharSet(rtl_TextEncoding)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
constexpr tools::Long Width() const
TA_BASELINE
::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(const uno::Reference< rendering::XPolyPolygon2D > &xPoly)
PITCH_VARIABLE
css::geometry::RealRectangle2D queryTextBounds(const css::rendering::StringContext &rText, const css::uno::Sequence< double > &rLogicalAdvancements, const css::uno::Reference< css::rendering::XCanvasFont > &rCanvasFont, const css::geometry::Matrix2D &rFontMatrix)
B2IRange fround(const B2DRange &rRange)
void SetLanguage(LanguageType)
void SetPitch(FontPitch ePitch)
css::uno::Reference< css::rendering::XGraphicDevice > mxGraphicDevice
ALIGN_BASELINE
int i
TextLayoutDrawHelper(const css::uno::Reference< css::rendering::XGraphicDevice > &xGraphicDevice)
PITCH_FIXED
Color doubleSequenceToColor(const uno::Sequence< double > &rColor, const uno::Reference< rendering::XColorSpace > &xColorSpace)
void transform(const basegfx::B2DHomMatrix &rMatrix)
ITALIC_NONE
void SetColor(const Color &)
void SetAlignment(TextAlign)
#define ENSURE_OR_THROW(c, m)
void SetEmphasisMark(FontEmphasisMark)
CanvasFont::ImplRef canvasFontFromXFont(const uno::Reference< rendering::XCanvasFont > &xFont)
ITALIC_NORMAL
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
void translate(double fX, double fY)
void SetVertical(bool bVertical)
void SetItalic(FontItalic)
void drawText(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const css::rendering::ViewState &rViewState, const css::rendering::RenderState &rRenderState, const ::basegfx::B2ISize &rOutputOffset, const css::rendering::StringContext &rText, const css::uno::Sequence< double > &rLogicalAdvancements, const css::uno::Reference< css::rendering::XCanvasFont > &rCanvasFont, const css::geometry::Matrix2D &rFontMatrix, bool bAlphaSurface, bool bIsRTL)