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 <iostream>
21 : #include <iomanip>
22 :
23 : #include "sal/config.h"
24 :
25 : #include <cstdio>
26 :
27 : #include <math.h>
28 : #include <sal/alloca.h>
29 :
30 : #include <salgdi.hxx>
31 : #include <sallayout.hxx>
32 : #include <basegfx/polygon/b2dpolypolygon.hxx>
33 : #include <basegfx/matrix/b2dhommatrix.hxx>
34 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 :
36 : #include <i18nlangtag/lang.h>
37 :
38 : #include <tools/debug.hxx>
39 : #include <vcl/svapp.hxx>
40 :
41 : #include <limits.h>
42 :
43 : #if defined _MSC_VER
44 : #pragma warning(push, 1)
45 : #endif
46 : #include <unicode/ubidi.h>
47 : #include <unicode/uchar.h>
48 : #if defined _MSC_VER
49 : #pragma warning(pop)
50 : #endif
51 :
52 : #include <algorithm>
53 :
54 : #ifdef DEBUG
55 : //#define MULTI_SL_DEBUG
56 : #endif
57 :
58 : #ifdef MULTI_SL_DEBUG
59 : #include <string>
60 : FILE * mslLogFile = NULL;
61 : FILE * mslLog()
62 : {
63 : #ifdef _MSC_VER
64 : std::string logFileName(getenv("TEMP"));
65 : logFileName.append("\\msllayout.log");
66 : if (mslLogFile == NULL) mslLogFile = fopen(logFileName.c_str(),"w");
67 : else fflush(mslLogFile);
68 : return mslLogFile;
69 : #else
70 : return stdout;
71 : #endif
72 : }
73 : #endif
74 :
75 0 : std::ostream &operator <<(std::ostream& s, ImplLayoutArgs &rArgs)
76 : {
77 : #ifndef SAL_LOG_INFO
78 : (void) rArgs;
79 : #else
80 : s << "ImplLayoutArgs{";
81 :
82 : s << "Flags=";
83 : if (rArgs.mnFlags == 0)
84 : s << 0;
85 : else {
86 : bool need_or = false;
87 : s << "{";
88 : #define TEST(x) if (rArgs.mnFlags & SAL_LAYOUT_##x) { if (need_or) s << "|"; s << #x; need_or = true; }
89 : TEST(BIDI_RTL);
90 : TEST(BIDI_STRONG);
91 : TEST(RIGHT_ALIGN);
92 : TEST(KERNING_PAIRS);
93 : TEST(KERNING_ASIAN);
94 : TEST(VERTICAL);
95 : TEST(COMPLEX_DISABLED);
96 : TEST(ENABLE_LIGATURES);
97 : TEST(SUBSTITUTE_DIGITS);
98 : TEST(KASHIDA_JUSTIFICATON);
99 : TEST(DISABLE_GLYPH_PROCESSING);
100 : TEST(FOR_FALLBACK);
101 : #undef TEST
102 : s << "}";
103 : }
104 :
105 : s << ",Length=" << rArgs.mnLength;
106 : s << ",MinCharPos=" << rArgs.mnMinCharPos;
107 : s << ",EndCharPos=" << rArgs.mnEndCharPos;
108 :
109 : s << ",Str=\"";
110 : int lim = rArgs.mnLength;
111 : if (lim > 10)
112 : lim = 7;
113 : for (int i = 0; i < lim; i++) {
114 : if (rArgs.mpStr[i] == '\n')
115 : s << "\\n";
116 : else if (rArgs.mpStr[i] < ' ' || (rArgs.mpStr[i] >= 0x7F && rArgs.mpStr[i] <= 0xFF))
117 : s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << (int) rArgs.mpStr[i] << std::setfill(' ') << std::setw(1) << std::dec;
118 : else if (rArgs.mpStr[i] < 0x7F)
119 : s << (char) rArgs.mpStr[i];
120 : else
121 : s << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int) rArgs.mpStr[i] << std::setfill(' ') << std::setw(1) << std::dec;
122 : }
123 : if (rArgs.mnLength > lim)
124 : s << "...";
125 : s << "\"";
126 :
127 : s << ",DXArray=";
128 : if (rArgs.mpDXArray) {
129 : s << "[";
130 : int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
131 : lim = count;
132 : if (lim > 10)
133 : lim = 7;
134 : for (int i = 0; i < lim; i++) {
135 : s << rArgs.mpDXArray[i];
136 : if (i < lim-1)
137 : s << ",";
138 : }
139 : if (count > lim) {
140 : if (count > lim + 1)
141 : s << "...";
142 : s << rArgs.mpDXArray[count-1];
143 : }
144 : s << "]";
145 : } else
146 : s << "NULL";
147 :
148 : s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
149 :
150 : s << "}";
151 :
152 : #endif
153 0 : return s;
154 : }
155 :
156 : // TODO: ask the glyph directly, for now we need this method because of #i99367#
157 : // true if a codepoint doesn't influence the logical text width
158 0 : bool IsDiacritic( sal_UCS4 nChar )
159 : {
160 : // shortcut abvious non-diacritics
161 0 : if( nChar < 0x0300 )
162 0 : return false;
163 0 : if( nChar >= 0x2100 )
164 0 : return false;
165 :
166 : // TODO: #i105058# use icu uchar.h's character classification instead of the handcrafted table
167 : struct DiaRange { sal_UCS4 mnMin, mnEnd;};
168 : static const DiaRange aRanges[] = {
169 : {0x0300, 0x0370},
170 : {0x0590, 0x05BE}, {0x05BF, 0x05C0}, {0x05C1, 0x05C3}, {0x05C4, 0x05C6}, {0x05C7, 0x05C8},
171 : {0x0610, 0x061B}, {0x064B, 0x0660}, {0x0670, 0x0671}, {0x06D6, 0x06DD}, {0x06DF, 0x06E5}, {0x06E7, 0x06E9}, {0x06EA,0x06EF},
172 : {0x0730, 0x074D}, {0x07A6, 0x07B1}, {0x07EB, 0x07F4},
173 : {0x1DC0, 0x1E00},
174 : {0x205F, 0x2070}, {0x20D0, 0x2100},
175 : {0xFB1E, 0xFB1F}
176 : };
177 :
178 : // TODO: almost anything is faster than an O(n) search
179 : static const int nCount = SAL_N_ELEMENTS(aRanges);
180 0 : const DiaRange* pRange = &aRanges[0];
181 0 : for( int i = nCount; --i >= 0; ++pRange )
182 0 : if( (pRange->mnMin <= nChar) && (nChar < pRange->mnEnd) )
183 0 : return true;
184 :
185 0 : return false;
186 : }
187 :
188 0 : int GetVerticalFlags( sal_UCS4 nChar )
189 : {
190 0 : if( (nChar >= 0x1100 && nChar <= 0x11f9) // Hangul Jamo
191 0 : || (nChar == 0x2030 || nChar == 0x2031) // per mille sign
192 0 : || (nChar >= 0x3000 && nChar <= 0xfaff) // unified CJK
193 0 : || (nChar >= 0xfe20 && nChar <= 0xfe6f) // CJK compatibility
194 0 : || (nChar >= 0xff00 && nChar <= 0xfffd) ) // other CJK
195 : {
196 : /* #i52932# remember:
197 : nChar == 0x2010 || nChar == 0x2015
198 : nChar == 0x2016 || nChar == 0x2026
199 : are GF_NONE also, but already handled in the outer if condition
200 : */
201 0 : if((nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012)
202 0 : || (nChar == 0xFF3B || nChar == 0xFF3D)
203 0 : || (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms
204 0 : || (nChar == 0xFFE3) )
205 0 : return GF_NONE; // not rotated
206 0 : else if( nChar == 0x30fc )
207 0 : return GF_ROTR; // right
208 0 : return GF_ROTL; // left
209 : }
210 0 : else if( (nChar >= 0x20000) && (nChar <= 0x3FFFF) ) // all SIP/TIP ideographs
211 0 : return GF_ROTL; // left
212 :
213 0 : return GF_NONE; // not rotated as default
214 : }
215 :
216 0 : sal_UCS4 GetVerticalChar( sal_UCS4 )
217 : {
218 0 : return 0; // #i14788# input method is responsible vertical char changes
219 : }
220 :
221 0 : VCL_DLLPUBLIC sal_UCS4 GetMirroredChar( sal_UCS4 nChar )
222 : {
223 0 : nChar = u_charMirror( nChar );
224 0 : return nChar;
225 : }
226 :
227 0 : VCL_DLLPUBLIC sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
228 : {
229 : // currently only conversion from ASCII digits is interesting
230 0 : if( (nChar < '0') || ('9' < nChar) )
231 0 : return nChar;
232 :
233 : int nOffset;
234 : // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
235 : // CAVEAT! To some like Mongolian MS assigned the same primary language
236 : // although the script type is different!
237 0 : switch( eLang & LANGUAGE_MASK_PRIMARY )
238 : {
239 : default:
240 0 : nOffset = 0;
241 0 : break;
242 : case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
243 0 : nOffset = 0x0660 - '0'; // arabic-indic digits
244 0 : break;
245 : case LANGUAGE_FARSI & LANGUAGE_MASK_PRIMARY:
246 : case LANGUAGE_URDU_PAKISTAN & LANGUAGE_MASK_PRIMARY:
247 : case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
248 : case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
249 0 : nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
250 0 : break;
251 : case LANGUAGE_BENGALI & LANGUAGE_MASK_PRIMARY:
252 0 : nOffset = 0x09E6 - '0'; // bengali
253 0 : break;
254 : case LANGUAGE_HINDI & LANGUAGE_MASK_PRIMARY:
255 0 : nOffset = 0x0966 - '0'; // devanagari
256 0 : break;
257 : case LANGUAGE_AMHARIC_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
258 : case LANGUAGE_TIGRIGNA_ETHIOPIA & LANGUAGE_MASK_PRIMARY:
259 : // TODO case:
260 0 : nOffset = 0x1369 - '0'; // ethiopic
261 0 : break;
262 : case LANGUAGE_GUJARATI & LANGUAGE_MASK_PRIMARY:
263 0 : nOffset = 0x0AE6 - '0'; // gujarati
264 0 : break;
265 : #ifdef LANGUAGE_GURMUKHI // TODO case:
266 : case LANGUAGE_GURMUKHI & LANGUAGE_MASK_PRIMARY:
267 : nOffset = 0x0A66 - '0'; // gurmukhi
268 : break;
269 : #endif
270 : case LANGUAGE_KANNADA & LANGUAGE_MASK_PRIMARY:
271 0 : nOffset = 0x0CE6 - '0'; // kannada
272 0 : break;
273 : case LANGUAGE_KHMER & LANGUAGE_MASK_PRIMARY:
274 0 : nOffset = 0x17E0 - '0'; // khmer
275 0 : break;
276 : case LANGUAGE_LAO & LANGUAGE_MASK_PRIMARY:
277 0 : nOffset = 0x0ED0 - '0'; // lao
278 0 : break;
279 : case LANGUAGE_MALAYALAM & LANGUAGE_MASK_PRIMARY:
280 0 : nOffset = 0x0D66 - '0'; // malayalam
281 0 : break;
282 : case LANGUAGE_MONGOLIAN_MONGOLIAN_LSO & LANGUAGE_MASK_PRIMARY:
283 0 : switch (eLang)
284 : {
285 : case LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA:
286 : case LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA:
287 : case LANGUAGE_MONGOLIAN_MONGOLIAN_LSO:
288 0 : nOffset = 0x1810 - '0'; // mongolian
289 0 : break;
290 : default:
291 0 : nOffset = 0; // mongolian cyrillic
292 0 : break;
293 : }
294 0 : break;
295 : case LANGUAGE_BURMESE & LANGUAGE_MASK_PRIMARY:
296 0 : nOffset = 0x1040 - '0'; // myanmar
297 0 : break;
298 : case LANGUAGE_ODIA & LANGUAGE_MASK_PRIMARY:
299 0 : nOffset = 0x0B66 - '0'; // odia
300 0 : break;
301 : case LANGUAGE_TAMIL & LANGUAGE_MASK_PRIMARY:
302 0 : nOffset = 0x0BE7 - '0'; // tamil
303 0 : break;
304 : case LANGUAGE_TELUGU & LANGUAGE_MASK_PRIMARY:
305 0 : nOffset = 0x0C66 - '0'; // telugu
306 0 : break;
307 : case LANGUAGE_THAI & LANGUAGE_MASK_PRIMARY:
308 0 : nOffset = 0x0E50 - '0'; // thai
309 0 : break;
310 : case LANGUAGE_TIBETAN & LANGUAGE_MASK_PRIMARY:
311 0 : nOffset = 0x0F20 - '0'; // tibetan
312 0 : break;
313 : }
314 :
315 0 : nChar += nOffset;
316 0 : return nChar;
317 : }
318 :
319 0 : inline bool IsControlChar( sal_UCS4 cChar )
320 : {
321 : // C0 control characters
322 0 : if( (0x0001 <= cChar) && (cChar <= 0x001F) )
323 0 : return true;
324 : // formatting characters
325 0 : if( (0x200E <= cChar) && (cChar <= 0x200F) )
326 0 : return true;
327 0 : if( (0x2028 <= cChar) && (cChar <= 0x202E) )
328 0 : return true;
329 : // deprecated formatting characters
330 0 : if( (0x206A <= cChar) && (cChar <= 0x206F) )
331 0 : return true;
332 0 : if( (0x2060 == cChar) )
333 0 : return true;
334 : // byte order markers and invalid unicode
335 0 : if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
336 0 : return true;
337 0 : return false;
338 : }
339 :
340 0 : bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
341 : {
342 : // check if charpos could extend current run
343 0 : int nIndex = maRuns.size();
344 0 : if( nIndex >= 2 )
345 : {
346 0 : int nRunPos0 = maRuns[ nIndex-2 ];
347 0 : int nRunPos1 = maRuns[ nIndex-1 ];
348 0 : if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) )
349 : {
350 : // extend current run by new charpos
351 0 : maRuns[ nIndex-1 ] = nCharPos + int(!bRTL);
352 0 : return false;
353 : }
354 : // ignore new charpos when it is in current run
355 0 : if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
356 0 : return false;
357 0 : if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
358 0 : return false;
359 : }
360 :
361 : // else append a new run consisting of the new charpos
362 0 : maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
363 0 : maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
364 0 : return true;
365 : }
366 :
367 0 : bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
368 : {
369 0 : if( nCharPos0 == nCharPos1 )
370 0 : return false;
371 :
372 : // swap if needed
373 0 : if( bRTL == (nCharPos0 < nCharPos1) )
374 : {
375 0 : int nTemp = nCharPos0;
376 0 : nCharPos0 = nCharPos1;
377 0 : nCharPos1 = nTemp;
378 : }
379 :
380 : // append new run
381 0 : maRuns.push_back( nCharPos0 );
382 0 : maRuns.push_back( nCharPos1 );
383 0 : return true;
384 : }
385 :
386 0 : bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
387 : {
388 0 : if( mnRunIndex >= (int)maRuns.size() )
389 0 : return false;
390 :
391 0 : int nMinCharPos = maRuns[ mnRunIndex+0 ];
392 0 : int nEndCharPos = maRuns[ mnRunIndex+1 ];
393 0 : if( nMinCharPos > nEndCharPos ) // reversed in RTL case
394 : {
395 0 : int nTemp = nMinCharPos;
396 0 : nMinCharPos = nEndCharPos;
397 0 : nEndCharPos = nTemp;
398 : }
399 :
400 0 : if( nCharPos < nMinCharPos )
401 0 : return false;
402 0 : if( nCharPos >= nEndCharPos )
403 0 : return false;
404 0 : return true;
405 : }
406 :
407 0 : bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
408 : {
409 0 : bool bRet = false;
410 0 : int nRunIndex = mnRunIndex;
411 :
412 0 : ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
413 :
414 0 : pThis->ResetPos();
415 :
416 0 : for (size_t i = 0; i < maRuns.size(); i+=2)
417 : {
418 0 : if( (bRet = PosIsInRun( nCharPos )) == true )
419 0 : break;
420 0 : pThis->NextRun();
421 : }
422 :
423 0 : pThis->mnRunIndex = nRunIndex;
424 0 : return bRet;
425 : }
426 :
427 0 : bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
428 : {
429 : // negative nCharPos => reset to first run
430 0 : if( *nCharPos < 0 )
431 0 : mnRunIndex = 0;
432 :
433 : // return false when all runs completed
434 0 : if( mnRunIndex >= (int)maRuns.size() )
435 0 : return false;
436 :
437 0 : int nRunPos0 = maRuns[ mnRunIndex+0 ];
438 0 : int nRunPos1 = maRuns[ mnRunIndex+1 ];
439 0 : *bRightToLeft = (nRunPos0 > nRunPos1);
440 :
441 0 : if( *nCharPos < 0 )
442 : {
443 : // get first valid nCharPos in run
444 0 : *nCharPos = nRunPos0;
445 : }
446 : else
447 : {
448 : // advance to next nCharPos for LTR case
449 0 : if( !*bRightToLeft )
450 0 : ++(*nCharPos);
451 :
452 : // advance to next run if current run is completed
453 0 : if( *nCharPos == nRunPos1 )
454 : {
455 0 : if( (mnRunIndex += 2) >= (int)maRuns.size() )
456 0 : return false;
457 0 : nRunPos0 = maRuns[ mnRunIndex+0 ];
458 0 : nRunPos1 = maRuns[ mnRunIndex+1 ];
459 0 : *bRightToLeft = (nRunPos0 > nRunPos1);
460 0 : *nCharPos = nRunPos0;
461 : }
462 : }
463 :
464 : // advance to next nCharPos for RTL case
465 0 : if( *bRightToLeft )
466 0 : --(*nCharPos);
467 :
468 0 : return true;
469 : }
470 :
471 0 : bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
472 : {
473 0 : if( mnRunIndex >= (int)maRuns.size() )
474 0 : return false;
475 :
476 0 : int nRunPos0 = maRuns[ mnRunIndex+0 ];
477 0 : int nRunPos1 = maRuns[ mnRunIndex+1 ];
478 0 : *bRightToLeft = (nRunPos1 < nRunPos0) ;
479 0 : if( !*bRightToLeft )
480 : {
481 0 : *nMinRunPos = nRunPos0;
482 0 : *nEndRunPos = nRunPos1;
483 : }
484 : else
485 : {
486 0 : *nMinRunPos = nRunPos1;
487 0 : *nEndRunPos = nRunPos0;
488 : }
489 0 : return true;
490 : }
491 :
492 0 : ImplLayoutArgs::ImplLayoutArgs( const sal_Unicode* pStr, int nLen,
493 : int nMinCharPos, int nEndCharPos, int nFlags, const LanguageTag& rLanguageTag )
494 : :
495 : maLanguageTag( rLanguageTag ),
496 : mnFlags( nFlags ),
497 : mnLength( nLen ),
498 : mnMinCharPos( nMinCharPos ),
499 : mnEndCharPos( nEndCharPos ),
500 : mpStr( pStr ),
501 : mpDXArray( NULL ),
502 : mnLayoutWidth( 0 ),
503 0 : mnOrientation( 0 )
504 : {
505 0 : if( mnFlags & SAL_LAYOUT_BIDI_STRONG )
506 : {
507 : // handle strong BiDi mode
508 :
509 : // do not bother to BiDi analyze strong LTR/RTL
510 : // TODO: can we assume these strings do not have unicode control chars?
511 : // if not remove the control characters from the runs
512 0 : bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
513 0 : AddRun( mnMinCharPos, mnEndCharPos, bRTL );
514 : }
515 : else
516 : {
517 : // handle weak BiDi mode
518 :
519 0 : UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
520 0 : if( mnFlags & SAL_LAYOUT_BIDI_RTL )
521 0 : nLevel = UBIDI_DEFAULT_RTL;
522 :
523 : // prepare substring for BiDi analysis
524 : // TODO: reuse allocated pParaBidi
525 0 : UErrorCode rcI18n = U_ZERO_ERROR;
526 0 : UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
527 0 : if( !pParaBidi )
528 0 : return;
529 0 : ubidi_setPara( pParaBidi, reinterpret_cast<const UChar *>(mpStr), mnLength, nLevel, NULL, &rcI18n ); // UChar != sal_Unicode in MinGW
530 :
531 0 : UBiDi* pLineBidi = pParaBidi;
532 0 : int nSubLength = mnEndCharPos - mnMinCharPos;
533 0 : if( nSubLength != mnLength )
534 : {
535 0 : pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
536 0 : ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
537 : }
538 :
539 : // run BiDi algorithm
540 0 : const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
541 : //maRuns.resize( 2 * nRunCount );
542 0 : for( int i = 0; i < nRunCount; ++i )
543 : {
544 : int32_t nMinPos, nLength;
545 0 : const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
546 0 : const int nPos0 = nMinPos + mnMinCharPos;
547 0 : const int nPos1 = nPos0 + nLength;
548 :
549 0 : const bool bRTL = (nDir == UBIDI_RTL);
550 0 : AddRun( nPos0, nPos1, bRTL );
551 : }
552 :
553 : // cleanup BiDi engine
554 0 : if( pLineBidi != pParaBidi )
555 0 : ubidi_close( pLineBidi );
556 0 : ubidi_close( pParaBidi );
557 : }
558 :
559 : // prepare calls to GetNextPos/GetNextRun
560 0 : maRuns.ResetPos();
561 : }
562 :
563 : // add a run after splitting it up to get rid of control chars
564 0 : void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
565 : {
566 : DBG_ASSERT( nCharPos0 <= nCharPos1, "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
567 :
568 : // remove control characters from runs by splitting them up
569 0 : if( !bRTL )
570 : {
571 0 : for( int i = nCharPos0; i < nCharPos1; ++i )
572 0 : if( IsControlChar( mpStr[i] ) )
573 : {
574 : // add run until control char
575 0 : maRuns.AddRun( nCharPos0, i, bRTL );
576 0 : nCharPos0 = i + 1;
577 : }
578 : }
579 : else
580 : {
581 0 : for( int i = nCharPos1; --i >= nCharPos0; )
582 0 : if( IsControlChar( mpStr[i] ) )
583 : {
584 : // add run until control char
585 0 : maRuns.AddRun( i+1, nCharPos1, bRTL );
586 0 : nCharPos1 = i;
587 : }
588 : }
589 :
590 : // add remainder of run
591 0 : maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
592 0 : }
593 :
594 0 : bool ImplLayoutArgs::PrepareFallback()
595 : {
596 : // short circuit if no fallback is needed
597 0 : if( maFallbackRuns.IsEmpty() )
598 : {
599 0 : maRuns.Clear();
600 0 : return false;
601 : }
602 :
603 : // convert the fallback requests to layout requests
604 : bool bRTL;
605 : int nMin, nEnd;
606 :
607 : // get the individual fallback requests
608 : typedef std::vector<int> IntVector;
609 0 : IntVector aPosVector;
610 0 : aPosVector.reserve( mnLength );
611 0 : maFallbackRuns.ResetPos();
612 0 : for(; maFallbackRuns.GetRun( &nMin, &nEnd, &bRTL ); maFallbackRuns.NextRun() )
613 0 : for( int i = nMin; i < nEnd; ++i )
614 0 : aPosVector.push_back( i );
615 0 : maFallbackRuns.Clear();
616 :
617 : // sort the individual fallback requests
618 0 : std::sort( aPosVector.begin(), aPosVector.end() );
619 :
620 : // adjust fallback runs to have the same order and limits of the original runs
621 0 : ImplLayoutRuns aNewRuns;
622 0 : maRuns.ResetPos();
623 0 : for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
624 : {
625 0 : if( !bRTL) {
626 0 : IntVector::const_iterator it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
627 0 : for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
628 0 : aNewRuns.AddPos( *it, bRTL );
629 : } else {
630 0 : IntVector::const_iterator it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
631 0 : while( (it != aPosVector.begin()) && (*--it >= nMin) )
632 0 : aNewRuns.AddPos( *it, bRTL );
633 : }
634 : }
635 :
636 0 : maRuns = aNewRuns; // TODO: use vector<>::swap()
637 0 : maRuns.ResetPos();
638 0 : return true;
639 : }
640 :
641 0 : bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
642 : {
643 0 : bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
644 0 : maRuns.NextRun();
645 0 : return bValid;
646 : }
647 :
648 0 : SalLayout::SalLayout()
649 : : mnMinCharPos( -1 ),
650 : mnEndCharPos( -1 ),
651 : mnLayoutFlags( 0 ),
652 : mnUnitsPerPixel( 1 ),
653 : mnOrientation( 0 ),
654 : mnRefCount( 1 ),
655 0 : maDrawOffset( 0, 0 )
656 0 : {}
657 :
658 0 : SalLayout::~SalLayout()
659 0 : {}
660 :
661 0 : void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
662 : {
663 0 : mnMinCharPos = rArgs.mnMinCharPos;
664 0 : mnEndCharPos = rArgs.mnEndCharPos;
665 0 : mnLayoutFlags = rArgs.mnFlags;
666 0 : mnOrientation = rArgs.mnOrientation;
667 0 : }
668 :
669 0 : void SalLayout::Release() const
670 : {
671 : // TODO: protect when multiple threads can access this
672 0 : if( --mnRefCount > 0 )
673 0 : return;
674 : // const_cast because some compilers violate ANSI C++ spec
675 0 : delete const_cast<SalLayout*>(this);
676 : }
677 :
678 0 : Point SalLayout::GetDrawPosition( const Point& rRelative ) const
679 : {
680 0 : Point aPos = maDrawBase;
681 0 : Point aOfs = rRelative + maDrawOffset;
682 :
683 0 : if( mnOrientation == 0 )
684 0 : aPos += aOfs;
685 : else
686 : {
687 : // cache trigonometric results
688 : static int nOldOrientation = 0;
689 : static double fCos = 1.0, fSin = 0.0;
690 0 : if( nOldOrientation != mnOrientation )
691 : {
692 0 : nOldOrientation = mnOrientation;
693 0 : double fRad = mnOrientation * (M_PI / 1800.0);
694 0 : fCos = cos( fRad );
695 0 : fSin = sin( fRad );
696 : }
697 :
698 0 : double fX = aOfs.X();
699 0 : double fY = aOfs.Y();
700 0 : long nX = static_cast<long>( +fCos * fX + fSin * fY );
701 0 : long nY = static_cast<long>( +fCos * fY - fSin * fX );
702 0 : aPos += Point( nX, nY );
703 : }
704 :
705 0 : return aPos;
706 : }
707 :
708 : // returns asian kerning values in quarter of character width units
709 : // to enable automatic halfwidth substitution for fullwidth punctuation
710 : // return value is negative for l, positive for r, zero for neutral
711 :
712 : // If the range doesn't match in 0x3000 and 0x30FB, please change
713 : // also ImplCalcKerning.
714 :
715 0 : int SalLayout::CalcAsianKerning( sal_UCS4 c, bool bLeft, bool /*TODO:? bVertical*/ )
716 : {
717 : // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
718 : static const signed char nTable[0x30] =
719 : {
720 : 0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
721 : +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
722 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
723 : };
724 :
725 0 : int nResult = 0;
726 0 : if( (c >= 0x3000) && (c < 0x3030) )
727 0 : nResult = nTable[ c - 0x3000 ];
728 0 : else switch( c )
729 : {
730 : case 0x30FB:
731 0 : nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
732 0 : break;
733 : case 0x2019: case 0x201D:
734 : case 0xFF01: case 0xFF09: case 0xFF0C:
735 : case 0xFF1A: case 0xFF1B:
736 0 : nResult = -2;
737 0 : break;
738 : case 0x2018: case 0x201C:
739 : case 0xFF08:
740 0 : nResult = +2;
741 0 : break;
742 : default:
743 0 : break;
744 : }
745 :
746 0 : return nResult;
747 : }
748 :
749 0 : bool SalLayout::GetOutline( SalGraphics& rSalGraphics,
750 : ::basegfx::B2DPolyPolygonVector& rVector ) const
751 : {
752 0 : bool bAllOk = true;
753 0 : bool bOneOk = false;
754 :
755 0 : Point aPos;
756 0 : ::basegfx::B2DPolyPolygon aGlyphOutline;
757 0 : for( int nStart = 0;;)
758 : {
759 : sal_GlyphId nLGlyph;
760 0 : if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
761 0 : break;
762 :
763 : // get outline of individual glyph, ignoring "empty" glyphs
764 0 : bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
765 0 : bAllOk &= bSuccess;
766 0 : bOneOk |= bSuccess;
767 : // only add non-empty outlines
768 0 : if( bSuccess && (aGlyphOutline.count() > 0) )
769 : {
770 0 : if( aPos.X() || aPos.Y() )
771 : {
772 0 : aGlyphOutline.transform(basegfx::tools::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
773 : }
774 :
775 : // insert outline at correct position
776 0 : rVector.push_back( aGlyphOutline );
777 : }
778 0 : }
779 :
780 0 : return (bAllOk && bOneOk);
781 : }
782 :
783 0 : bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
784 : {
785 0 : bool bRet = false;
786 0 : rRect.SetEmpty();
787 :
788 0 : Point aPos;
789 0 : Rectangle aRectangle;
790 0 : for( int nStart = 0;;)
791 : {
792 : sal_GlyphId nLGlyph;
793 0 : if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
794 0 : break;
795 :
796 : // get bounding rectangle of individual glyph
797 0 : if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
798 : {
799 : // merge rectangle
800 0 : aRectangle += aPos;
801 0 : if (rRect.IsEmpty())
802 0 : rRect = aRectangle;
803 : else
804 0 : rRect.Union(aRectangle);
805 0 : bRet = true;
806 : }
807 0 : }
808 :
809 0 : return bRet;
810 : }
811 :
812 0 : bool SalLayout::IsSpacingGlyph( sal_GlyphId nGlyph ) const
813 : {
814 0 : bool bRet = false;
815 0 : if( nGlyph & GF_ISCHAR )
816 : {
817 0 : long nChar = nGlyph & GF_IDXMASK;
818 : bRet = (nChar <= 0x0020) // blank
819 : //|| (nChar == 0x00A0) // non breaking space
820 0 : || (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
821 0 : || (nChar == 0x3000); // ideographic space
822 : }
823 : else
824 0 : bRet = ((nGlyph & GF_IDXMASK) == 3);
825 0 : return bRet;
826 : }
827 :
828 0 : GenericSalLayout::GenericSalLayout()
829 0 : {}
830 :
831 0 : GenericSalLayout::~GenericSalLayout()
832 0 : {}
833 :
834 0 : void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
835 : {
836 0 : m_GlyphItems.push_back(rGlyphItem);
837 0 : }
838 :
839 0 : bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
840 : {
841 : // initialize character extents buffer
842 0 : int nCharCount = mnEndCharPos - mnMinCharPos;
843 0 : for( int n = 0; n < nCharCount; ++n )
844 0 : pCharWidths[n] = 0;
845 :
846 : // determine cluster extents
847 0 : for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), end = m_GlyphItems.end(); pG != end ; ++pG)
848 : {
849 : // use cluster start to get char index
850 0 : if( !pG->IsClusterStart() )
851 0 : continue;
852 :
853 0 : int n = pG->mnCharPos;
854 0 : if( n >= mnEndCharPos )
855 0 : continue;
856 0 : n -= mnMinCharPos;
857 0 : if( n < 0 )
858 0 : continue;
859 :
860 : // left glyph in cluster defines default extent
861 0 : long nXPosMin = pG->maLinearPos.X();
862 0 : long nXPosMax = nXPosMin + pG->mnNewWidth;
863 :
864 : // calculate right x-position for this glyph cluster
865 : // break if no more glyphs in layout
866 : // break at next glyph cluster start
867 0 : while( (pG+1 != end) && !pG[1].IsClusterStart() )
868 : {
869 : // advance to next glyph in cluster
870 0 : ++pG;
871 :
872 0 : if( pG->IsDiacritic() )
873 0 : continue; // ignore diacritics
874 : // get leftmost x-extent of this glyph
875 0 : long nXPos = pG->maLinearPos.X();
876 0 : if( nXPosMin > nXPos )
877 0 : nXPosMin = nXPos;
878 :
879 : // get rightmost x-extent of this glyph
880 0 : nXPos += pG->mnNewWidth;
881 0 : if( nXPosMax < nXPos )
882 0 : nXPosMax = nXPos;
883 : }
884 :
885 : // when the current cluster overlaps with the next one assume
886 : // rightmost cluster edge is the leftmost edge of next cluster
887 : // for clusters that do not have x-sorted glyphs
888 : // TODO: avoid recalculation of left bound in next cluster iteration
889 0 : for( GlyphVector::const_iterator pN = pG; ++pN != end; )
890 : {
891 0 : if( pN->IsClusterStart() )
892 0 : break;
893 0 : if( pN->IsDiacritic() )
894 0 : continue; // ignore diacritics
895 0 : if( nXPosMax > pN->maLinearPos.X() )
896 0 : nXPosMax = pN->maLinearPos.X();
897 : }
898 0 : if( nXPosMax < nXPosMin )
899 0 : nXPosMin = nXPosMax = 0;
900 :
901 : // character width is sum of glyph cluster widths
902 0 : pCharWidths[n] += nXPosMax - nXPosMin;
903 : }
904 :
905 : // TODO: distribute the cluster width proportionally to the characters
906 : // clusters (e.g. ligatures) correspond to more than one char index,
907 : // so some character widths are still uninitialized. This is solved
908 : // by setting the first charwidth of the cluster to the cluster width
909 :
910 0 : return true;
911 : }
912 :
913 0 : long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
914 : {
915 0 : if( pCharWidths )
916 0 : if( !GetCharWidths( pCharWidths ) )
917 0 : return 0;
918 :
919 0 : long nWidth = GetTextWidth();
920 0 : return nWidth;
921 : }
922 :
923 : // the text width is the maximum logical extent of all glyphs
924 0 : long GenericSalLayout::GetTextWidth() const
925 : {
926 0 : if( m_GlyphItems.empty() )
927 0 : return 0;
928 :
929 : // initialize the extent
930 0 : long nMinPos = 0;
931 0 : long nMaxPos = 0;
932 :
933 0 : for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), end = m_GlyphItems.end(); pG != end ; ++pG )
934 : {
935 : // update the text extent with the glyph extent
936 0 : long nXPos = pG->maLinearPos.X();
937 0 : if( nMinPos > nXPos )
938 0 : nMinPos = nXPos;
939 0 : nXPos += pG->mnNewWidth - pG->mnXOffset;
940 0 : if( nMaxPos < nXPos )
941 0 : nMaxPos = nXPos;
942 : }
943 :
944 0 : long nWidth = nMaxPos - nMinPos;
945 0 : return nWidth;
946 : }
947 :
948 0 : void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
949 : {
950 0 : SalLayout::AdjustLayout( rArgs );
951 :
952 0 : if( rArgs.mpDXArray )
953 0 : ApplyDXArray( rArgs );
954 0 : else if( rArgs.mnLayoutWidth )
955 0 : Justify( rArgs.mnLayoutWidth );
956 0 : }
957 :
958 : // This DXArray thing is one of the stupidest ideas I have ever seen (I've been
959 : // told that it probably a one-to-one mapping of some Windows 3.1 API, which is
960 : // telling). To justify a text string, Writer calls OutputDevice::GetTextArray()
961 : // to get an array that maps input characters (not glyphs) to their absolute
962 : // position, GetTextArray() in turn calls SalLayout::FillDXArray() to get an
963 : // array of character widths that it converts to absolute positions.
964 :
965 : // Writer would then apply justification adjustments to that array of absolute
966 : // character positions and return to OutputDevice, which eventually calls
967 : // ApplyDXArray(), which needs to extract the individual adjustments for each
968 : // character to apply it to corresponding glyphs, and since that information is
969 : // already lost it tries to do some heuristics to guess it again. Those
970 : // heuristics often fail, and have always been a source of all sorts of weird
971 : // text layout bugs, and instead of fixing the broken design a hack after hack
972 : // have been applied on top of it, making it a complete mess that nobody
973 : // understands.
974 :
975 : // As you can see by now, this is utterly stupid, why Writer does not just send
976 : // us directly the advance width transformations it wants to apply to each
977 : // character instead of this whole mess?
978 :
979 0 : void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
980 : {
981 0 : if( m_GlyphItems.empty())
982 0 : return;
983 :
984 : // determine cluster boundaries and x base offset
985 0 : const int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
986 0 : int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
987 : size_t i;
988 : int n,p;
989 0 : long nBasePointX = -1;
990 0 : if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
991 0 : nBasePointX = 0;
992 0 : for(p = 0; p < nCharCount; ++p )
993 0 : pLogCluster[ p ] = -1;
994 :
995 0 : for( i = 0; i < m_GlyphItems.size(); ++i)
996 : {
997 0 : n = m_GlyphItems[i].mnCharPos - rArgs.mnMinCharPos;
998 0 : if( (n < 0) || (nCharCount <= n) )
999 0 : continue;
1000 0 : if( pLogCluster[ n ] < 0 )
1001 0 : pLogCluster[ n ] = i;
1002 0 : if( nBasePointX < 0 )
1003 0 : nBasePointX = m_GlyphItems[i].maLinearPos.X();
1004 : }
1005 : // retarget unresolved pLogCluster[n] to a glyph inside the cluster
1006 : // TODO: better do it while the deleted-glyph markers are still there
1007 0 : for( n = 0; n < nCharCount; ++n )
1008 0 : if( (p = pLogCluster[0]) >= 0 )
1009 0 : break;
1010 0 : if( n >= nCharCount )
1011 0 : return;
1012 0 : for( n = 0; n < nCharCount; ++n )
1013 : {
1014 0 : if( pLogCluster[ n ] < 0 )
1015 0 : pLogCluster[ n ] = p;
1016 : else
1017 0 : p = pLogCluster[ n ];
1018 : }
1019 :
1020 : // calculate adjusted cluster widths
1021 0 : sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( m_GlyphItems.size() * sizeof(sal_Int32) );
1022 0 : for( i = 0; i < m_GlyphItems.size(); ++i )
1023 0 : pNewGlyphWidths[ i ] = 0;
1024 :
1025 : bool bRTL;
1026 0 : for( int nCharPos = p = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
1027 : {
1028 0 : n = nCharPos - rArgs.mnMinCharPos;
1029 0 : if( (n < 0) || (nCharCount <= n) ) continue;
1030 :
1031 0 : if( pLogCluster[ n ] >= 0 )
1032 0 : p = pLogCluster[ n ];
1033 0 : if( p >= 0 )
1034 : {
1035 0 : long nDelta = rArgs.mpDXArray[ n ] ;
1036 0 : if( n > 0 )
1037 0 : nDelta -= rArgs.mpDXArray[ n-1 ];
1038 0 : pNewGlyphWidths[ p ] += nDelta * mnUnitsPerPixel;
1039 : }
1040 : }
1041 :
1042 : // move cluster positions using the adjusted widths
1043 0 : long nDelta = 0;
1044 0 : long nNewPos = 0;
1045 0 : for( i = 0; i < m_GlyphItems.size(); ++i)
1046 : {
1047 0 : if( m_GlyphItems[i].IsClusterStart() )
1048 : {
1049 : // calculate original and adjusted cluster width
1050 0 : int nOldClusterWidth = m_GlyphItems[i].mnNewWidth - m_GlyphItems[i].mnXOffset;
1051 0 : int nNewClusterWidth = pNewGlyphWidths[i];
1052 : size_t j;
1053 0 : for( j = i; ++j < m_GlyphItems.size(); )
1054 : {
1055 0 : if( m_GlyphItems[j].IsClusterStart() )
1056 0 : break;
1057 0 : if( !m_GlyphItems[j].IsDiacritic() ) // #i99367# ignore diacritics
1058 0 : nOldClusterWidth += m_GlyphItems[j].mnNewWidth - m_GlyphItems[j].mnXOffset;
1059 0 : nNewClusterWidth += pNewGlyphWidths[j];
1060 : }
1061 0 : const int nDiff = nNewClusterWidth - nOldClusterWidth;
1062 :
1063 : // adjust cluster glyph widths and positions
1064 0 : nDelta = nBasePointX + (nNewPos - m_GlyphItems[i].maLinearPos.X());
1065 0 : if( !m_GlyphItems[i].IsRTLGlyph() )
1066 : {
1067 : // for LTR case extend rightmost glyph in cluster
1068 0 : m_GlyphItems[j - 1].mnNewWidth += nDiff;
1069 : }
1070 : else
1071 : {
1072 : // right align cluster in new space for RTL case
1073 0 : m_GlyphItems[i].mnNewWidth += nDiff;
1074 0 : nDelta += nDiff;
1075 : }
1076 :
1077 0 : nNewPos += nNewClusterWidth;
1078 : }
1079 :
1080 0 : m_GlyphItems[i].maLinearPos.X() += nDelta;
1081 : }
1082 : }
1083 :
1084 0 : void GenericSalLayout::Justify( long nNewWidth )
1085 : {
1086 0 : nNewWidth *= mnUnitsPerPixel;
1087 0 : int nOldWidth = GetTextWidth();
1088 0 : if( !nOldWidth || nNewWidth==nOldWidth )
1089 0 : return;
1090 :
1091 0 : if(m_GlyphItems.empty())
1092 : {
1093 0 : return;
1094 : }
1095 : // find rightmost glyph, it won't get stretched
1096 0 : GlyphVector::iterator pGRight = m_GlyphItems.begin();
1097 0 : pGRight += m_GlyphItems.size() - 1;
1098 0 : GlyphVector::iterator pG;
1099 : // count stretchable glyphs
1100 0 : int nStretchable = 0;
1101 0 : int nMaxGlyphWidth = 0;
1102 0 : for(pG = m_GlyphItems.begin(); pG != pGRight; ++pG)
1103 : {
1104 0 : if( !pG->IsDiacritic() )
1105 0 : ++nStretchable;
1106 0 : if( nMaxGlyphWidth < pG->mnOrigWidth )
1107 0 : nMaxGlyphWidth = pG->mnOrigWidth;
1108 : }
1109 :
1110 : // move rightmost glyph to requested position
1111 0 : nOldWidth -= pGRight->mnOrigWidth;
1112 0 : if( nOldWidth <= 0 )
1113 0 : return;
1114 0 : if( nNewWidth < nMaxGlyphWidth)
1115 0 : nNewWidth = nMaxGlyphWidth;
1116 0 : nNewWidth -= pGRight->mnOrigWidth;
1117 0 : pGRight->maLinearPos.X() = maBasePoint.X() + nNewWidth;
1118 :
1119 : // justify glyph widths and positions
1120 0 : int nDiffWidth = nNewWidth - nOldWidth;
1121 0 : if( nDiffWidth >= 0) // expanded case
1122 : {
1123 : // expand width by distributing space between glyphs evenly
1124 0 : int nDeltaSum = 0;
1125 0 : for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
1126 : {
1127 : // move glyph to justified position
1128 0 : pG->maLinearPos.X() += nDeltaSum;
1129 :
1130 : // do not stretch non-stretchable glyphs
1131 0 : if( pG->IsDiacritic() || (nStretchable <= 0) )
1132 0 : continue;
1133 :
1134 : // distribute extra space equally to stretchable glyphs
1135 0 : int nDeltaWidth = nDiffWidth / nStretchable--;
1136 0 : nDiffWidth -= nDeltaWidth;
1137 0 : pG->mnNewWidth += nDeltaWidth;
1138 0 : nDeltaSum += nDeltaWidth;
1139 : }
1140 : }
1141 : else // condensed case
1142 : {
1143 : // squeeze width by moving glyphs proportionally
1144 0 : double fSqueeze = (double)nNewWidth / nOldWidth;
1145 0 : if(m_GlyphItems.size() > 1)
1146 : {
1147 0 : for( pG = m_GlyphItems.begin(); ++pG != pGRight;)
1148 : {
1149 0 : int nX = pG->maLinearPos.X() - maBasePoint.X();
1150 0 : nX = (int)(nX * fSqueeze);
1151 0 : pG->maLinearPos.X() = nX + maBasePoint.X();
1152 : }
1153 : }
1154 : // adjust glyph widths to new positions
1155 0 : for( pG = m_GlyphItems.begin(); pG != pGRight; ++pG )
1156 0 : pG->mnNewWidth = pG[1].maLinearPos.X() - pG[0].maLinearPos.X();
1157 : }
1158 : }
1159 :
1160 0 : void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
1161 : {
1162 0 : long nOffset = 0;
1163 :
1164 0 : for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1165 : {
1166 0 : const int n = pG->mnCharPos;
1167 0 : if( n < nLength - 1)
1168 : {
1169 : // ignore code ranges that are not affected by asian punctuation compression
1170 0 : const sal_Unicode cHere = pStr[n];
1171 0 : if( ((0x3000 != (cHere & 0xFF00)) && (0x2010 != (cHere & 0xFFF0))) || (0xFF00 != (cHere & 0xFF00)) )
1172 0 : continue;
1173 0 : const sal_Unicode cNext = pStr[n+1];
1174 0 : if( ((0x3000 != (cNext & 0xFF00)) && (0x2010 != (cNext & 0xFFF0))) || (0xFF00 != (cNext & 0xFF00)) )
1175 0 : continue;
1176 :
1177 : // calculate compression values
1178 0 : const bool bVertical = false;
1179 0 : long nKernFirst = +CalcAsianKerning( cHere, true, bVertical );
1180 0 : long nKernNext = -CalcAsianKerning( cNext, false, bVertical );
1181 :
1182 : // apply punctuation compression to logical glyph widths
1183 0 : long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
1184 0 : if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
1185 : {
1186 0 : int nGlyphWidth = pG->mnOrigWidth;
1187 0 : nDelta = (nDelta * nGlyphWidth + 2) / 4;
1188 0 : if( pG+1 == pGEnd )
1189 0 : pG->mnNewWidth += nDelta;
1190 0 : nOffset += nDelta;
1191 : }
1192 : }
1193 :
1194 : // adjust the glyph positions to the new glyph widths
1195 0 : if( pG+1 != pGEnd )
1196 0 : pG->maLinearPos.X() += nOffset;
1197 : }
1198 0 : }
1199 :
1200 0 : void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
1201 : {
1202 : // TODO: reimplement method when container type for GlyphItems changes
1203 :
1204 : // skip if the kashida glyph in the font looks suspicious
1205 0 : if( nKashidaWidth <= 0 )
1206 0 : return;
1207 :
1208 : // calculate max number of needed kashidas
1209 0 : int nKashidaCount = 0;
1210 0 : for (GlyphVector::iterator pG = m_GlyphItems.begin();
1211 0 : pG != m_GlyphItems.end(); ++pG)
1212 : {
1213 : // only inject kashidas in RTL contexts
1214 0 : if( !pG->IsRTLGlyph() )
1215 0 : continue;
1216 : // no kashida-injection for blank justified expansion either
1217 0 : if( IsSpacingGlyph( pG->maGlyphId) )
1218 0 : continue;
1219 :
1220 : // calculate gap, ignore if too small
1221 0 : int nGapWidth = pG->mnNewWidth - pG->mnOrigWidth;
1222 : // worst case is one kashida even for mini-gaps
1223 0 : if( nGapWidth < nKashidaWidth )
1224 0 : continue;
1225 :
1226 0 : nKashidaCount = 0;
1227 0 : Point aPos = pG->maLinearPos;
1228 0 : aPos.X() -= nGapWidth; // cluster is already right aligned
1229 0 : int const nCharPos = pG->mnCharPos;
1230 0 : GlyphVector::iterator pG2 = pG;
1231 0 : for(; nGapWidth > nKashidaWidth; nGapWidth -= nKashidaWidth, ++nKashidaCount )
1232 : {
1233 : pG2 = m_GlyphItems.insert(pG2, GlyphItem(nCharPos, nKashidaIndex, aPos,
1234 0 : GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth ));
1235 0 : ++pG2;
1236 0 : aPos.X() += nKashidaWidth;
1237 : }
1238 :
1239 : // fixup rightmost kashida for gap remainder
1240 0 : if( nGapWidth > 0 )
1241 : {
1242 : pG2 = m_GlyphItems.insert(pG2, GlyphItem(nCharPos, nKashidaIndex, aPos,
1243 0 : GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaCount ? nGapWidth : nGapWidth/2 ));
1244 0 : ++pG2;
1245 0 : aPos.X() += nGapWidth;
1246 : }
1247 0 : pG = pG2;
1248 : }
1249 : }
1250 :
1251 0 : void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
1252 : {
1253 : // initialize result array
1254 0 : long nXPos = -1;
1255 : int i;
1256 0 : for( i = 0; i < nMaxIndex; ++i )
1257 0 : pCaretXArray[ i ] = nXPos;
1258 :
1259 : // calculate caret positions using glyph array
1260 0 : for( GlyphVector::const_iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1261 : {
1262 0 : nXPos = pG->maLinearPos.X();
1263 0 : long nXRight = nXPos + pG->mnOrigWidth;
1264 0 : int n = pG->mnCharPos;
1265 0 : int nCurrIdx = 2 * (n - mnMinCharPos);
1266 0 : if( !pG->IsRTLGlyph() )
1267 : {
1268 : // normal positions for LTR case
1269 0 : pCaretXArray[ nCurrIdx ] = nXPos;
1270 0 : pCaretXArray[ nCurrIdx+1 ] = nXRight;
1271 : }
1272 : else
1273 : {
1274 : // reverse positions for RTL case
1275 0 : pCaretXArray[ nCurrIdx ] = nXRight;
1276 0 : pCaretXArray[ nCurrIdx+1 ] = nXPos;
1277 : }
1278 : }
1279 0 : }
1280 :
1281 0 : sal_Int32 GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
1282 : {
1283 0 : int nCharCapacity = mnEndCharPos - mnMinCharPos;
1284 0 : sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
1285 0 : if( !GetCharWidths( pCharWidths ) )
1286 0 : return -1;
1287 :
1288 0 : long nWidth = 0;
1289 0 : for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1290 : {
1291 0 : nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
1292 0 : if( nWidth > nMaxWidth )
1293 0 : return i;
1294 0 : nWidth += nCharExtra;
1295 : }
1296 :
1297 0 : return -1;
1298 : }
1299 :
1300 0 : int GenericSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1301 : int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry,
1302 : const PhysicalFontFace** /*pFallbackFonts*/ ) const
1303 : {
1304 0 : GlyphVector::const_iterator pG = m_GlyphItems.begin();
1305 0 : GlyphVector::const_iterator pGEnd = m_GlyphItems.end();
1306 0 : pG += nStart;
1307 :
1308 : // find next glyph in substring
1309 0 : for(; pG != pGEnd; ++nStart, ++pG )
1310 : {
1311 0 : int n = pG->mnCharPos;
1312 0 : if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
1313 0 : break;
1314 : }
1315 :
1316 : // return zero if no more glyph found
1317 0 : if( nStart >= (int)m_GlyphItems.size() )
1318 0 : return 0;
1319 :
1320 0 : if( pG == pGEnd )
1321 0 : return 0;
1322 :
1323 : // calculate absolute position in pixel units
1324 0 : Point aRelativePos = pG->maLinearPos - maBasePoint;
1325 :
1326 : // find more glyphs which can be merged into one drawing instruction
1327 0 : int nCount = 0;
1328 0 : long nYPos = pG->maLinearPos.Y();
1329 0 : long nOldFlags = pG->maGlyphId;
1330 : for(;;)
1331 : {
1332 : // update return data with glyph info
1333 0 : ++nCount;
1334 0 : *(pGlyphs++) = pG->maGlyphId;
1335 0 : if( pCharPosAry )
1336 0 : *(pCharPosAry++) = pG->mnCharPos;
1337 0 : if( pGlyphAdvAry )
1338 0 : *pGlyphAdvAry = pG->mnNewWidth;
1339 :
1340 : // break at end of glyph list
1341 0 : if( ++nStart >= (int)m_GlyphItems.size() )
1342 0 : break;
1343 : // break when enough glyphs
1344 0 : if( nCount >= nLen )
1345 0 : break;
1346 :
1347 0 : long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X();
1348 0 : if( pGlyphAdvAry )
1349 : {
1350 : // override default advance width with correct value
1351 0 : *(pGlyphAdvAry++) = nGlyphAdvance;
1352 : }
1353 : else
1354 : {
1355 : // stop when next x-position is unexpected
1356 0 : if( pG->mnOrigWidth != nGlyphAdvance )
1357 0 : break;
1358 : }
1359 :
1360 : // advance to next glyph
1361 0 : ++pG;
1362 :
1363 : // stop when next y-position is unexpected
1364 0 : if( nYPos != pG->maLinearPos.Y() )
1365 0 : break;
1366 :
1367 : // stop when no longer in string
1368 0 : int n = pG->mnCharPos;
1369 0 : if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
1370 : break;
1371 :
1372 : // stop when glyph flags change
1373 0 : if( (nOldFlags ^ pG->maGlyphId) & GF_FLAGMASK )
1374 0 : break;
1375 :
1376 0 : nOldFlags = pG->maGlyphId; // &GF_FLAGMASK not needed for test above
1377 0 : }
1378 :
1379 0 : aRelativePos.X() /= mnUnitsPerPixel;
1380 0 : aRelativePos.Y() /= mnUnitsPerPixel;
1381 0 : rPos = GetDrawPosition( aRelativePos );
1382 :
1383 0 : return nCount;
1384 : }
1385 :
1386 0 : void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
1387 : {
1388 0 : if( nStart >= (int)m_GlyphItems.size() )
1389 0 : return;
1390 :
1391 0 : GlyphVector::iterator pG = m_GlyphItems.begin();
1392 0 : pG += nStart;
1393 :
1394 : // the nNewXPos argument determines the new cell position
1395 : // as RTL-glyphs are right justified in their cell
1396 : // the cell position needs to be adjusted to the glyph position
1397 0 : if( pG->IsRTLGlyph() )
1398 0 : nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
1399 : // calculate the x-offset to the old position
1400 0 : long nXDelta = nNewXPos - pG->maLinearPos.X();
1401 : // adjust all following glyph positions if needed
1402 0 : if( nXDelta != 0 )
1403 : {
1404 0 : for( GlyphVector::iterator pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1405 : {
1406 0 : pG->maLinearPos.X() += nXDelta;
1407 : }
1408 : }
1409 : }
1410 :
1411 0 : void GenericSalLayout::DropGlyph( int nStart )
1412 : {
1413 0 : if( nStart >= (int)m_GlyphItems.size())
1414 0 : return;
1415 :
1416 0 : GlyphVector::iterator pG = m_GlyphItems.begin();
1417 0 : pG += nStart;
1418 0 : pG->maGlyphId = GF_DROPPED;
1419 0 : pG->mnCharPos = -1;
1420 : }
1421 :
1422 0 : void GenericSalLayout::Simplify( bool bIsBase )
1423 : {
1424 0 : const sal_GlyphId nDropMarker = bIsBase ? GF_DROPPED : 0;
1425 :
1426 : // remove dropped glyphs inplace
1427 0 : size_t j = 0;
1428 0 : for(size_t i = 0; i < m_GlyphItems.size(); i++ )
1429 : {
1430 0 : if( m_GlyphItems[i].maGlyphId == nDropMarker )
1431 0 : continue;
1432 :
1433 0 : if( i != j )
1434 : {
1435 0 : m_GlyphItems[j] = m_GlyphItems[i];
1436 : }
1437 0 : j += 1;
1438 : }
1439 0 : m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
1440 0 : }
1441 :
1442 : // make sure GlyphItems are sorted left to right
1443 0 : void GenericSalLayout::SortGlyphItems()
1444 : {
1445 : // move cluster components behind their cluster start (especially for RTL)
1446 : // using insertion sort because the glyph items are "almost sorted"
1447 :
1448 0 : for( GlyphVector::iterator pG = m_GlyphItems.begin(), pGEnd = m_GlyphItems.end(); pG != pGEnd; ++pG )
1449 : {
1450 : // find a cluster starting with a diacritic
1451 0 : if( !pG->IsDiacritic() )
1452 0 : continue;
1453 0 : if( !pG->IsClusterStart() )
1454 0 : continue;
1455 0 : for( GlyphVector::iterator pBaseGlyph = pG; ++pBaseGlyph != pGEnd; )
1456 : {
1457 : // find the base glyph matching to the misplaced diacritic
1458 0 : if( pBaseGlyph->IsClusterStart() )
1459 0 : break;
1460 0 : if( pBaseGlyph->IsDiacritic() )
1461 0 : continue;
1462 :
1463 : // found the matching base glyph
1464 : // => this base glyph becomes the new cluster start
1465 0 : iter_swap(pG, pBaseGlyph);
1466 :
1467 : // update glyph flags of swapped glyphitems
1468 0 : pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
1469 0 : pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
1470 : // prepare for checking next cluster
1471 0 : pG = pBaseGlyph;
1472 0 : break;
1473 : }
1474 : }
1475 0 : }
1476 :
1477 0 : MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout, const PhysicalFontFace* pBaseFont )
1478 : : SalLayout()
1479 : , mnLevel( 1 )
1480 0 : , mbInComplete( false )
1481 : {
1482 : //maFallbackRuns[0].Clear();
1483 0 : mpFallbackFonts[ 0 ] = pBaseFont;
1484 0 : mpLayouts[ 0 ] = &rBaseLayout;
1485 0 : mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
1486 0 : }
1487 :
1488 0 : void MultiSalLayout::SetInComplete(bool bInComplete)
1489 : {
1490 0 : mbInComplete = bInComplete;
1491 0 : maFallbackRuns[mnLevel-1] = ImplLayoutRuns();
1492 0 : }
1493 :
1494 0 : MultiSalLayout::~MultiSalLayout()
1495 : {
1496 0 : for( int i = 0; i < mnLevel; ++i )
1497 0 : mpLayouts[ i ]->Release();
1498 0 : }
1499 :
1500 0 : bool MultiSalLayout::AddFallback( SalLayout& rFallback,
1501 : ImplLayoutRuns& rFallbackRuns, const PhysicalFontFace* pFallbackFont )
1502 : {
1503 0 : if( mnLevel >= MAX_FALLBACK )
1504 0 : return false;
1505 :
1506 0 : mpFallbackFonts[ mnLevel ] = pFallbackFont;
1507 0 : mpLayouts[ mnLevel ] = &rFallback;
1508 0 : maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1509 0 : ++mnLevel;
1510 0 : return true;
1511 : }
1512 :
1513 0 : bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
1514 : {
1515 0 : if( mnLevel <= 1 )
1516 0 : return false;
1517 0 : if (!mbInComplete)
1518 0 : maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1519 0 : return true;
1520 : }
1521 :
1522 0 : void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1523 : {
1524 0 : SalLayout::AdjustLayout( rArgs );
1525 0 : ImplLayoutArgs aMultiArgs = rArgs;
1526 :
1527 0 : if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1528 : {
1529 : // for stretched text in a MultiSalLayout the target width needs to be
1530 : // distributed by individually adjusting its virtual character widths
1531 0 : long nTargetWidth = aMultiArgs.mnLayoutWidth;
1532 0 : nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1533 0 : aMultiArgs.mnLayoutWidth = 0;
1534 :
1535 : // we need to get the original unmodified layouts ready
1536 0 : for( int n = 0; n < mnLevel; ++n )
1537 0 : mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1538 : // then we can measure the unmodified metrics
1539 0 : int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1540 0 : sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
1541 0 : FillDXArray( pJustificationArray );
1542 : // #i17359# multilayout is not simplified yet, so calculating the
1543 : // unjustified width needs handholding; also count the number of
1544 : // stretchable virtual char widths
1545 0 : long nOrigWidth = 0;
1546 0 : int nStretchable = 0;
1547 0 : for( int i = 0; i < nCharCount; ++i )
1548 : {
1549 : // convert array from widths to sum of widths
1550 0 : nOrigWidth += pJustificationArray[i];
1551 0 : if( pJustificationArray[i] > 0 )
1552 0 : ++nStretchable;
1553 : }
1554 :
1555 : // now we are able to distribute the extra width over the virtual char widths
1556 0 : if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1557 : {
1558 0 : int nDiffWidth = nTargetWidth - nOrigWidth;
1559 0 : int nWidthSum = 0;
1560 0 : for( int i = 0; i < nCharCount; ++i )
1561 : {
1562 0 : int nJustWidth = pJustificationArray[i];
1563 0 : if( (nJustWidth > 0) && (nStretchable > 0) )
1564 : {
1565 0 : int nDeltaWidth = nDiffWidth / nStretchable;
1566 0 : nJustWidth += nDeltaWidth;
1567 0 : nDiffWidth -= nDeltaWidth;
1568 0 : --nStretchable;
1569 : }
1570 0 : nWidthSum += nJustWidth;
1571 0 : pJustificationArray[i] = nWidthSum;
1572 : }
1573 0 : if( nWidthSum != nTargetWidth )
1574 0 : pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1575 :
1576 : // the justification array is still in base level units
1577 : // => convert it to pixel units
1578 0 : if( mnUnitsPerPixel > 1 )
1579 : {
1580 0 : for( int i = 0; i < nCharCount; ++i )
1581 : {
1582 0 : sal_Int32 nVal = pJustificationArray[ i ];
1583 0 : nVal += (mnUnitsPerPixel + 1) / 2;
1584 0 : pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1585 : }
1586 : }
1587 :
1588 : // change the mpDXArray temporarilly (just for the justification)
1589 0 : aMultiArgs.mpDXArray = pJustificationArray;
1590 : }
1591 : }
1592 :
1593 : // Compute rtl flags, since in some scripts glyphs/char order can be
1594 : // reversed for a few character sequencies e.g. Myanmar
1595 0 : std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1596 0 : rArgs.ResetPos();
1597 : bool bRtl;
1598 : int nRunStart, nRunEnd;
1599 0 : while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1600 : {
1601 0 : if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1602 0 : vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1603 : }
1604 0 : rArgs.ResetPos();
1605 :
1606 : // prepare "merge sort"
1607 : int nStartOld[ MAX_FALLBACK ];
1608 : int nStartNew[ MAX_FALLBACK ];
1609 : int nCharPos[ MAX_FALLBACK ];
1610 : sal_Int32 nGlyphAdv[ MAX_FALLBACK ];
1611 0 : int nValid[ MAX_FALLBACK ] = {0};
1612 :
1613 : sal_GlyphId nDummy;
1614 0 : Point aPos;
1615 0 : int nLevel = 0, n;
1616 0 : for( n = 0; n < mnLevel; ++n )
1617 : {
1618 : // now adjust the individual components
1619 0 : if( n > 0 )
1620 : {
1621 0 : aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1622 0 : aMultiArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
1623 : }
1624 0 : mpLayouts[n]->AdjustLayout( aMultiArgs );
1625 :
1626 : // disable glyph-injection for glyph-fallback SalLayout iteration
1627 0 : mpLayouts[n]->DisableGlyphInjection( true );
1628 :
1629 : // remove unused parts of component
1630 0 : if( n > 0 )
1631 : {
1632 0 : if (mbInComplete && (n == mnLevel-1))
1633 0 : mpLayouts[n]->Simplify( true );
1634 : else
1635 0 : mpLayouts[n]->Simplify( false );
1636 : }
1637 :
1638 : // prepare merging components
1639 0 : nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1640 0 : nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1641 0 : nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] );
1642 : #ifdef MULTI_SL_DEBUG
1643 : if (nValid[nLevel]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", n, nStartOld[nLevel], nStartNew[nLevel], aPos.X(), nGlyphAdv[nLevel], nCharPos[nLevel],
1644 : rArgs.mpStr[nCharPos[nLevel]]);
1645 : #endif
1646 0 : if( (n > 0) && !nValid[ nLevel ] )
1647 : {
1648 : // an empty fallback layout can be released
1649 0 : mpLayouts[n]->Release();
1650 : }
1651 : else
1652 : {
1653 : // reshuffle used fallbacks if needed
1654 0 : if( nLevel != n )
1655 : {
1656 0 : mpLayouts[ nLevel ] = mpLayouts[ n ];
1657 0 : mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ];
1658 0 : maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1659 : }
1660 0 : ++nLevel;
1661 : }
1662 : }
1663 0 : mnLevel = nLevel;
1664 :
1665 : // prepare merge the fallback levels
1666 0 : long nXPos = 0;
1667 0 : double fUnitMul = 1.0;
1668 0 : for( n = 0; n < nLevel; ++n )
1669 0 : maFallbackRuns[n].ResetPos();
1670 : // get the next codepoint index that needs fallback
1671 0 : int nActiveCharPos = nCharPos[0];
1672 : // get the end index of the active run
1673 0 : int nLastRunEndChar = (vRtl[nActiveCharPos - mnMinCharPos])?
1674 0 : rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1675 0 : int nRunVisibleEndChar = nCharPos[0];
1676 : // merge the fallback levels
1677 0 : while( nValid[0] && (nLevel > 0))
1678 : {
1679 : // find best fallback level
1680 0 : for( n = 0; n < nLevel; ++n )
1681 0 : if( nValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1682 : // fallback level n wins when it requested no further fallback
1683 0 : break;
1684 0 : int nFBLevel = n;
1685 :
1686 0 : if( n < nLevel )
1687 : {
1688 : // use base(n==0) or fallback(n>=1) level
1689 0 : fUnitMul = mnUnitsPerPixel;
1690 0 : fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1691 0 : long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1692 0 : mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1693 : }
1694 : else
1695 : {
1696 0 : n = 0; // keep NotDef in base level
1697 0 : fUnitMul = 1.0;
1698 : }
1699 :
1700 0 : if( n > 0 )
1701 : {
1702 : // drop the NotDef glyphs in the base layout run if a fallback run exists
1703 0 : while (
1704 0 : (maFallbackRuns[ n-1 ].PosIsInRun( nCharPos[0] ) ) &&
1705 0 : (!maFallbackRuns[ n ].PosIsInAnyRun( nCharPos[0] ) )
1706 : )
1707 : {
1708 0 : mpLayouts[0]->DropGlyph( nStartOld[0] );
1709 0 : nStartOld[0] = nStartNew[0];
1710 0 : nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
1711 0 : nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
1712 : #ifdef MULTI_SL_DEBUG
1713 : if (nValid[0]) fprintf(mslLog(), "layout[0]->GetNextGlyphs %d,%d x%d a%d c%d %x\n", nStartOld[0], nStartNew[0], aPos.X(), nGlyphAdv[0], nCharPos[0], rArgs.mpStr[nCharPos[0]]);
1714 : #endif
1715 0 : if( !nValid[0] )
1716 0 : break;
1717 : }
1718 : }
1719 :
1720 : // skip to end of layout run and calculate its advance width
1721 0 : int nRunAdvance = 0;
1722 0 : bool bKeepNotDef = (nFBLevel >= nLevel);
1723 : for(;;)
1724 : {
1725 0 : nRunAdvance += nGlyphAdv[n];
1726 :
1727 : // proceed to next glyph
1728 0 : nStartOld[n] = nStartNew[n];
1729 0 : int nOrigCharPos = nCharPos[n];
1730 0 : nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
1731 0 : nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
1732 : #ifdef MULTI_SL_DEBUG
1733 : if (nValid[n]) fprintf(mslLog(), "layout[%d]->GetNextGlyphs %d,%d a%d c%d %x\n", n, nStartOld[n], nStartNew[n], nGlyphAdv[n], nCharPos[n], rArgs.mpStr[nCharPos[n]]);
1734 : #endif
1735 : // break after last glyph of active layout
1736 0 : if( !nValid[n] )
1737 : {
1738 : // performance optimization (when a fallback layout is no longer needed)
1739 0 : if( n >= nLevel-1 )
1740 0 : --nLevel;
1741 0 : break;
1742 : }
1743 :
1744 : //If the next character is one which belongs to the next level, then we
1745 : //are finished here for now, and we'll pick up after the next level has
1746 : //been processed
1747 0 : if ((n+1 < nLevel) && (nCharPos[n] != nOrigCharPos))
1748 : {
1749 0 : if (nOrigCharPos < nCharPos[n])
1750 : {
1751 0 : if (nCharPos[n+1] > nOrigCharPos && (nCharPos[n+1] < nCharPos[n]))
1752 0 : break;
1753 : }
1754 0 : else if (nOrigCharPos > nCharPos[n])
1755 : {
1756 0 : if (nCharPos[n+1] > nCharPos[n] && (nCharPos[n+1] < nOrigCharPos))
1757 0 : break;
1758 : }
1759 : }
1760 :
1761 : // break at end of layout run
1762 0 : if( n > 0 )
1763 : {
1764 : // skip until end of fallback run
1765 0 : if( !maFallbackRuns[n-1].PosIsInRun( nCharPos[n] ) )
1766 0 : break;
1767 : }
1768 : else
1769 : {
1770 : // break when a fallback is needed and available
1771 0 : bool bNeedFallback = maFallbackRuns[0].PosIsInRun( nCharPos[0] );
1772 0 : if( bNeedFallback )
1773 0 : if( !maFallbackRuns[ nLevel-1 ].PosIsInRun( nCharPos[0] ) )
1774 0 : break;
1775 : // break when change from resolved to unresolved base layout run
1776 0 : if( bKeepNotDef && !bNeedFallback )
1777 0 : { maFallbackRuns[0].NextRun(); break; }
1778 0 : bKeepNotDef = bNeedFallback;
1779 : }
1780 : // check for reordered glyphs
1781 0 : if (aMultiArgs.mpDXArray &&
1782 0 : nRunVisibleEndChar < mnEndCharPos &&
1783 0 : nRunVisibleEndChar >= mnMinCharPos &&
1784 0 : nCharPos[n] < mnEndCharPos &&
1785 0 : nCharPos[n] >= mnMinCharPos)
1786 : {
1787 0 : if (vRtl[nActiveCharPos - mnMinCharPos])
1788 : {
1789 0 : if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1790 0 : >= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1791 : {
1792 0 : nRunVisibleEndChar = nCharPos[n];
1793 : }
1794 : }
1795 0 : else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1796 0 : <= aMultiArgs.mpDXArray[nCharPos[n] - mnMinCharPos])
1797 : {
1798 0 : nRunVisibleEndChar = nCharPos[n];
1799 : }
1800 : }
1801 0 : }
1802 :
1803 : // if a justification array is available
1804 : // => use it directly to calculate the corresponding run width
1805 0 : if( aMultiArgs.mpDXArray )
1806 : {
1807 : // the run advance is the width from the first char
1808 : // in the run to the first char in the next run
1809 0 : nRunAdvance = 0;
1810 : #ifdef MULTI_SL_DEBUG
1811 : const bool bLTR = !(vRtl[nActiveCharPos - mnMinCharPos]);//(nActiveCharPos < nCharPos[0]);
1812 : int nOldRunAdv = 0;
1813 : int nDXIndex = nCharPos[0] - mnMinCharPos - bLTR;
1814 : if( nDXIndex >= 0 )
1815 : nOldRunAdv += aMultiArgs.mpDXArray[ nDXIndex ];
1816 : nDXIndex = nActiveCharPos - mnMinCharPos - bLTR;
1817 : if( nDXIndex >= 0 )
1818 : nOldRunAdv -= aMultiArgs.mpDXArray[ nDXIndex ];
1819 : if( !bLTR )
1820 : nOldRunAdv = -nOldRunAdv;
1821 : #endif
1822 0 : if (vRtl[nActiveCharPos - mnMinCharPos])
1823 : {
1824 0 : if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1825 0 : nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1826 0 : if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1827 0 : nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
1828 : #ifdef MULTI_SL_DEBUG
1829 : fprintf(mslLog(), "rtl visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar-1, nRunVisibleEndChar-1, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
1830 : #endif
1831 : }
1832 : else
1833 : {
1834 0 : if (nRunVisibleEndChar >= mnMinCharPos)
1835 0 : nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1836 0 : if (nLastRunEndChar >= mnMinCharPos)
1837 0 : nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
1838 : #ifdef MULTI_SL_DEBUG
1839 : fprintf(mslLog(), "visible %d-%d,%d-%d adv%d(%d)\n", nLastRunEndChar, nRunVisibleEndChar, nActiveCharPos - bLTR, nCharPos[0] - bLTR, nRunAdvance, nOldRunAdv);
1840 : #endif
1841 : }
1842 0 : nLastRunEndChar = nRunVisibleEndChar;
1843 0 : nRunVisibleEndChar = nCharPos[0];
1844 : // the requested width is still in pixel units
1845 : // => convert it to base level font units
1846 0 : nRunAdvance *= mnUnitsPerPixel;
1847 : }
1848 : else
1849 : {
1850 : // the measured width is still in fallback font units
1851 : // => convert it to base level font units
1852 0 : if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1853 0 : nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
1854 : }
1855 :
1856 : // calculate new x position (in base level units)
1857 0 : nXPos += nRunAdvance;
1858 :
1859 : // prepare for next fallback run
1860 0 : nActiveCharPos = nCharPos[0];
1861 : // it essential that the runs don't get ahead of themselves and in the
1862 : // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1863 : // have already been reached on the base level
1864 0 : for( int i = nFBLevel; --i >= 0;)
1865 : {
1866 0 : if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1867 : {
1868 0 : if (bRtl)
1869 : {
1870 0 : if (nRunStart > nActiveCharPos)
1871 0 : maFallbackRuns[i].NextRun();
1872 : }
1873 : else
1874 : {
1875 0 : if (nRunEnd <= nActiveCharPos)
1876 0 : maFallbackRuns[i].NextRun();
1877 : }
1878 : }
1879 : }
1880 : }
1881 :
1882 0 : mpLayouts[0]->Simplify( true );
1883 :
1884 : // reenable glyph-injection
1885 0 : for( n = 0; n < mnLevel; ++n )
1886 0 : mpLayouts[n]->DisableGlyphInjection( false );
1887 0 : }
1888 :
1889 0 : void MultiSalLayout::InitFont() const
1890 : {
1891 0 : if( mnLevel > 0 )
1892 0 : mpLayouts[0]->InitFont();
1893 0 : }
1894 :
1895 0 : void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1896 : {
1897 0 : for( int i = mnLevel; --i >= 0; )
1898 : {
1899 0 : SalLayout& rLayout = *mpLayouts[ i ];
1900 0 : rLayout.DrawBase() += maDrawBase;
1901 0 : rLayout.DrawOffset() += maDrawOffset;
1902 0 : rLayout.InitFont();
1903 0 : rLayout.DrawText( rGraphics );
1904 0 : rLayout.DrawOffset() -= maDrawOffset;
1905 0 : rLayout.DrawBase() -= maDrawBase;
1906 : }
1907 : // NOTE: now the baselevel font is active again
1908 0 : }
1909 :
1910 0 : sal_Int32 MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
1911 : {
1912 0 : if( mnLevel <= 0 )
1913 0 : return -1;
1914 0 : if( mnLevel == 1 )
1915 0 : return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1916 :
1917 0 : int nCharCount = mnEndCharPos - mnMinCharPos;
1918 0 : sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
1919 0 : mpLayouts[0]->FillDXArray( pCharWidths );
1920 :
1921 0 : for( int n = 1; n < mnLevel; ++n )
1922 : {
1923 0 : SalLayout& rLayout = *mpLayouts[ n ];
1924 0 : rLayout.FillDXArray( pCharWidths + nCharCount );
1925 0 : double fUnitMul = mnUnitsPerPixel;
1926 0 : fUnitMul /= rLayout.GetUnitsPerPixel();
1927 0 : for( int i = 0; i < nCharCount; ++i )
1928 : {
1929 0 : long w = pCharWidths[ i + nCharCount ];
1930 0 : w = static_cast<long>(w*fUnitMul + 0.5);
1931 0 : pCharWidths[ i ] += w;
1932 : }
1933 : }
1934 :
1935 0 : long nWidth = 0;
1936 0 : for( int i = 0; i < nCharCount; ++i )
1937 : {
1938 0 : nWidth += pCharWidths[ i ] * nFactor;
1939 0 : if( nWidth > nMaxWidth )
1940 0 : return (i + mnMinCharPos);
1941 0 : nWidth += nCharExtra;
1942 : }
1943 :
1944 0 : return -1;
1945 : }
1946 :
1947 0 : long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
1948 : {
1949 0 : long nMaxWidth = 0;
1950 :
1951 : // prepare merging of fallback levels
1952 0 : sal_Int32* pTempWidths = NULL;
1953 0 : const int nCharCount = mnEndCharPos - mnMinCharPos;
1954 0 : if( pCharWidths )
1955 : {
1956 0 : for( int i = 0; i < nCharCount; ++i )
1957 0 : pCharWidths[i] = 0;
1958 0 : pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
1959 : }
1960 :
1961 0 : for( int n = mnLevel; --n >= 0; )
1962 : {
1963 : // query every fallback level
1964 0 : long nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths );
1965 0 : if( !nTextWidth )
1966 0 : continue;
1967 : // merge results from current level
1968 0 : double fUnitMul = mnUnitsPerPixel;
1969 0 : fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1970 0 : nTextWidth = static_cast<long>(nTextWidth * fUnitMul + 0.5);
1971 0 : if( nMaxWidth < nTextWidth )
1972 0 : nMaxWidth = nTextWidth;
1973 0 : if( !pCharWidths )
1974 0 : continue;
1975 : // calculate virtual char widths using most probable fallback layout
1976 0 : for( int i = 0; i < nCharCount; ++i )
1977 : {
1978 : // #i17359# restriction:
1979 : // one char cannot be resolved from different fallbacks
1980 0 : if( pCharWidths[i] != 0 )
1981 0 : continue;
1982 0 : long nCharWidth = pTempWidths[i];
1983 0 : if( !nCharWidth )
1984 0 : continue;
1985 0 : nCharWidth = static_cast<long>(nCharWidth * fUnitMul + 0.5);
1986 0 : pCharWidths[i] = nCharWidth;
1987 : }
1988 : }
1989 :
1990 0 : return nMaxWidth;
1991 : }
1992 :
1993 0 : void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
1994 : {
1995 0 : SalLayout& rLayout = *mpLayouts[ 0 ];
1996 0 : rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
1997 :
1998 0 : if( mnLevel > 1 )
1999 : {
2000 0 : sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
2001 0 : for( int n = 1; n < mnLevel; ++n )
2002 : {
2003 0 : mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
2004 0 : double fUnitMul = mnUnitsPerPixel;
2005 0 : fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
2006 0 : for( int i = 0; i < nMaxIndex; ++i )
2007 0 : if( pTempPos[i] >= 0 )
2008 : {
2009 0 : long w = pTempPos[i];
2010 0 : w = static_cast<long>(w*fUnitMul + 0.5);
2011 0 : pCaretXArray[i] = w;
2012 : }
2013 : }
2014 : }
2015 0 : }
2016 :
2017 0 : int MultiSalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdxAry, Point& rPos,
2018 : int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry,
2019 : const PhysicalFontFace** pFallbackFonts ) const
2020 : {
2021 : // for multi-level fallback only single glyphs should be used
2022 0 : if( mnLevel > 1 && nLen > 1 )
2023 0 : nLen = 1;
2024 :
2025 : // NOTE: nStart is tagged with current font index
2026 0 : int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
2027 0 : nStart &= ~GF_FONTMASK;
2028 0 : for(; nLevel < mnLevel; ++nLevel, nStart=0 )
2029 : {
2030 0 : SalLayout& rLayout = *mpLayouts[ nLevel ];
2031 0 : rLayout.InitFont();
2032 : int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
2033 0 : nStart, pGlyphAdvAry, pCharPosAry );
2034 0 : if( nRetVal )
2035 : {
2036 0 : int nFontTag = nLevel << GF_FONTSHIFT;
2037 0 : nStart |= nFontTag;
2038 0 : double fUnitMul = mnUnitsPerPixel;
2039 0 : fUnitMul /= mpLayouts[nLevel]->GetUnitsPerPixel();
2040 0 : for( int i = 0; i < nRetVal; ++i )
2041 : {
2042 0 : if( pGlyphAdvAry )
2043 : {
2044 0 : long w = pGlyphAdvAry[i];
2045 0 : w = static_cast<long>(w * fUnitMul + 0.5);
2046 0 : pGlyphAdvAry[i] = w;
2047 : }
2048 0 : pGlyphIdxAry[ i ] |= nFontTag;
2049 0 : if( pFallbackFonts )
2050 : {
2051 0 : pFallbackFonts[ i ] = mpFallbackFonts[ nLevel ];
2052 : }
2053 : }
2054 0 : rPos += maDrawBase;
2055 0 : rPos += maDrawOffset;
2056 0 : return nRetVal;
2057 : }
2058 : }
2059 :
2060 : // #111016# reset to base level font when done
2061 0 : mpLayouts[0]->InitFont();
2062 0 : return 0;
2063 : }
2064 :
2065 0 : bool MultiSalLayout::GetOutline( SalGraphics& rGraphics,
2066 : ::basegfx::B2DPolyPolygonVector& rPPV ) const
2067 : {
2068 0 : bool bRet = false;
2069 :
2070 0 : for( int i = mnLevel; --i >= 0; )
2071 : {
2072 0 : SalLayout& rLayout = *mpLayouts[ i ];
2073 0 : rLayout.DrawBase() = maDrawBase;
2074 0 : rLayout.DrawOffset() += maDrawOffset;
2075 0 : rLayout.InitFont();
2076 0 : bRet |= rLayout.GetOutline( rGraphics, rPPV );
2077 0 : rLayout.DrawOffset() -= maDrawOffset;
2078 : }
2079 :
2080 0 : return bRet;
2081 3 : }
2082 :
2083 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|