update emoji autocorrect entries from po-files
[LibreOffice.git] / vcl / source / gdi / textlayout.cxx
blob5b369b6f218b1b0416e50ad3a9df5a69885cb26d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "vcl/ctrl.hxx"
21 #include "vcl/outdev.hxx"
23 #include "outfont.hxx"
24 #include "textlayout.hxx"
26 #include <com/sun/star/i18n/ScriptDirection.hpp>
28 #include <tools/diagnose_ex.h>
29 #include <tools/fract.hxx>
31 #if OSL_DEBUG_LEVEL > 1
32 #include <rtl/strbuf.hxx>
33 #endif
35 #include <boost/scoped_array.hpp>
37 namespace vcl
40 using ::com::sun::star::uno::Reference;
41 using ::com::sun::star::uno::Exception;
42 namespace ScriptDirection = ::com::sun::star::i18n::ScriptDirection;
44 DefaultTextLayout::~DefaultTextLayout()
48 long DefaultTextLayout::GetTextWidth( const OUString& _rText, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
50 return m_rTargetDevice.GetTextWidth( _rText, _nStartIndex, _nLength );
53 void DefaultTextLayout::DrawText( const Point& _rStartPoint, const OUString& _rText, sal_Int32 _nStartIndex,
54 sal_Int32 _nLength, MetricVector* _pVector, OUString* _pDisplayText )
56 m_rTargetDevice.DrawText( _rStartPoint, _rText, _nStartIndex, _nLength, _pVector, _pDisplayText );
59 bool DefaultTextLayout::GetCaretPositions( const OUString& _rText, long* _pCaretXArray,
60 sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
62 return m_rTargetDevice.GetCaretPositions( _rText, _pCaretXArray, _nStartIndex, _nLength );
65 sal_Int32 DefaultTextLayout::GetTextBreak( const OUString& _rText, long _nMaxTextWidth, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
67 return m_rTargetDevice.GetTextBreak( _rText, _nMaxTextWidth, _nStartIndex, _nLength );
70 bool DefaultTextLayout::DecomposeTextRectAction() const
72 return false;
75 class ReferenceDeviceTextLayout : public ITextLayout
77 public:
78 ReferenceDeviceTextLayout( const Control& _rControl, OutputDevice& _rTargetDevice, OutputDevice& _rReferenceDevice );
79 virtual ~ReferenceDeviceTextLayout();
81 // ITextLayout
82 virtual long GetTextWidth( const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen ) const SAL_OVERRIDE;
83 virtual void DrawText( const Point& _rStartPoint, const OUString& _rText, sal_Int32 _nStartIndex, sal_Int32 _nLength, MetricVector* _pVector, OUString* _pDisplayText ) SAL_OVERRIDE;
84 virtual bool GetCaretPositions( const OUString& _rText, long* _pCaretXArray, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const SAL_OVERRIDE;
85 virtual sal_Int32 GetTextBreak(const OUString& _rText, long _nMaxTextWidth, sal_Int32 _nStartIndex, sal_Int32 _nLength) const SAL_OVERRIDE;
86 virtual bool DecomposeTextRectAction() const SAL_OVERRIDE;
88 public:
89 // equivalents to the respective OutputDevice methods, which take the reference device into account
90 long GetTextArray( const OUString& _rText, long* _pDXAry, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const;
91 Rectangle DrawText( const Rectangle& _rRect, const OUString& _rText, DrawTextFlags _nStyle, MetricVector* _pVector, OUString* _pDisplayText );
93 protected:
94 void onBeginDrawText()
96 m_aCompleteTextRect.SetEmpty();
98 Rectangle onEndDrawText()
100 return m_aCompleteTextRect;
103 private:
104 OutputDevice& m_rTargetDevice;
105 OutputDevice& m_rReferenceDevice;
106 Font m_aUnzoomedPointFont;
107 const Fraction m_aZoom;
108 const bool m_bRTLEnabled;
110 Rectangle m_aCompleteTextRect;
113 ReferenceDeviceTextLayout::ReferenceDeviceTextLayout( const Control& _rControl, OutputDevice& _rTargetDevice,
114 OutputDevice& _rReferenceDevice )
115 :m_rTargetDevice( _rTargetDevice )
116 ,m_rReferenceDevice( _rReferenceDevice )
117 ,m_aUnzoomedPointFont( _rControl.GetUnzoomedControlPointFont() )
118 ,m_aZoom( _rControl.GetZoom() )
119 ,m_bRTLEnabled( _rControl.IsRTLEnabled() )
121 m_rTargetDevice.Push( PushFlags::MAPMODE | PushFlags::FONT | PushFlags::TEXTLAYOUTMODE );
123 MapMode aTargetMapMode( m_rTargetDevice.GetMapMode() );
124 OSL_ENSURE( aTargetMapMode.GetOrigin() == Point(), "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: uhm, the code below won't work here ..." );
126 // normally, controls simulate "zoom" by "zooming" the font. This is responsible for (part of) the discrepancies
127 // between text in Writer and text in controls in Writer, though both have the same font.
128 // So, if we have a zoom set at the control, then we do not scale the font, but instead modify the map mode
129 // to accommodate for the zoom.
130 aTargetMapMode.SetScaleX( m_aZoom ); // TODO: shouldn't this be "current_scale * zoom"?
131 aTargetMapMode.SetScaleY( m_aZoom );
133 // also, use a higher-resolution map unit than "pixels", which should save us some rounding errors when
134 // translating coordinates between the reference device and the target device.
135 OSL_ENSURE( aTargetMapMode.GetMapUnit() == MAP_PIXEL,
136 "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: this class is not expected to work with such target devices!" );
137 // we *could* adjust all the code in this class to handle this case, but at the moment, it's not necessary
138 const MapUnit eTargetMapUnit = m_rReferenceDevice.GetMapMode().GetMapUnit();
139 aTargetMapMode.SetMapUnit( eTargetMapUnit );
140 OSL_ENSURE( aTargetMapMode.GetMapUnit() != MAP_PIXEL,
141 "ReferenceDeviceTextLayout::ReferenceDeviceTextLayout: a reference device which has map mode PIXEL?!" );
143 m_rTargetDevice.SetMapMode( aTargetMapMode );
145 // now that the Zoom is part of the map mode, reset the target device's font to the "unzoomed" version
146 Font aDrawFont( m_aUnzoomedPointFont );
147 aDrawFont.SetSize( OutputDevice::LogicToLogic( aDrawFont.GetSize(), MAP_POINT, eTargetMapUnit ) );
148 _rTargetDevice.SetFont( aDrawFont );
150 // transfer font to the reference device
151 m_rReferenceDevice.Push( PushFlags::FONT | PushFlags::TEXTLAYOUTMODE );
152 Font aRefFont( m_aUnzoomedPointFont );
153 aRefFont.SetSize( OutputDevice::LogicToLogic(
154 aRefFont.GetSize(), MAP_POINT, m_rReferenceDevice.GetMapMode().GetMapUnit() ) );
155 m_rReferenceDevice.SetFont( aRefFont );
158 ReferenceDeviceTextLayout::~ReferenceDeviceTextLayout()
160 m_rReferenceDevice.Pop();
161 m_rTargetDevice.Pop();
164 namespace
166 bool lcl_normalizeLength( const OUString& _rText, const sal_Int32 _nStartIndex, sal_Int32& _io_nLength )
168 sal_Int32 nTextLength = _rText.getLength();
169 if ( _nStartIndex > nTextLength )
170 return false;
171 if ( _nStartIndex + _io_nLength > nTextLength )
172 _io_nLength = nTextLength - _nStartIndex;
173 return true;
177 long ReferenceDeviceTextLayout::GetTextArray( const OUString& _rText, long* _pDXAry, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
179 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) )
180 return 0;
182 // retrieve the character widths from the reference device
183 long nTextWidth = m_rReferenceDevice.GetTextArray( _rText, _pDXAry, _nStartIndex, _nLength );
184 #if OSL_DEBUG_LEVEL > 1
185 if ( _pDXAry )
187 OStringBuffer aTrace;
188 aTrace.append( "ReferenceDeviceTextLayout::GetTextArray( " );
189 aTrace.append( OUStringToOString( _rText, RTL_TEXTENCODING_UTF8 ) );
190 aTrace.append( " ): " );
191 aTrace.append( nTextWidth );
192 aTrace.append( " = ( " );
193 for ( sal_Int32 i=0; i<_nLength; )
195 aTrace.append( _pDXAry[i] );
196 if ( ++i < _nLength )
197 aTrace.append( ", " );
199 aTrace.append( ")" );
200 OSL_TRACE( "%s", aTrace.makeStringAndClear().getStr() );
202 #endif
203 return nTextWidth;
206 long ReferenceDeviceTextLayout::GetTextWidth( const OUString& _rText, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
208 return GetTextArray( _rText, NULL, _nStartIndex, _nLength );
211 void ReferenceDeviceTextLayout::DrawText( const Point& _rStartPoint, const OUString& _rText, sal_Int32 _nStartIndex, sal_Int32 _nLength, MetricVector* _pVector, OUString* _pDisplayText )
213 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) )
214 return;
216 if ( _pVector && _pDisplayText )
218 MetricVector aGlyphBounds;
219 m_rReferenceDevice.GetGlyphBoundRects( _rStartPoint, _rText, _nStartIndex, _nLength, _nStartIndex, aGlyphBounds );
220 ::std::copy(
221 aGlyphBounds.begin(), aGlyphBounds.end(),
222 ::std::insert_iterator< MetricVector > ( *_pVector, _pVector->end() ) );
223 *_pDisplayText += _rText.copy( _nStartIndex, _nLength );
224 return;
227 boost::scoped_array<long> pCharWidths(new long[ _nLength ]);
228 long nTextWidth = GetTextArray( _rText, pCharWidths.get(), _nStartIndex, _nLength );
229 m_rTargetDevice.DrawTextArray( _rStartPoint, _rText, pCharWidths.get(), _nStartIndex, _nLength );
230 pCharWidths.reset();
232 m_aCompleteTextRect.Union( Rectangle( _rStartPoint, Size( nTextWidth, m_rTargetDevice.GetTextHeight() ) ) );
235 bool ReferenceDeviceTextLayout::GetCaretPositions( const OUString& _rText, long* _pCaretXArray,
236 sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
238 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) )
239 return false;
241 // retrieve the caret positions from the reference device
242 if ( !m_rReferenceDevice.GetCaretPositions( _rText, _pCaretXArray, _nStartIndex, _nLength ) )
243 return false;
245 return true;
248 sal_Int32 ReferenceDeviceTextLayout::GetTextBreak( const OUString& _rText, long _nMaxTextWidth, sal_Int32 _nStartIndex, sal_Int32 _nLength ) const
250 if ( !lcl_normalizeLength( _rText, _nStartIndex, _nLength ) )
251 return 0;
253 return m_rReferenceDevice.GetTextBreak( _rText, _nMaxTextWidth, _nStartIndex, _nLength );
256 bool ReferenceDeviceTextLayout::DecomposeTextRectAction() const
258 return true;
261 Rectangle ReferenceDeviceTextLayout::DrawText( const Rectangle& _rRect, const OUString& _rText, DrawTextFlags _nStyle, MetricVector* _pVector, OUString* _pDisplayText )
263 if ( _rText.isEmpty() )
264 return Rectangle();
266 // determine text layout mode from the RTL-ness of the control whose text we render
267 ComplexTextLayoutMode nTextLayoutMode = m_bRTLEnabled ? TEXT_LAYOUT_BIDI_RTL : TEXT_LAYOUT_DEFAULT;
268 m_rReferenceDevice.SetLayoutMode( nTextLayoutMode );
269 m_rTargetDevice.SetLayoutMode( nTextLayoutMode | TEXT_LAYOUT_TEXTORIGIN_LEFT );
271 // TEXT_LAYOUT_TEXTORIGIN_LEFT is because when we do actually draw the text (in DrawText( Point, ... )), then
272 // our caller gives us the left border of the draw position, regardless of script type, text layout,
273 // and the like in our ctor, we set the map mode of the target device from pixel to twip, but our caller doesn't know this,
274 // but passed pixel coordinates. So, adjust the rect.
275 Rectangle aRect( m_rTargetDevice.PixelToLogic( _rRect ) );
277 onBeginDrawText();
278 m_rTargetDevice.DrawText( aRect, _rText, _nStyle, _pVector, _pDisplayText, this );
279 Rectangle aTextRect = onEndDrawText();
281 if ( aTextRect.IsEmpty() && !aRect.IsEmpty() )
283 // this happens for instance if we're in a PaintToDevice call, where only a MetaFile is recorded,
284 // but no actual painting happens, so our "DrawText( Point, ... )" is never called
285 // In this case, calculate the rect from what OutputDevice::GetTextRect would give us. This has
286 // the disadvantage of less accuracy, compared with the approach to calculate the rect from the
287 // single "DrawText( Point, ... )" calls, since more intermediate arithmetics will translate
288 // from ref- to target-units.
289 aTextRect = m_rTargetDevice.GetTextRect( aRect, _rText, _nStyle, NULL, this );
292 // similar to above, the text rect now contains TWIPs (or whatever unit the ref device has), but the caller
293 // expects pixel coordinates
294 aTextRect = m_rTargetDevice.LogicToPixel( aTextRect );
296 // convert the metric vector
297 if ( _pVector )
299 for ( MetricVector::iterator charRect = _pVector->begin();
300 charRect != _pVector->end();
301 ++charRect
304 *charRect = m_rTargetDevice.LogicToPixel( *charRect );
308 return aTextRect;
311 ControlTextRenderer::ControlTextRenderer( const Control& _rControl, OutputDevice& _rTargetDevice, OutputDevice& _rReferenceDevice )
312 :m_pImpl( new ReferenceDeviceTextLayout( _rControl, _rTargetDevice, _rReferenceDevice ) )
316 ControlTextRenderer::~ControlTextRenderer()
320 Rectangle ControlTextRenderer::DrawText( const Rectangle& _rRect, const OUString& _rText, DrawTextFlags _nStyle,
321 MetricVector* _pVector, OUString* _pDisplayText )
323 return m_pImpl->DrawText( _rRect, _rText, _nStyle, _pVector, _pDisplayText );
326 } // namespace vcl
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */