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