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