Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <svtools/scriptedtext.hxx>
21 : #include <vector>
22 : #include <rtl/ustring.hxx>
23 : #include <vcl/outdev.hxx>
24 : #include <vcl/font.hxx>
25 : #include <tools/debug.hxx>
26 : #include <tools/gen.hxx>
27 : #include <com/sun/star/i18n/ScriptType.hpp>
28 :
29 :
30 : using namespace ::std;
31 : using namespace ::rtl;
32 : using namespace ::com::sun::star;
33 :
34 :
35 :
36 :
37 : class SvtScriptedTextHelper_Impl
38 : {
39 : private:
40 : OutputDevice& mrOutDevice; /// The output device for drawing the text.
41 : Font maLatinFont; /// The font for latin text portions.
42 : Font maAsianFont; /// The font for asian text portions.
43 : Font maCmplxFont; /// The font for complex text portions.
44 : Font maDefltFont; /// The default font of the output device.
45 : OUString maText; /// The text.
46 :
47 : vector< sal_Int32 > maPosVec; /// The start position of each text portion.
48 : vector< sal_Int16 > maScriptVec; /// The script type of each text portion.
49 : vector< sal_Int32 > maWidthVec; /// The output width of each text portion.
50 : Size maTextSize; /// The size the text will take in the current output device.
51 :
52 : /** Assignment operator not implemented to prevent usage. */
53 : SvtScriptedTextHelper_Impl& operator=( const SvtScriptedTextHelper_Impl& ) SAL_DELETED_FUNCTION;
54 :
55 : /** Gets the font of the given script type. */
56 : const Font& GetFont( sal_uInt16 _nScript ) const;
57 : /** Sets a font on the output device depending on the script type. */
58 0 : inline void SetOutDevFont( sal_uInt16 _nScript )
59 0 : { mrOutDevice.SetFont( GetFont( _nScript ) ); }
60 : /** Fills maPosVec with positions of all changes of script type.
61 : This method expects correctly initialized maPosVec and maScriptVec. */
62 : void CalculateSizes();
63 : /** Fills maPosVec with positions of all changes of script type and
64 : maScriptVec with the script type of each portion. */
65 : void CalculateBreaks(
66 : const uno::Reference< i18n::XBreakIterator >& _xBreakIter );
67 :
68 : public:
69 : /** This constructor sets an output device and fonts for all script types. */
70 : SvtScriptedTextHelper_Impl(
71 : OutputDevice& _rOutDevice,
72 : Font* _pLatinFont,
73 : Font* _pAsianFont,
74 : Font* _pCmplxFont );
75 : /** Copy constructor. */
76 : SvtScriptedTextHelper_Impl(
77 : const SvtScriptedTextHelper_Impl& _rCopy );
78 : /** Destructor. */
79 : ~SvtScriptedTextHelper_Impl();
80 :
81 : /** Sets new fonts and recalculates the text width. */
82 : void SetFonts( Font* _pLatinFont, Font* _pAsianFont, Font* _pCmplxFont );
83 : /** Sets a new text and calculates all script breaks and the text width. */
84 : void SetText(
85 : const OUString& _rText,
86 : const uno::Reference< i18n::XBreakIterator >& _xBreakIter );
87 :
88 : /** Returns a size struct containing the width and height of the text in the current output device. */
89 : const Size& GetTextSize() const;
90 :
91 : /** Draws the text in the current output device. */
92 : void DrawText( const Point& _rPos );
93 : };
94 :
95 :
96 0 : SvtScriptedTextHelper_Impl::SvtScriptedTextHelper_Impl(
97 : OutputDevice& _rOutDevice,
98 : Font* _pLatinFont, Font* _pAsianFont, Font* _pCmplxFont ) :
99 : mrOutDevice( _rOutDevice ),
100 : maLatinFont( _pLatinFont ? *_pLatinFont : _rOutDevice.GetFont() ),
101 : maAsianFont( _pAsianFont ? *_pAsianFont : _rOutDevice.GetFont() ),
102 : maCmplxFont( _pCmplxFont ? *_pCmplxFont : _rOutDevice.GetFont() ),
103 0 : maDefltFont( _rOutDevice.GetFont() )
104 : {
105 0 : }
106 :
107 0 : SvtScriptedTextHelper_Impl::SvtScriptedTextHelper_Impl( const SvtScriptedTextHelper_Impl& _rCopy ) :
108 : mrOutDevice( _rCopy.mrOutDevice ),
109 : maLatinFont( _rCopy.maLatinFont ),
110 : maAsianFont( _rCopy.maAsianFont ),
111 : maCmplxFont( _rCopy.maCmplxFont ),
112 : maDefltFont( _rCopy.maDefltFont ),
113 : maText( _rCopy.maText ),
114 : maPosVec( _rCopy.maPosVec ),
115 : maScriptVec( _rCopy.maScriptVec ),
116 : maWidthVec( _rCopy.maWidthVec ),
117 0 : maTextSize( _rCopy.maTextSize )
118 : {
119 0 : }
120 :
121 0 : SvtScriptedTextHelper_Impl::~SvtScriptedTextHelper_Impl()
122 : {
123 0 : }
124 :
125 0 : const Font& SvtScriptedTextHelper_Impl::GetFont( sal_uInt16 _nScript ) const
126 : {
127 0 : switch( _nScript )
128 : {
129 0 : case i18n::ScriptType::LATIN: return maLatinFont;
130 0 : case i18n::ScriptType::ASIAN: return maAsianFont;
131 0 : case i18n::ScriptType::COMPLEX: return maCmplxFont;
132 : }
133 0 : return maDefltFont;
134 : }
135 :
136 0 : void SvtScriptedTextHelper_Impl::CalculateSizes()
137 : {
138 0 : maTextSize.Width() = maTextSize.Height() = 0;
139 0 : maDefltFont = mrOutDevice.GetFont();
140 :
141 : // calculate text portion widths and total width
142 0 : maWidthVec.clear();
143 0 : if( !maPosVec.empty() )
144 : {
145 : DBG_ASSERT( maPosVec.size() - 1 == maScriptVec.size(),
146 : "SvtScriptedTextHelper_Impl::CalculateWidth - invalid vectors" );
147 :
148 0 : sal_Int32 nThisPos = maPosVec[ 0 ];
149 : sal_Int32 nNextPos;
150 0 : sal_Int32 nPosVecSize = maPosVec.size();
151 0 : sal_Int32 nPosVecIndex = 1;
152 :
153 : sal_Int16 nScript;
154 0 : sal_Int32 nScriptVecIndex = 0;
155 :
156 : sal_Int32 nCurrWidth;
157 :
158 0 : while( nPosVecIndex < nPosVecSize )
159 : {
160 0 : nNextPos = maPosVec[ nPosVecIndex++ ];
161 0 : nScript = maScriptVec[ nScriptVecIndex++ ];
162 :
163 0 : SetOutDevFont( nScript );
164 0 : nCurrWidth = mrOutDevice.GetTextWidth( maText, nThisPos, nNextPos - nThisPos );
165 0 : maWidthVec.push_back( nCurrWidth );
166 0 : maTextSize.Width() += nCurrWidth;
167 0 : nThisPos = nNextPos;
168 : }
169 : }
170 :
171 : // calculate maximum font height
172 0 : SetOutDevFont( i18n::ScriptType::LATIN );
173 0 : maTextSize.Height() = std::max( maTextSize.Height(), mrOutDevice.GetTextHeight() );
174 0 : SetOutDevFont( i18n::ScriptType::ASIAN );
175 0 : maTextSize.Height() = std::max( maTextSize.Height(), mrOutDevice.GetTextHeight() );
176 0 : SetOutDevFont( i18n::ScriptType::COMPLEX );
177 0 : maTextSize.Height() = std::max( maTextSize.Height(), mrOutDevice.GetTextHeight() );
178 :
179 0 : mrOutDevice.SetFont( maDefltFont );
180 0 : }
181 :
182 0 : void SvtScriptedTextHelper_Impl::CalculateBreaks( const uno::Reference< i18n::XBreakIterator >& _xBreakIter )
183 : {
184 0 : maPosVec.clear();
185 0 : maScriptVec.clear();
186 :
187 : DBG_ASSERT( _xBreakIter.is(), "SvtScriptedTextHelper_Impl::CalculateBreaks - no break iterator" );
188 :
189 0 : sal_Int32 nLen = maText.getLength();
190 0 : if( nLen )
191 : {
192 0 : if( _xBreakIter.is() )
193 : {
194 0 : sal_Int32 nThisPos = 0; // first position of this portion
195 0 : sal_Int32 nNextPos = 0; // first position of next portion
196 : sal_Int16 nPortScript; // script type of this portion
197 0 : do
198 : {
199 0 : nPortScript = _xBreakIter->getScriptType( maText, nThisPos );
200 0 : nNextPos = _xBreakIter->endOfScript( maText, nThisPos, nPortScript );
201 :
202 0 : switch( nPortScript )
203 : {
204 : case i18n::ScriptType::LATIN:
205 : case i18n::ScriptType::ASIAN:
206 : case i18n::ScriptType::COMPLEX:
207 0 : maPosVec.push_back( nThisPos );
208 0 : maScriptVec.push_back( nPortScript );
209 0 : break;
210 : default:
211 : {
212 : /* *** handling of weak characters ***
213 : - first portion is weak: Use OutputDevice::HasGlyphs() to find the correct font
214 : - weak portion follows another portion: Script type of preceding portion is used */
215 0 : if( maPosVec.empty() )
216 : {
217 0 : sal_Int32 nCharIx = 0;
218 0 : sal_Int32 nNextCharIx = 0;
219 : sal_Int16 nScript;
220 0 : do
221 : {
222 0 : nScript = i18n::ScriptType::LATIN;
223 0 : while( (nScript != i18n::ScriptType::WEAK) && (nCharIx == nNextCharIx) )
224 : {
225 0 : nNextCharIx = mrOutDevice.HasGlyphs( GetFont( nScript ), maText, nCharIx, nNextPos - nCharIx );
226 0 : if( nCharIx == nNextCharIx )
227 0 : ++nScript;
228 : }
229 0 : if( nNextCharIx == nCharIx )
230 0 : ++nNextCharIx;
231 :
232 0 : maPosVec.push_back( nCharIx );
233 0 : maScriptVec.push_back( nScript );
234 0 : nCharIx = nNextCharIx;
235 : }
236 0 : while( nCharIx < nNextPos && nCharIx != -1 );
237 : }
238 : // nothing to do for following portions
239 : }
240 : }
241 0 : nThisPos = nNextPos;
242 : }
243 0 : while( (0 <= nThisPos) && (nThisPos < nLen) );
244 : }
245 : else // no break iterator: whole text LATIN
246 : {
247 0 : maPosVec.push_back( 0 );
248 0 : maScriptVec.push_back( i18n::ScriptType::LATIN );
249 : }
250 :
251 : // push end position of last portion
252 0 : if( !maPosVec.empty() )
253 0 : maPosVec.push_back( nLen );
254 : }
255 0 : CalculateSizes();
256 0 : }
257 :
258 0 : void SvtScriptedTextHelper_Impl::SetFonts( Font* _pLatinFont, Font* _pAsianFont, Font* _pCmplxFont )
259 : {
260 0 : maLatinFont = _pLatinFont ? *_pLatinFont : maDefltFont;
261 0 : maAsianFont = _pAsianFont ? *_pAsianFont : maDefltFont;
262 0 : maCmplxFont = _pCmplxFont ? *_pCmplxFont : maDefltFont;
263 0 : CalculateSizes();
264 0 : }
265 :
266 0 : void SvtScriptedTextHelper_Impl::SetText( const OUString& _rText, const uno::Reference< i18n::XBreakIterator >& _xBreakIter )
267 : {
268 0 : maText = _rText;
269 0 : CalculateBreaks( _xBreakIter );
270 0 : }
271 :
272 0 : const Size& SvtScriptedTextHelper_Impl::GetTextSize() const
273 : {
274 0 : return maTextSize;
275 : }
276 :
277 0 : void SvtScriptedTextHelper_Impl::DrawText( const Point& _rPos )
278 : {
279 0 : if( maText.isEmpty() || maPosVec.empty() )
280 0 : return;
281 :
282 : DBG_ASSERT( maPosVec.size() - 1 == maScriptVec.size(), "SvtScriptedTextHelper_Impl::DrawText - invalid vectors" );
283 : DBG_ASSERT( maScriptVec.size() == maWidthVec.size(), "SvtScriptedTextHelper_Impl::DrawText - invalid vectors" );
284 :
285 0 : maDefltFont = mrOutDevice.GetFont();
286 0 : Point aCurrPos( _rPos );
287 0 : sal_Int32 nThisPos = maPosVec[ 0 ];
288 : sal_Int32 nNextPos;
289 0 : sal_Int32 nPosVecSize = maPosVec.size();
290 0 : sal_Int32 nPosVecIndex = 1;
291 :
292 : sal_Int16 nScript;
293 0 : sal_Int32 nVecIndex = 0;
294 :
295 0 : while( nPosVecIndex < nPosVecSize )
296 : {
297 0 : nNextPos = maPosVec[ nPosVecIndex++ ];
298 0 : nScript = maScriptVec[ nVecIndex ];
299 :
300 0 : SetOutDevFont( nScript );
301 0 : mrOutDevice.DrawText( aCurrPos, maText, nThisPos, nNextPos - nThisPos );
302 0 : aCurrPos.X() += maWidthVec[ nVecIndex++ ];
303 0 : aCurrPos.X() += mrOutDevice.GetTextHeight() / 5; // add 20% of font height as portion spacing
304 0 : nThisPos = nNextPos;
305 : }
306 0 : mrOutDevice.SetFont( maDefltFont );
307 : }
308 :
309 :
310 :
311 :
312 0 : SvtScriptedTextHelper::SvtScriptedTextHelper( OutputDevice& _rOutDevice ) :
313 0 : mpImpl( new SvtScriptedTextHelper_Impl( _rOutDevice, NULL, NULL, NULL ) )
314 : {
315 0 : }
316 :
317 0 : SvtScriptedTextHelper::SvtScriptedTextHelper( const SvtScriptedTextHelper& _rCopy ) :
318 0 : mpImpl( new SvtScriptedTextHelper_Impl( *_rCopy.mpImpl ) )
319 : {
320 0 : }
321 :
322 0 : SvtScriptedTextHelper::~SvtScriptedTextHelper()
323 : {
324 0 : delete mpImpl;
325 0 : }
326 :
327 0 : void SvtScriptedTextHelper::SetFonts( Font* _pLatinFont, Font* _pAsianFont, Font* _pCmplxFont )
328 : {
329 0 : mpImpl->SetFonts( _pLatinFont, _pAsianFont, _pCmplxFont );
330 0 : }
331 :
332 0 : void SvtScriptedTextHelper::SetDefaultFont()
333 : {
334 0 : mpImpl->SetFonts( NULL, NULL, NULL );
335 0 : }
336 :
337 0 : void SvtScriptedTextHelper::SetText( const OUString& _rText, const uno::Reference< i18n::XBreakIterator >& _xBreakIter )
338 : {
339 0 : mpImpl->SetText( _rText, _xBreakIter );
340 0 : }
341 :
342 0 : const Size& SvtScriptedTextHelper::GetTextSize() const
343 : {
344 0 : return mpImpl->GetTextSize();
345 : }
346 :
347 0 : void SvtScriptedTextHelper::DrawText( const Point& _rPos )
348 : {
349 0 : mpImpl->DrawText( _rPos );
350 0 : }
351 :
352 :
353 :
354 :
355 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|