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