1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_canvas.hxx" 26 27 #include <tools/poly.hxx> 28 29 #include <vcl/metric.hxx> 30 #include <vcl/virdev.hxx> 31 #include <vcl/metric.hxx> 32 #include <vcl/canvastools.hxx> 33 #include <tools/diagnose_ex.h> 34 35 #include <boost/scoped_array.hpp> 36 #include <boost/bind.hpp> 37 #include <com/sun/star/rendering/FontRequest.hpp> 38 #include <com/sun/star/rendering/PanoseProportion.hpp> 39 #include <com/sun/star/rendering/XCanvasFont.hpp> 40 #include <com/sun/star/rendering/TextDirection.hpp> 41 #include <comphelper/sequence.hxx> 42 #include <comphelper/scopeguard.hxx> 43 #include <tools/color.hxx> 44 #include <basegfx/polygon/b2dpolypolygon.hxx> 45 #include <basegfx/tools/canvastools.hxx> 46 #include <canvas/canvastools.hxx> 47 #include <canvas/debug.hxx> 48 #include "dx_impltools.hxx" 49 #include <vcl/sysdata.hxx> 50 #include <i18npool/mslangid.hxx> 51 #include "dx_textlayout_drawhelper.hxx" 52 #include "dx_bitmap.hxx" 53 #include "dx_canvasfont.hxx" 54 55 class ::com::sun::star::rendering::XCanvasFont; 56 57 using namespace ::com::sun::star; 58 59 60 ////////////////////////////////////////////////////////////////////////////// 61 62 namespace dxcanvas 63 { 64 class DXBitmap; TextLayoutDrawHelper(const uno::Reference<rendering::XGraphicDevice> & xGraphicDevice)65 TextLayoutDrawHelper::TextLayoutDrawHelper( 66 const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice ) : 67 mxGraphicDevice(xGraphicDevice) 68 { 69 } 70 ~TextLayoutDrawHelper()71 TextLayoutDrawHelper::~TextLayoutDrawHelper() 72 { 73 } 74 setupLayoutMode(VirtualDevice & rVirDev,sal_Int8 nTextDirection)75 void setupLayoutMode( VirtualDevice& rVirDev, 76 sal_Int8 nTextDirection ) 77 { 78 // TODO(P3): avoid if already correctly set 79 ULONG nLayoutMode; 80 switch( nTextDirection ) 81 { 82 default: 83 nLayoutMode = 0; 84 break; 85 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT: 86 nLayoutMode = TEXT_LAYOUT_BIDI_LTR; 87 break; 88 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT: 89 nLayoutMode = TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG; 90 break; 91 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT: 92 nLayoutMode = TEXT_LAYOUT_BIDI_RTL; 93 break; 94 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT: 95 nLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG; 96 break; 97 } 98 99 // set calculated layout mode. Origin is always the left edge, 100 // as required at the API spec 101 rVirDev.SetLayoutMode( nLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT ); 102 } 103 drawText(const GraphicsSharedPtr & rGraphics,const::com::sun::star::rendering::ViewState & rViewState,const::com::sun::star::rendering::RenderState & rRenderState,const::basegfx::B2ISize & rOutputOffset,const::com::sun::star::rendering::StringContext & rText,const::com::sun::star::uno::Sequence<double> & rLogicalAdvancements,const::com::sun::star::uno::Reference<::com::sun::star::rendering::XCanvasFont> & rCanvasFont,const::com::sun::star::geometry::Matrix2D & rFontMatrix,bool bAlphaSurface,sal_Int8 nTextDirection)104 void TextLayoutDrawHelper::drawText( 105 const GraphicsSharedPtr& rGraphics, 106 const ::com::sun::star::rendering::ViewState& rViewState, 107 const ::com::sun::star::rendering::RenderState& rRenderState, 108 const ::basegfx::B2ISize& rOutputOffset, 109 const ::com::sun::star::rendering::StringContext& rText, 110 const ::com::sun::star::uno::Sequence< double >& rLogicalAdvancements, 111 const ::com::sun::star::uno::Reference< 112 ::com::sun::star::rendering::XCanvasFont >& rCanvasFont, 113 const ::com::sun::star::geometry::Matrix2D& rFontMatrix, 114 bool bAlphaSurface, 115 sal_Int8 nTextDirection) 116 { 117 HDC hdc = rGraphics->GetHDC(); 118 119 // issue an ReleaseHDC() when leaving the scope 120 const ::comphelper::ScopeGuard aGuard( 121 boost::bind( &Gdiplus::Graphics::ReleaseHDC, 122 rGraphics.get(), 123 hdc )); 124 125 SystemGraphicsData aSystemGraphicsData; 126 aSystemGraphicsData.nSize = sizeof(SystemGraphicsData); 127 aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(hdc); 128 VirtualDevice aVirtualDevice(&aSystemGraphicsData, 0); 129 130 // disable font antialiasing - GDI does not handle alpha 131 // surfaces properly. 132 if( bAlphaSurface ) 133 aVirtualDevice.SetAntialiasing(ANTIALIASING_DISABLE_TEXT); 134 135 if(rText.Length) 136 { 137 sal_Bool test = mxGraphicDevice.is(); 138 ENSURE_OR_THROW( test, 139 "TextLayoutDrawHelper::drawText(): Invalid GraphicDevice" ); 140 141 // set text color. Make sure to remove transparence part first. 142 Color aColor( COL_WHITE ); 143 144 if( rRenderState.DeviceColor.getLength() > 2 ) 145 aColor = ::vcl::unotools::doubleSequenceToColor( 146 rRenderState.DeviceColor, 147 mxGraphicDevice->getDeviceColorSpace()); 148 aColor.SetTransparency(0); 149 aVirtualDevice.SetTextColor(aColor); 150 151 // create the font 152 const ::com::sun::star::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest(); 153 Font aFont( 154 rFontRequest.FontDescription.FamilyName, 155 rFontRequest.FontDescription.StyleName, 156 Size( 0, ::basegfx::fround(rFontRequest.CellSize))); 157 158 aFont.SetAlign( ALIGN_BASELINE ); 159 aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==com::sun::star::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); 160 aFont.SetVertical( (rFontRequest.FontDescription.IsVertical==com::sun::star::util::TriState_YES) ? sal_True : sal_False ); 161 aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) ); 162 aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL ); 163 aFont.SetPitch( 164 rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED 165 ? PITCH_FIXED : PITCH_VARIABLE); 166 167 aFont.SetLanguage(MsLangId::convertLocaleToLanguage(rFontRequest.Locale)); 168 169 // setup font color 170 aFont.SetColor( aColor ); 171 aFont.SetFillColor( aColor ); 172 173 // adjust to stretched font 174 if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11)) 175 { 176 const Size aSize = aVirtualDevice.GetFontMetric( aFont ).GetSize(); 177 const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 ); 178 double fStretch = (rFontMatrix.m00 + rFontMatrix.m01); 179 180 if( !::basegfx::fTools::equalZero( fDividend) ) 181 fStretch /= fDividend; 182 183 const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch ); 184 185 aFont.SetWidth( nNewWidth ); 186 } 187 188 // set font 189 aVirtualDevice.SetFont(aFont); 190 191 setupLayoutMode( aVirtualDevice, nTextDirection ); 192 193 // create world transformation matrix 194 ::basegfx::B2DHomMatrix aWorldTransform; 195 ::canvas::tools::mergeViewAndRenderTransform(aWorldTransform, rViewState, rRenderState); 196 197 if(!rOutputOffset.equalZero()) 198 { 199 aWorldTransform.translate(rOutputOffset.getX(), rOutputOffset.getY()); 200 } 201 202 // set ViewState clipping 203 if(rViewState.Clip.is()) 204 { 205 ::basegfx::B2DPolyPolygon aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rViewState.Clip)); 206 ::basegfx::B2DHomMatrix aMatrix; 207 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix, rViewState.AffineTransform ); 208 209 if(!rOutputOffset.equalZero()) 210 { 211 aMatrix.translate(rOutputOffset.getX(), rOutputOffset.getY()); 212 } 213 214 aClipPoly.transform(aMatrix); 215 const Region& rClipRegion = Region(PolyPolygon(aClipPoly)); 216 aVirtualDevice.IntersectClipRegion(rClipRegion); 217 } 218 219 if(rRenderState.Clip.is()) 220 { 221 ::basegfx::B2DPolyPolygon aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rRenderState.Clip)); 222 aClipPoly.transform(aWorldTransform); 223 const Region& rClipRegion = Region(PolyPolygon(aClipPoly)); 224 aVirtualDevice.IntersectClipRegion(rClipRegion); 225 } 226 227 // set world transform 228 XFORM aXForm; 229 aXForm.eM11 = (FLOAT)aWorldTransform.get(0, 0); 230 aXForm.eM12 = (FLOAT)aWorldTransform.get(1, 0); 231 aXForm.eM21 = (FLOAT)aWorldTransform.get(0, 1); 232 aXForm.eM22 = (FLOAT)aWorldTransform.get(1, 1); 233 aXForm.eDx = (FLOAT)aWorldTransform.get(0, 2); 234 aXForm.eDy = (FLOAT)aWorldTransform.get(1, 2); 235 236 // TODO(F3): This is NOT supported on 95/98/ME! 237 SetGraphicsMode(hdc, GM_ADVANCED); 238 SetTextAlign(hdc, TA_BASELINE); 239 SetWorldTransform(hdc, &aXForm); 240 241 // use a empty StartPosition for text rendering 242 const Point aEmptyPoint(0, 0); 243 244 // create the String 245 const String aText(rText.Text.getStr()); 246 247 if( rLogicalAdvancements.getLength() ) 248 { 249 // create the DXArray 250 const sal_Int32 nLen( rLogicalAdvancements.getLength() ); 251 ::boost::scoped_array<sal_Int32> pDXArray( new sal_Int32[nLen] ); 252 for( sal_Int32 i=0; i<nLen; ++i ) 253 pDXArray[i] = basegfx::fround( rLogicalAdvancements[i] ); 254 255 // draw the String 256 aVirtualDevice.DrawTextArray( aEmptyPoint, 257 aText, 258 pDXArray.get(), 259 (xub_StrLen)rText.StartPosition, 260 (xub_StrLen)rText.Length ); 261 } 262 else 263 { 264 // draw the String 265 aVirtualDevice.DrawText( aEmptyPoint, 266 aText, 267 (xub_StrLen)rText.StartPosition, 268 (xub_StrLen)rText.Length ); 269 } 270 } 271 } 272 queryTextBounds(const rendering::StringContext & rText,const uno::Sequence<double> & rLogicalAdvancements,const uno::Reference<rendering::XCanvasFont> & rCanvasFont,const geometry::Matrix2D & rFontMatrix,sal_Int8 nTextDirection)273 geometry::RealRectangle2D TextLayoutDrawHelper::queryTextBounds( const rendering::StringContext& rText, 274 const uno::Sequence< double >& rLogicalAdvancements, 275 const uno::Reference< rendering::XCanvasFont >& rCanvasFont, 276 const geometry::Matrix2D& rFontMatrix, 277 sal_Int8 nTextDirection ) 278 { 279 if(!(rText.Length)) 280 return geometry::RealRectangle2D(); 281 282 // TODO(F1): Fetching default screen DC here, will yield wrong 283 // metrics when e.g. formatting for a printer! 284 SystemGraphicsData aSystemGraphicsData; 285 aSystemGraphicsData.nSize = sizeof(SystemGraphicsData); 286 aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(GetDC( NULL )); 287 VirtualDevice aVirtualDevice(&aSystemGraphicsData, 0); 288 289 // create the font 290 const ::com::sun::star::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest(); 291 Font aFont( 292 rFontRequest.FontDescription.FamilyName, 293 rFontRequest.FontDescription.StyleName, 294 Size( 0, ::basegfx::fround(rFontRequest.CellSize))); 295 296 aFont.SetAlign( ALIGN_BASELINE ); 297 aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==com::sun::star::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); 298 aFont.SetVertical( (rFontRequest.FontDescription.IsVertical==com::sun::star::util::TriState_YES) ? sal_True : sal_False ); 299 aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) ); 300 aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL ); 301 aFont.SetPitch( 302 rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED 303 ? PITCH_FIXED : PITCH_VARIABLE); 304 305 // adjust to stretched font 306 if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11)) 307 { 308 const Size aSize = aVirtualDevice.GetFontMetric( aFont ).GetSize(); 309 const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 ); 310 double fStretch = (rFontMatrix.m00 + rFontMatrix.m01); 311 312 if( !::basegfx::fTools::equalZero( fDividend) ) 313 fStretch /= fDividend; 314 315 const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch ); 316 317 aFont.SetWidth( nNewWidth ); 318 } 319 320 // set font 321 aVirtualDevice.SetFont(aFont); 322 323 setupLayoutMode( aVirtualDevice, nTextDirection ); 324 325 // need metrics for Y offset, the XCanvas always renders 326 // relative to baseline 327 const ::FontMetric& aMetric( aVirtualDevice.GetFontMetric() ); 328 329 const sal_Int32 nAboveBaseline( -aMetric.GetIntLeading() - aMetric.GetAscent() ); 330 const sal_Int32 nBelowBaseline( aMetric.GetDescent() ); 331 332 if( rLogicalAdvancements.getLength() ) 333 { 334 return geometry::RealRectangle2D( 0, nAboveBaseline, 335 rLogicalAdvancements[ rLogicalAdvancements.getLength()-1 ], 336 nBelowBaseline ); 337 } 338 else 339 { 340 return geometry::RealRectangle2D( 0, nAboveBaseline, 341 aVirtualDevice.GetTextWidth( 342 rText.Text, 343 ::canvas::tools::numeric_cast<sal_uInt16>(rText.StartPosition), 344 ::canvas::tools::numeric_cast<sal_uInt16>(rText.Length) ), 345 nBelowBaseline ); 346 } 347 } 348 } 349 350 351 // eof 352