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