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