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