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