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_vcl.hxx" 26 27 #include "vcl/ctrl.hxx" 28 #include "vcl/outdev.hxx" 29 30 #include "outfont.hxx" 31 #include "textlayout.hxx" 32 33 #include <com/sun/star/i18n/ScriptDirection.hpp> 34 35 #include <tools/diagnose_ex.h> 36 37 #if OSL_DEBUG_LEVEL > 1 38 #include <rtl/strbuf.hxx> 39 #endif 40 41 //........................................................................ 42 namespace vcl 43 { 44 //........................................................................ 45 46 using ::com::sun::star::uno::Reference; 47 using ::com::sun::star::uno::Exception; 48 namespace ScriptDirection = ::com::sun::star::i18n::ScriptDirection; 49 50 //==================================================================== 51 //= DefaultTextLayout 52 //==================================================================== 53 //-------------------------------------------------------------------- ~DefaultTextLayout()54 DefaultTextLayout::~DefaultTextLayout() 55 { 56 } 57 58 //-------------------------------------------------------------------- GetTextWidth(const XubString & _rText,xub_StrLen _nStartIndex,xub_StrLen _nLength) const59 long DefaultTextLayout::GetTextWidth( const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const 60 { 61 return m_rTargetDevice.GetTextWidth( _rText, _nStartIndex, _nLength ); 62 } 63 64 //-------------------------------------------------------------------- DrawText(const Point & _rStartPoint,const XubString & _rText,xub_StrLen _nStartIndex,xub_StrLen _nLength,MetricVector * _pVector,String * _pDisplayText)65 void DefaultTextLayout::DrawText( const Point& _rStartPoint, const XubString& _rText, xub_StrLen _nStartIndex, 66 xub_StrLen _nLength, MetricVector* _pVector, String* _pDisplayText ) 67 { 68 m_rTargetDevice.DrawText( _rStartPoint, _rText, _nStartIndex, _nLength, _pVector, _pDisplayText ); 69 } 70 71 //-------------------------------------------------------------------- GetCaretPositions(const XubString & _rText,sal_Int32 * _pCaretXArray,xub_StrLen _nStartIndex,xub_StrLen _nLength) const72 bool DefaultTextLayout::GetCaretPositions( const XubString& _rText, sal_Int32* _pCaretXArray, 73 xub_StrLen _nStartIndex, xub_StrLen _nLength ) const 74 { 75 return m_rTargetDevice.GetCaretPositions( _rText, _pCaretXArray, _nStartIndex, _nLength ); 76 } 77 78 //-------------------------------------------------------------------- GetTextBreak(const XubString & _rText,long _nMaxTextWidth,xub_StrLen _nStartIndex,xub_StrLen _nLength) const79 xub_StrLen DefaultTextLayout::GetTextBreak( const XubString& _rText, long _nMaxTextWidth, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const 80 { 81 return m_rTargetDevice.GetTextBreak( _rText, _nMaxTextWidth, _nStartIndex, _nLength ); 82 } 83 84 //-------------------------------------------------------------------- DecomposeTextRectAction() const85 bool DefaultTextLayout::DecomposeTextRectAction() const 86 { 87 return false; 88 } 89 90 //==================================================================== 91 //= ReferenceDeviceTextLayout 92 //==================================================================== 93 class ReferenceDeviceTextLayout : public ITextLayout 94 { 95 public: 96 ReferenceDeviceTextLayout( const Control& _rControl, OutputDevice& _rTargetDevice, OutputDevice& _rReferenceDevice ); 97 virtual ~ReferenceDeviceTextLayout(); 98 99 // ITextLayout 100 virtual long GetTextWidth( const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen ) const; 101 virtual void DrawText( const Point& _rStartPoint, const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength, MetricVector* _pVector, String* _pDisplayText ); 102 virtual bool GetCaretPositions( const XubString& _rText, sal_Int32* _pCaretXArray, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const; 103 virtual xub_StrLen GetTextBreak( const XubString& _rText, long _nMaxTextWidth, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const; 104 virtual bool DecomposeTextRectAction() const; 105 106 public: 107 // equivalents to the respective OutputDevice methods, which take the reference device into account 108 long GetTextArray( const XubString& _rText, sal_Int32* _pDXAry, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const; 109 Rectangle DrawText( const Rectangle& _rRect, const XubString& _rText, sal_uInt16 _nStyle, MetricVector* _pVector, String* _pDisplayText ); 110 111 protected: onBeginDrawText()112 void onBeginDrawText() 113 { 114 m_aCompleteTextRect.SetEmpty(); 115 } onEndDrawText()116 Rectangle onEndDrawText() 117 { 118 return m_aCompleteTextRect; 119 } 120 121 private: 122 OutputDevice& m_rTargetDevice; 123 OutputDevice& m_rReferenceDevice; 124 Font m_aUnzoomedPointFont; 125 const Fraction m_aZoom; 126 const bool m_bRTLEnabled; 127 128 Rectangle m_aCompleteTextRect; 129 }; 130 131 //==================================================================== 132 //= ControlTextRenderer 133 //==================================================================== ReferenceDeviceTextLayout(const Control & _rControl,OutputDevice & _rTargetDevice,OutputDevice & _rReferenceDevice)134 ReferenceDeviceTextLayout::ReferenceDeviceTextLayout( const Control& _rControl, OutputDevice& _rTargetDevice, 135 OutputDevice& _rReferenceDevice ) 136 :m_rTargetDevice( _rTargetDevice ) 137 ,m_rReferenceDevice( _rReferenceDevice ) 138 ,m_aUnzoomedPointFont( _rControl.GetUnzoomedControlPointFont() ) 139 ,m_aZoom( _rControl.GetZoom() ) 140 ,m_bRTLEnabled( _rControl.IsRTLEnabled() ) 141 { 142 m_rTargetDevice.Push( PUSH_MAPMODE | PUSH_FONT | PUSH_TEXTLAYOUTMODE ); 143 144 MapMode aTargetMapMode( m_rTargetDevice.GetMapMode() ); 145 OSL_ENSURE( aTargetMapMode.GetOrigin() == Point(), "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: uhm, the code below won't work here ..." ); 146 147 // normally, controls simulate "zoom" by "zooming" the font. This is responsible for (part of) the discrepancies 148 // between text in Writer and text in controls in Writer, though both have the same font. 149 // So, if we have a zoom set at the control, then we do not scale the font, but instead modify the map mode 150 // to accomodate for the zoom. 151 aTargetMapMode.SetScaleX( m_aZoom ); // TODO: shouldn't this be "current_scale * zoom"? 152 aTargetMapMode.SetScaleY( m_aZoom ); 153 154 // also, use a higher-resolution map unit than "pixels", which should save us some rounding errors when 155 // translating coordinates between the reference device and the target device. 156 OSL_ENSURE( aTargetMapMode.GetMapUnit() == MAP_PIXEL, 157 "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: this class is not expected to work with such target devices!" ); 158 // we *could* adjust all the code in this class to handle this case, but at the moment, it's not necessary 159 const MapUnit eTargetMapUnit = m_rReferenceDevice.GetMapMode().GetMapUnit(); 160 aTargetMapMode.SetMapUnit( eTargetMapUnit ); 161 OSL_ENSURE( aTargetMapMode.GetMapUnit() != MAP_PIXEL, 162 "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: a reference device which has map mode PIXEL?!" ); 163 164 m_rTargetDevice.SetMapMode( aTargetMapMode ); 165 166 // now that the Zoom is part of the map mode, reset the target device's font to the "unzoomed" version 167 Font aDrawFont( m_aUnzoomedPointFont ); 168 aDrawFont.SetSize( m_rTargetDevice.LogicToLogic( aDrawFont.GetSize(), MAP_POINT, eTargetMapUnit ) ); 169 _rTargetDevice.SetFont( aDrawFont ); 170 171 // transfer font to the reference device 172 m_rReferenceDevice.Push( PUSH_FONT | PUSH_TEXTLAYOUTMODE ); 173 Font aRefFont( m_aUnzoomedPointFont ); 174 aRefFont.SetSize( OutputDevice::LogicToLogic( 175 aRefFont.GetSize(), MAP_POINT, m_rReferenceDevice.GetMapMode().GetMapUnit() ) ); 176 m_rReferenceDevice.SetFont( aRefFont ); 177 } 178 179 //-------------------------------------------------------------------- ~ReferenceDeviceTextLayout()180 ReferenceDeviceTextLayout::~ReferenceDeviceTextLayout() 181 { 182 m_rReferenceDevice.Pop(); 183 m_rTargetDevice.Pop(); 184 } 185 186 //-------------------------------------------------------------------- 187 namespace 188 { 189 //................................................................ lcl_normalizeLength(const XubString & _rText,const xub_StrLen _nStartIndex,xub_StrLen & _io_nLength)190 bool lcl_normalizeLength( const XubString& _rText, const xub_StrLen _nStartIndex, xub_StrLen& _io_nLength ) 191 { 192 xub_StrLen nTextLength = _rText.Len(); 193 if ( _nStartIndex > nTextLength ) 194 return false; 195 if ( _nStartIndex + _io_nLength > nTextLength ) 196 _io_nLength = nTextLength - _nStartIndex; 197 return true; 198 } 199 } 200 201 //-------------------------------------------------------------------- GetTextArray(const XubString & _rText,sal_Int32 * _pDXAry,xub_StrLen _nStartIndex,xub_StrLen _nLength) const202 long ReferenceDeviceTextLayout::GetTextArray( const XubString& _rText, sal_Int32* _pDXAry, xub_StrLen _nStartIndex, 203 xub_StrLen _nLength ) const 204 { 205 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) 206 return 0; 207 208 // retrieve the character widths from the reference device 209 long nTextWidth = m_rReferenceDevice.GetTextArray( _rText, _pDXAry, _nStartIndex, _nLength ); 210 #if OSL_DEBUG_LEVEL > 1 211 if ( _pDXAry ) 212 { 213 ::rtl::OStringBuffer aTrace; 214 aTrace.append( "ReferenceDeviceTextLayout::GetTextArray( " ); 215 aTrace.append( ::rtl::OUStringToOString( _rText, RTL_TEXTENCODING_UTF8 ) ); 216 aTrace.append( " ): " ); 217 aTrace.append( nTextWidth ); 218 aTrace.append( " = ( " ); 219 for ( size_t i=0; i<_nLength; ) 220 { 221 aTrace.append( _pDXAry[i] ); 222 if ( ++i < _nLength ) 223 aTrace.append( ", " ); 224 } 225 aTrace.append( ")" ); 226 OSL_TRACE( aTrace.makeStringAndClear().getStr() ); 227 } 228 #endif 229 return nTextWidth; 230 } 231 232 //-------------------------------------------------------------------- GetTextWidth(const XubString & _rText,xub_StrLen _nStartIndex,xub_StrLen _nLength) const233 long ReferenceDeviceTextLayout::GetTextWidth( const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const 234 { 235 return GetTextArray( _rText, NULL, _nStartIndex, _nLength ); 236 } 237 238 //-------------------------------------------------------------------- DrawText(const Point & _rStartPoint,const XubString & _rText,xub_StrLen _nStartIndex,xub_StrLen _nLength,MetricVector * _pVector,String * _pDisplayText)239 void ReferenceDeviceTextLayout::DrawText( const Point& _rStartPoint, const XubString& _rText, xub_StrLen _nStartIndex, xub_StrLen _nLength, MetricVector* _pVector, String* _pDisplayText ) 240 { 241 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) 242 return; 243 244 if ( _pVector && _pDisplayText ) 245 { 246 MetricVector aGlyphBounds; 247 m_rReferenceDevice.GetGlyphBoundRects( _rStartPoint, _rText, _nStartIndex, _nLength, _nStartIndex, aGlyphBounds ); 248 ::std::copy( 249 aGlyphBounds.begin(), aGlyphBounds.end(), 250 ::std::insert_iterator< MetricVector > ( *_pVector, _pVector->end() ) ); 251 _pDisplayText->Append( _rText.Copy( _nStartIndex, _nLength ) ); 252 return; 253 } 254 255 sal_Int32* pCharWidths = new sal_Int32[ _nLength ]; 256 long nTextWidth = GetTextArray( _rText, pCharWidths, _nStartIndex, _nLength ); 257 m_rTargetDevice.DrawTextArray( _rStartPoint, _rText, pCharWidths, _nStartIndex, _nLength ); 258 delete[] pCharWidths; 259 260 m_aCompleteTextRect.Union( Rectangle( _rStartPoint, Size( nTextWidth, m_rTargetDevice.GetTextHeight() ) ) ); 261 } 262 263 //-------------------------------------------------------------------- GetCaretPositions(const XubString & _rText,sal_Int32 * _pCaretXArray,xub_StrLen _nStartIndex,xub_StrLen _nLength) const264 bool ReferenceDeviceTextLayout::GetCaretPositions( const XubString& _rText, sal_Int32* _pCaretXArray, 265 xub_StrLen _nStartIndex, xub_StrLen _nLength ) const 266 { 267 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) 268 return false; 269 270 // retrieve the caret positions from the reference device 271 if ( !m_rReferenceDevice.GetCaretPositions( _rText, _pCaretXArray, _nStartIndex, _nLength ) ) 272 return false; 273 274 return true; 275 } 276 277 //-------------------------------------------------------------------- GetTextBreak(const XubString & _rText,long _nMaxTextWidth,xub_StrLen _nStartIndex,xub_StrLen _nLength) const278 xub_StrLen ReferenceDeviceTextLayout::GetTextBreak( const XubString& _rText, long _nMaxTextWidth, xub_StrLen _nStartIndex, xub_StrLen _nLength ) const 279 { 280 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) ) 281 return 0; 282 283 return m_rReferenceDevice.GetTextBreak( _rText, _nMaxTextWidth, _nStartIndex, _nLength ); 284 } 285 286 //-------------------------------------------------------------------- DecomposeTextRectAction() const287 bool ReferenceDeviceTextLayout::DecomposeTextRectAction() const 288 { 289 return true; 290 } 291 292 //-------------------------------------------------------------------- 293 namespace 294 { zoomBy(long _value,const Fraction & _zoom)295 long zoomBy( long _value, const Fraction& _zoom ) 296 { 297 double n = (double)_value; 298 n *= (double)_zoom.GetNumerator(); 299 n /= (double)_zoom.GetDenominator(); 300 return (long)::rtl::math::round( n ); 301 } unzoomBy(long _value,const Fraction & _zoom)302 long unzoomBy( long _value, const Fraction& _zoom ) 303 { 304 return zoomBy( _value, Fraction( _zoom.GetDenominator(), _zoom.GetNumerator() ) ); 305 } 306 } 307 308 //-------------------------------------------------------------------- DrawText(const Rectangle & _rRect,const XubString & _rText,sal_uInt16 _nStyle,MetricVector * _pVector,String * _pDisplayText)309 Rectangle ReferenceDeviceTextLayout::DrawText( const Rectangle& _rRect, const XubString& _rText, sal_uInt16 _nStyle, MetricVector* _pVector, String* _pDisplayText ) 310 { 311 if ( !_rText.Len() ) 312 return Rectangle(); 313 314 // determine text layout mode from the RTL-ness of the control whose text we render 315 sal_uLong nTextLayoutMode = m_bRTLEnabled ? TEXT_LAYOUT_BIDI_RTL : TEXT_LAYOUT_BIDI_LTR; 316 m_rReferenceDevice.SetLayoutMode( nTextLayoutMode ); 317 m_rTargetDevice.SetLayoutMode( nTextLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT ); 318 // TEXT_LAYOUT_TEXTORIGIN_LEFT is because when we do actually draw the text (in DrawText( Point, ... )), then 319 // our caller gives us the left border of the draw position, regardless of script type, text layout, 320 // and the like 321 322 // in our ctor, we set the map mode of the target device from pixel to twip, but our caller doesn't know this, 323 // but passed pixel coordinates. So, adjust the rect. 324 Rectangle aRect( m_rTargetDevice.PixelToLogic( _rRect ) ); 325 326 onBeginDrawText(); 327 m_rTargetDevice.DrawText( aRect, _rText, _nStyle, _pVector, _pDisplayText, this ); 328 Rectangle aTextRect = onEndDrawText(); 329 330 if ( aTextRect.IsEmpty() && !aRect.IsEmpty() ) 331 { 332 // this happens for instance if we're in a PaintToDevice call, where only a MetaFile is recorded, 333 // but no actual painting happens, so our "DrawText( Point, ... )" is never called 334 // In this case, calculate the rect from what OutputDevice::GetTextRect would give us. This has 335 // the disadvantage of less accuracy, compared with the approach to calculate the rect from the 336 // single "DrawText( Point, ... )" calls, since more intermediate arithmetics will translate 337 // from ref- to target-units. 338 aTextRect = m_rTargetDevice.GetTextRect( aRect, _rText, _nStyle, NULL, this ); 339 } 340 341 // similar to above, the text rect now contains TWIPs (or whatever unit the ref device has), but the caller 342 // expects pixel coordinates 343 aTextRect = m_rTargetDevice.LogicToPixel( aTextRect ); 344 345 // convert the metric vector 346 if ( _pVector ) 347 { 348 for ( MetricVector::iterator charRect = _pVector->begin(); 349 charRect != _pVector->end(); 350 ++charRect 351 ) 352 { 353 *charRect = m_rTargetDevice.LogicToPixel( *charRect ); 354 } 355 } 356 357 return aTextRect; 358 } 359 360 //==================================================================== 361 //= ControlTextRenderer 362 //==================================================================== 363 //-------------------------------------------------------------------- ControlTextRenderer(const Control & _rControl,OutputDevice & _rTargetDevice,OutputDevice & _rReferenceDevice)364 ControlTextRenderer::ControlTextRenderer( const Control& _rControl, OutputDevice& _rTargetDevice, OutputDevice& _rReferenceDevice ) 365 :m_pImpl( new ReferenceDeviceTextLayout( _rControl, _rTargetDevice, _rReferenceDevice ) ) 366 { 367 } 368 369 //-------------------------------------------------------------------- ~ControlTextRenderer()370 ControlTextRenderer::~ControlTextRenderer() 371 { 372 } 373 374 //-------------------------------------------------------------------- DrawText(const Rectangle & _rRect,const XubString & _rText,sal_uInt16 _nStyle,MetricVector * _pVector,String * _pDisplayText)375 Rectangle ControlTextRenderer::DrawText( const Rectangle& _rRect, const XubString& _rText, sal_uInt16 _nStyle, 376 MetricVector* _pVector, String* _pDisplayText ) 377 { 378 return m_pImpl->DrawText( _rRect, _rText, _nStyle, _pVector, _pDisplayText ); 379 } 380 381 //........................................................................ 382 } // namespace vcl 383 //........................................................................ 384