LibreOffice Module vcl (master)  1
sallayout.cxx
Go to the documentation of this file.
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 #include <sal/log.hxx>
25 
26 #include <cstdio>
27 
28 #include <math.h>
29 
30 #include <salgdi.hxx>
31 #include <sallayout.hxx>
34 
35 #include <i18nlangtag/lang.h>
36 
37 #include <vcl/svapp.hxx>
38 
39 #include <unicode/ubidi.h>
40 #include <unicode/uchar.h>
41 
42 #include <algorithm>
43 #include <memory>
44 
45 #include <impglyphitem.hxx>
46 
47 // Glyph Flags
48 #define GF_FONTMASK 0xF0000000
49 #define GF_FONTSHIFT 28
50 
51 
52 std::ostream &operator <<(std::ostream& s, ImplLayoutArgs const &rArgs)
53 {
54 #ifndef SAL_LOG_INFO
55  (void) rArgs;
56 #else
57  s << "ImplLayoutArgs{";
58 
59  s << "Flags=";
60  if (rArgs.mnFlags == SalLayoutFlags::NONE)
61  s << 0;
62  else {
63  bool need_or = false;
64  s << "{";
65 #define TEST(x) if (rArgs.mnFlags & SalLayoutFlags::x) { if (need_or) s << "|"; s << #x; need_or = true; }
66  TEST(BiDiRtl);
71  TEST(Vertical);
74 #undef TEST
75  s << "}";
76  }
77 
78  const int nLength = rArgs.mrStr.getLength();
79 
80  s << ",Length=" << nLength;
81  s << ",MinCharPos=" << rArgs.mnMinCharPos;
82  s << ",EndCharPos=" << rArgs.mnEndCharPos;
83 
84  s << ",Str=\"";
85  int lim = nLength;
86  if (lim > 10)
87  lim = 7;
88  for (int i = 0; i < lim; i++) {
89  if (rArgs.mrStr[i] == '\n')
90  s << "\\n";
91  else if (rArgs.mrStr[i] < ' ' || (rArgs.mrStr[i] >= 0x7F && rArgs.mrStr[i] <= 0xFF))
92  s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec;
93  else if (rArgs.mrStr[i] < 0x7F)
94  s << static_cast<char>(rArgs.mrStr[i]);
95  else
96  s << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(rArgs.mrStr[i]) << std::setfill(' ') << std::setw(1) << std::dec;
97  }
98  if (nLength > lim)
99  s << "...";
100  s << "\"";
101 
102  s << ",DXArray=";
103  if (rArgs.mpDXArray) {
104  s << "[";
105  int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
106  lim = count;
107  if (lim > 10)
108  lim = 7;
109  for (int i = 0; i < lim; i++) {
110  s << rArgs.mpDXArray[i];
111  if (i < lim-1)
112  s << ",";
113  }
114  if (count > lim) {
115  if (count > lim + 1)
116  s << "...";
117  s << rArgs.mpDXArray[count-1];
118  }
119  s << "]";
120  } else
121  s << "NULL";
122 
123  s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
124 
125  s << "}";
126 
127 #endif
128  return s;
129 }
130 
132 {
133  nChar = u_charMirror( nChar );
134  return nChar;
135 }
136 
138 {
139  // currently only conversion from ASCII digits is interesting
140  if( (nChar < '0') || ('9' < nChar) )
141  return nChar;
142 
143  int nOffset;
144  // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
145  // CAVEAT! To some like Mongolian MS assigned the same primary language
146  // although the script type is different!
147  LanguageType pri = primary(eLang);
149  nOffset = 0x0660 - '0'; // arabic-indic digits
150  else if ( pri.anyOf(
153  primary(LANGUAGE_PUNJABI), //???
155  nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
156  else if ( pri == primary(LANGUAGE_BENGALI) )
157  nOffset = 0x09E6 - '0'; // bengali
158  else if ( pri == primary(LANGUAGE_HINDI) )
159  nOffset = 0x0966 - '0'; // devanagari
160  else if ( pri.anyOf(
163  // TODO case:
164  nOffset = 0x1369 - '0'; // ethiopic
165  else if ( pri == primary(LANGUAGE_GUJARATI) )
166  nOffset = 0x0AE6 - '0'; // gujarati
167 #ifdef LANGUAGE_GURMUKHI // TODO case:
168  else if ( pri == primary(LANGUAGE_GURMUKHI) )
169  nOffset = 0x0A66 - '0'; // gurmukhi
170 #endif
171  else if ( pri == primary(LANGUAGE_KANNADA) )
172  nOffset = 0x0CE6 - '0'; // kannada
173  else if ( pri == primary(LANGUAGE_KHMER))
174  nOffset = 0x17E0 - '0'; // khmer
175  else if ( pri == primary(LANGUAGE_LAO) )
176  nOffset = 0x0ED0 - '0'; // lao
177  else if ( pri == primary(LANGUAGE_MALAYALAM) )
178  nOffset = 0x0D66 - '0'; // malayalam
179  else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_LSO))
180  {
181  if (eLang.anyOf(
185  nOffset = 0x1810 - '0'; // mongolian
186  else
187  nOffset = 0; // mongolian cyrillic
188  }
189  else if ( pri == primary(LANGUAGE_BURMESE) )
190  nOffset = 0x1040 - '0'; // myanmar
191  else if ( pri == primary(LANGUAGE_ODIA) )
192  nOffset = 0x0B66 - '0'; // odia
193  else if ( pri == primary(LANGUAGE_TAMIL) )
194  nOffset = 0x0BE7 - '0'; // tamil
195  else if ( pri == primary(LANGUAGE_TELUGU) )
196  nOffset = 0x0C66 - '0'; // telugu
197  else if ( pri == primary(LANGUAGE_THAI) )
198  nOffset = 0x0E50 - '0'; // thai
199  else if ( pri == primary(LANGUAGE_TIBETAN) )
200  nOffset = 0x0F20 - '0'; // tibetan
201  else
202  {
203  nOffset = 0;
204  }
205 
206  nChar += nOffset;
207  return nChar;
208 }
209 
210 static bool IsControlChar( sal_UCS4 cChar )
211 {
212  // C0 control characters
213  if( (0x0001 <= cChar) && (cChar <= 0x001F) )
214  return true;
215  // formatting characters
216  if( (0x200E <= cChar) && (cChar <= 0x200F) )
217  return true;
218  if( (0x2028 <= cChar) && (cChar <= 0x202E) )
219  return true;
220  // deprecated formatting characters
221  if( (0x206A <= cChar) && (cChar <= 0x206F) )
222  return true;
223  if( 0x2060 == cChar )
224  return true;
225  // byte order markers and invalid unicode
226  if( (cChar == 0xFEFF) || (cChar == 0xFFFE) || (cChar == 0xFFFF) )
227  return true;
228  return false;
229 }
230 
231 void ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
232 {
233  // check if charpos could extend current run
234  int nIndex = maRuns.size();
235  if( nIndex >= 2 )
236  {
237  int nRunPos0 = maRuns[ nIndex-2 ];
238  int nRunPos1 = maRuns[ nIndex-1 ];
239  if( ((nCharPos + int(bRTL)) == nRunPos1) && ((nRunPos0 > nRunPos1) == bRTL) )
240  {
241  // extend current run by new charpos
242  maRuns[ nIndex-1 ] = nCharPos + int(!bRTL);
243  return;
244  }
245  // ignore new charpos when it is in current run
246  if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
247  return;
248  if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
249  return;
250  }
251 
252  // else append a new run consisting of the new charpos
253  maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
254  maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
255 }
256 
257 void ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
258 {
259  if( nCharPos0 == nCharPos1 )
260  return;
261 
262  // swap if needed
263  if( bRTL == (nCharPos0 < nCharPos1) )
264  {
265  int nTemp = nCharPos0;
266  nCharPos0 = nCharPos1;
267  nCharPos1 = nTemp;
268  }
269 
270  if (maRuns.size() >= 2 && nCharPos0 == maRuns[maRuns.size() - 2] && nCharPos1 == maRuns[maRuns.size() - 1])
271  {
272  //this run is the same as the last
273  return;
274  }
275 
276  // append new run
277  maRuns.push_back( nCharPos0 );
278  maRuns.push_back( nCharPos1 );
279 }
280 
281 bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
282 {
283  if( mnRunIndex >= static_cast<int>(maRuns.size()) )
284  return false;
285 
286  int nMinCharPos = maRuns[ mnRunIndex+0 ];
287  int nEndCharPos = maRuns[ mnRunIndex+1 ];
288  if( nMinCharPos > nEndCharPos ) // reversed in RTL case
289  {
290  int nTemp = nMinCharPos;
291  nMinCharPos = nEndCharPos;
292  nEndCharPos = nTemp;
293  }
294 
295  if( nCharPos < nMinCharPos )
296  return false;
297  if( nCharPos >= nEndCharPos )
298  return false;
299  return true;
300 }
301 
302 bool ImplLayoutRuns::PosIsInAnyRun( int nCharPos ) const
303 {
304  bool bRet = false;
305  int nRunIndex = mnRunIndex;
306 
307  ImplLayoutRuns *pThis = const_cast<ImplLayoutRuns*>(this);
308 
309  pThis->ResetPos();
310 
311  for (size_t i = 0; i < maRuns.size(); i+=2)
312  {
313  bRet = PosIsInRun( nCharPos );
314  if( bRet )
315  break;
316  pThis->NextRun();
317  }
318 
319  pThis->mnRunIndex = nRunIndex;
320  return bRet;
321 }
322 
323 bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
324 {
325  // negative nCharPos => reset to first run
326  if( *nCharPos < 0 )
327  mnRunIndex = 0;
328 
329  // return false when all runs completed
330  if( mnRunIndex >= static_cast<int>(maRuns.size()) )
331  return false;
332 
333  int nRunPos0 = maRuns[ mnRunIndex+0 ];
334  int nRunPos1 = maRuns[ mnRunIndex+1 ];
335  *bRightToLeft = (nRunPos0 > nRunPos1);
336 
337  if( *nCharPos < 0 )
338  {
339  // get first valid nCharPos in run
340  *nCharPos = nRunPos0;
341  }
342  else
343  {
344  // advance to next nCharPos for LTR case
345  if( !*bRightToLeft )
346  ++(*nCharPos);
347 
348  // advance to next run if current run is completed
349  if( *nCharPos == nRunPos1 )
350  {
351  if( (mnRunIndex += 2) >= static_cast<int>(maRuns.size()) )
352  return false;
353  nRunPos0 = maRuns[ mnRunIndex+0 ];
354  nRunPos1 = maRuns[ mnRunIndex+1 ];
355  *bRightToLeft = (nRunPos0 > nRunPos1);
356  *nCharPos = nRunPos0;
357  }
358  }
359 
360  // advance to next nCharPos for RTL case
361  if( *bRightToLeft )
362  --(*nCharPos);
363 
364  return true;
365 }
366 
367 bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
368 {
369  if( mnRunIndex >= static_cast<int>(maRuns.size()) )
370  return false;
371 
372  int nRunPos0 = maRuns[ mnRunIndex+0 ];
373  int nRunPos1 = maRuns[ mnRunIndex+1 ];
374  *bRightToLeft = (nRunPos1 < nRunPos0) ;
375  if( !*bRightToLeft )
376  {
377  *nMinRunPos = nRunPos0;
378  *nEndRunPos = nRunPos1;
379  }
380  else
381  {
382  *nMinRunPos = nRunPos1;
383  *nEndRunPos = nRunPos0;
384  }
385  return true;
386 }
387 
388 ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr,
389  int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, const LanguageTag& rLanguageTag,
390  vcl::TextLayoutCache const*const pLayoutCache)
391 :
392  maLanguageTag( rLanguageTag ),
393  mnFlags( nFlags ),
394  mrStr( rStr ),
395  mnMinCharPos( nMinCharPos ),
396  mnEndCharPos( nEndCharPos ),
397  m_pTextLayoutCache(pLayoutCache),
398  mpDXArray( nullptr ),
399  mnLayoutWidth( 0 ),
400  mnOrientation( 0 )
401 {
403  {
404  // handle strong BiDi mode
405 
406  // do not bother to BiDi analyze strong LTR/RTL
407  // TODO: can we assume these strings do not have unicode control chars?
408  // if not remove the control characters from the runs
409  bool bRTL(mnFlags & SalLayoutFlags::BiDiRtl);
410  AddRun( mnMinCharPos, mnEndCharPos, bRTL );
411  }
412  else
413  {
414  // handle weak BiDi mode
415  UBiDiLevel nLevel = (mnFlags & SalLayoutFlags::BiDiRtl)? 1 : 0;
416 
417  // prepare substring for BiDi analysis
418  // TODO: reuse allocated pParaBidi
419  UErrorCode rcI18n = U_ZERO_ERROR;
420  const int nLength = mrStr.getLength();
421  UBiDi* pParaBidi = ubidi_openSized(nLength, 0, &rcI18n);
422  if( !pParaBidi )
423  return;
424  ubidi_setPara(pParaBidi, reinterpret_cast<const UChar *>(mrStr.getStr()), nLength, nLevel, nullptr, &rcI18n);
425 
426  UBiDi* pLineBidi = pParaBidi;
427  int nSubLength = mnEndCharPos - mnMinCharPos;
428  if (nSubLength != nLength)
429  {
430  pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
431  ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
432  }
433 
434  // run BiDi algorithm
435  const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
436  //maRuns.resize( 2 * nRunCount );
437  for( int i = 0; i < nRunCount; ++i )
438  {
439  int32_t nMinPos, nRunLength;
440  const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nRunLength );
441  const int nPos0 = nMinPos + mnMinCharPos;
442  const int nPos1 = nPos0 + nRunLength;
443 
444  const bool bRTL = (nDir == UBIDI_RTL);
445  AddRun( nPos0, nPos1, bRTL );
446  }
447 
448  // cleanup BiDi engine
449  if( pLineBidi != pParaBidi )
450  ubidi_close( pLineBidi );
451  ubidi_close( pParaBidi );
452  }
453 
454  // prepare calls to GetNextPos/GetNextRun
455  maRuns.ResetPos();
456 }
457 
458 // add a run after splitting it up to get rid of control chars
459 void ImplLayoutArgs::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
460 {
461  SAL_WARN_IF( nCharPos0 > nCharPos1, "vcl", "ImplLayoutArgs::AddRun() nCharPos0>=nCharPos1" );
462 
463  // remove control characters from runs by splitting them up
464  if( !bRTL )
465  {
466  for( int i = nCharPos0; i < nCharPos1; ++i )
467  if( IsControlChar( mrStr[i] ) )
468  {
469  // add run until control char
470  maRuns.AddRun( nCharPos0, i, bRTL );
471  nCharPos0 = i + 1;
472  }
473  }
474  else
475  {
476  for( int i = nCharPos1; --i >= nCharPos0; )
477  if( IsControlChar( mrStr[i] ) )
478  {
479  // add run until control char
480  maRuns.AddRun( i+1, nCharPos1, bRTL );
481  nCharPos1 = i;
482  }
483  }
484 
485  // add remainder of run
486  maRuns.AddRun( nCharPos0, nCharPos1, bRTL );
487 }
488 
490 {
491  // short circuit if no fallback is needed
492  if( maFallbackRuns.IsEmpty() )
493  {
494  maRuns.Clear();
495  return false;
496  }
497 
498  // convert the fallback requests to layout requests
499  bool bRTL;
500  int nMin, nEnd;
501 
502  // get the individual fallback requests
503  std::vector<int> aPosVector;
504  aPosVector.reserve(mrStr.getLength());
506  for(; maFallbackRuns.GetRun( &nMin, &nEnd, &bRTL ); maFallbackRuns.NextRun() )
507  for( int i = nMin; i < nEnd; ++i )
508  aPosVector.push_back( i );
510 
511  // sort the individual fallback requests
512  std::sort( aPosVector.begin(), aPosVector.end() );
513 
514  // adjust fallback runs to have the same order and limits of the original runs
515  ImplLayoutRuns aNewRuns;
516  maRuns.ResetPos();
517  for(; maRuns.GetRun( &nMin, &nEnd, &bRTL ); maRuns.NextRun() )
518  {
519  if( !bRTL) {
520  auto it = std::lower_bound( aPosVector.begin(), aPosVector.end(), nMin );
521  for(; (it != aPosVector.end()) && (*it < nEnd); ++it )
522  aNewRuns.AddPos( *it, bRTL );
523  } else {
524  auto it = std::upper_bound( aPosVector.begin(), aPosVector.end(), nEnd );
525  while( (it != aPosVector.begin()) && (*--it >= nMin) )
526  aNewRuns.AddPos( *it, bRTL );
527  }
528  }
529 
530  maRuns = aNewRuns; // TODO: use vector<>::swap()
531  maRuns.ResetPos();
532  return true;
533 }
534 
535 bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
536 {
537  bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
538  maRuns.NextRun();
539  return bValid;
540 }
541 
543 : mnMinCharPos( -1 ),
544  mnEndCharPos( -1 ),
545  mnUnitsPerPixel( 1 ),
546  mnOrientation( 0 ),
547  maDrawOffset( 0, 0 )
548 {}
549 
551 {}
552 
554 {
555  mnMinCharPos = rArgs.mnMinCharPos;
556  mnEndCharPos = rArgs.mnEndCharPos;
558 }
559 
560 Point SalLayout::GetDrawPosition( const Point& rRelative ) const
561 {
562  Point aPos = maDrawBase;
563  Point aOfs = rRelative + maDrawOffset;
564 
565  if( mnOrientation == 0 )
566  aPos += aOfs;
567  else
568  {
569  // cache trigonometric results
570  static int nOldOrientation = 0;
571  static double fCos = 1.0, fSin = 0.0;
572  if( nOldOrientation != mnOrientation )
573  {
574  nOldOrientation = mnOrientation;
575  double fRad = mnOrientation * (M_PI / 1800.0);
576  fCos = cos( fRad );
577  fSin = sin( fRad );
578  }
579 
580  double fX = aOfs.X();
581  double fY = aOfs.Y();
582  long nX = static_cast<long>( +fCos * fX + fSin * fY );
583  long nY = static_cast<long>( +fCos * fY - fSin * fX );
584  aPos += Point( nX, nY );
585  }
586 
587  return aPos;
588 }
589 
591 {
592  bool bAllOk = true;
593  bool bOneOk = false;
594 
595  basegfx::B2DPolyPolygon aGlyphOutline;
596 
597  Point aPos;
598  const GlyphItem* pGlyph;
599  int nStart = 0;
600  while (GetNextGlyph(&pGlyph, aPos, nStart))
601  {
602  // get outline of individual glyph, ignoring "empty" glyphs
603  bool bSuccess = pGlyph->GetGlyphOutline(aGlyphOutline);
604  bAllOk &= bSuccess;
605  bOneOk |= bSuccess;
606  // only add non-empty outlines
607  if( bSuccess && (aGlyphOutline.count() > 0) )
608  {
609  if( aPos.X() || aPos.Y() )
610  {
611  aGlyphOutline.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
612  }
613 
614  // insert outline at correct position
615  rVector.push_back( aGlyphOutline );
616  }
617  }
618 
619  return (bAllOk && bOneOk);
620 }
621 
623 {
624  bool bRet = false;
625  rRect.SetEmpty();
626 
627  tools::Rectangle aRectangle;
628 
629  Point aPos;
630  const GlyphItem* pGlyph;
631  int nStart = 0;
632  while (GetNextGlyph(&pGlyph, aPos, nStart))
633  {
634  // get bounding rectangle of individual glyph
635  if (pGlyph->GetGlyphBoundRect(aRectangle))
636  {
637  // merge rectangle
638  aRectangle += aPos;
639  if (rRect.IsEmpty())
640  rRect = aRectangle;
641  else
642  rRect.Union(aRectangle);
643  bRet = true;
644  }
645  }
646 
647  return bRet;
648 }
649 
651 {
652  if (pCharWidths)
653  GetCharWidths(pCharWidths);
654 
655  return GetTextWidth();
656 }
657 
658 // the text width is the maximum logical extent of all glyphs
660 {
661  if (!m_GlyphItems.IsValid())
662  return 0;
663 
664  // initialize the extent
665  DeviceCoordinate nMinPos = 0;
666  DeviceCoordinate nMaxPos = 0;
667 
668  for (auto const& aGlyphItem : *m_GlyphItems.Impl())
669  {
670  // update the text extent with the glyph extent
671  DeviceCoordinate nXPos = aGlyphItem.m_aLinearPos.getX();
672  if( nMinPos > nXPos )
673  nMinPos = nXPos;
674  nXPos += aGlyphItem.m_nNewWidth - aGlyphItem.xOffset();
675  if( nMaxPos < nXPos )
676  nMaxPos = nXPos;
677  }
678 
679  DeviceCoordinate nWidth = nMaxPos - nMinPos;
680  return nWidth;
681 }
682 
684 {
685  nNewWidth *= mnUnitsPerPixel;
686  DeviceCoordinate nOldWidth = GetTextWidth();
687  if( !nOldWidth || nNewWidth==nOldWidth )
688  return;
689 
690  if (!m_GlyphItems.IsValid())
691  {
692  return;
693  }
694  // find rightmost glyph, it won't get stretched
695  std::vector<GlyphItem>::iterator pGlyphIterRight = m_GlyphItems.Impl()->begin();
696  pGlyphIterRight += m_GlyphItems.Impl()->size() - 1;
697  std::vector<GlyphItem>::iterator pGlyphIter;
698  // count stretchable glyphs
699  int nStretchable = 0;
700  int nMaxGlyphWidth = 0;
701  for(pGlyphIter = m_GlyphItems.Impl()->begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter)
702  {
703  if( !pGlyphIter->IsDiacritic() )
704  ++nStretchable;
705  if (nMaxGlyphWidth < pGlyphIter->origWidth())
706  nMaxGlyphWidth = pGlyphIter->origWidth();
707  }
708 
709  // move rightmost glyph to requested position
710  nOldWidth -= pGlyphIterRight->origWidth();
711  if( nOldWidth <= 0 )
712  return;
713  if( nNewWidth < nMaxGlyphWidth)
714  nNewWidth = nMaxGlyphWidth;
715  nNewWidth -= pGlyphIterRight->origWidth();
716  pGlyphIterRight->m_aLinearPos.setX( nNewWidth );
717 
718  // justify glyph widths and positions
719  int nDiffWidth = nNewWidth - nOldWidth;
720  if( nDiffWidth >= 0) // expanded case
721  {
722  // expand width by distributing space between glyphs evenly
723  int nDeltaSum = 0;
724  for( pGlyphIter = m_GlyphItems.Impl()->begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
725  {
726  // move glyph to justified position
727  pGlyphIter->m_aLinearPos.AdjustX(nDeltaSum );
728 
729  // do not stretch non-stretchable glyphs
730  if( pGlyphIter->IsDiacritic() || (nStretchable <= 0) )
731  continue;
732 
733  // distribute extra space equally to stretchable glyphs
734  int nDeltaWidth = nDiffWidth / nStretchable--;
735  nDiffWidth -= nDeltaWidth;
736  pGlyphIter->m_nNewWidth += nDeltaWidth;
737  nDeltaSum += nDeltaWidth;
738  }
739  }
740  else // condensed case
741  {
742  // squeeze width by moving glyphs proportionally
743  double fSqueeze = static_cast<double>(nNewWidth) / nOldWidth;
744  if(m_GlyphItems.Impl()->size() > 1)
745  {
746  for( pGlyphIter = m_GlyphItems.Impl()->begin(); ++pGlyphIter != pGlyphIterRight;)
747  {
748  int nX = pGlyphIter->m_aLinearPos.getX();
749  nX = static_cast<int>(nX * fSqueeze);
750  pGlyphIter->m_aLinearPos.setX( nX );
751  }
752  }
753  // adjust glyph widths to new positions
754  for( pGlyphIter = m_GlyphItems.Impl()->begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
755  pGlyphIter->m_nNewWidth = pGlyphIter[1].m_aLinearPos.getX() - pGlyphIter[0].m_aLinearPos.getX();
756  }
757 }
758 
759 // returns asian kerning values in quarter of character width units
760 // to enable automatic halfwidth substitution for fullwidth punctuation
761 // return value is negative for l, positive for r, zero for neutral
762 // TODO: handle vertical layout as proposed in commit 43bf2ad49c2b3989bbbe893e4fee2e032a3920f5?
763 static int lcl_CalcAsianKerning(sal_UCS4 c, bool bLeft)
764 {
765  // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
766  static const signed char nTable[0x30] =
767  {
768  0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
769  +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
770  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
771  };
772 
773  int nResult = 0;
774  if( (c >= 0x3000) && (c < 0x3030) )
775  nResult = nTable[ c - 0x3000 ];
776  else switch( c )
777  {
778  case 0x30FB:
779  nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
780  break;
781  case 0x2019: case 0x201D:
782  case 0xFF01: case 0xFF09: case 0xFF0C:
783  case 0xFF1A: case 0xFF1B:
784  nResult = -2;
785  break;
786  case 0x2018: case 0x201C:
787  case 0xFF08:
788  nResult = +2;
789  break;
790  default:
791  break;
792  }
793 
794  return nResult;
795 }
796 
798 {
799  return (0x3000 == (cp & 0xFF00)) || (0xFF00 == (cp & 0xFF00)) || (0x2010 == (cp & 0xFFF0));
800 }
801 
802 void GenericSalLayout::ApplyAsianKerning(const OUString& rStr)
803 {
804  const int nLength = rStr.getLength();
805  long nOffset = 0;
806 
807  for (std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.Impl()->begin(),
808  pGlyphIterEnd = m_GlyphItems.Impl()->end();
809  pGlyphIter != pGlyphIterEnd; ++pGlyphIter)
810  {
811  const int n = pGlyphIter->charPos();
812  if (n < nLength - 1)
813  {
814  // ignore code ranges that are not affected by asian punctuation compression
815  const sal_Unicode cCurrent = rStr[n];
816  if (!lcl_CanApplyAsianKerning(cCurrent))
817  continue;
818  const sal_Unicode cNext = rStr[n + 1];
819  if (!lcl_CanApplyAsianKerning(cNext))
820  continue;
821 
822  // calculate compression values
823  const int nKernCurrent = +lcl_CalcAsianKerning(cCurrent, true);
824  if (nKernCurrent == 0)
825  continue;
826  const int nKernNext = -lcl_CalcAsianKerning(cNext, false);
827  if (nKernNext == 0)
828  continue;
829 
830  // apply punctuation compression to logical glyph widths
831  int nDelta = (nKernCurrent < nKernNext) ? nKernCurrent : nKernNext;
832  if (nDelta < 0)
833  {
834  nDelta = (nDelta * pGlyphIter->origWidth() + 2) / 4;
835  if( pGlyphIter+1 == pGlyphIterEnd )
836  pGlyphIter->m_nNewWidth += nDelta;
837  nOffset += nDelta;
838  }
839  }
840 
841  // adjust the glyph positions to the new glyph widths
842  if( pGlyphIter+1 != pGlyphIterEnd )
843  pGlyphIter->m_aLinearPos.AdjustX(nOffset);
844  }
845 }
846 
847 void GenericSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
848 {
849  // initialize result array
850  for (int i = 0; i < nMaxIndex; ++i)
851  pCaretXArray[i] = -1;
852 
853  // calculate caret positions using glyph array
854  for (auto const& aGlyphItem : *m_GlyphItems.Impl())
855  {
856  long nXPos = aGlyphItem.m_aLinearPos.getX();
857  long nXRight = nXPos + aGlyphItem.origWidth();
858  int n = aGlyphItem.charPos();
859  int nCurrIdx = 2 * (n - mnMinCharPos);
860  // tdf#86399 if this is not the start of a cluster, don't overwrite the caret bounds of the cluster start
861  if (aGlyphItem.IsInCluster() && pCaretXArray[nCurrIdx] != -1)
862  continue;
863  if (!aGlyphItem.IsRTLGlyph() )
864  {
865  // normal positions for LTR case
866  pCaretXArray[ nCurrIdx ] = nXPos;
867  pCaretXArray[ nCurrIdx+1 ] = nXRight;
868  }
869  else
870  {
871  // reverse positions for RTL case
872  pCaretXArray[ nCurrIdx ] = nXRight;
873  pCaretXArray[ nCurrIdx+1 ] = nXPos;
874  }
875  }
876 }
877 
878 sal_Int32 GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
879 {
880  int nCharCapacity = mnEndCharPos - mnMinCharPos;
881  std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[nCharCapacity]);
882  GetCharWidths(pCharWidths.get());
883 
884  DeviceCoordinate nWidth = 0;
885  for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
886  {
887  nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
888  if( nWidth > nMaxWidth )
889  return i;
890  nWidth += nCharExtra;
891  }
892 
893  return -1;
894 }
895 
897  Point& rPos, int& nStart,
898  const PhysicalFontFace**, int* const pFallbackLevel) const
899 {
900  std::vector<GlyphItem>::const_iterator pGlyphIter = m_GlyphItems.Impl()->begin();
901  std::vector<GlyphItem>::const_iterator pGlyphIterEnd = m_GlyphItems.Impl()->end();
902  pGlyphIter += nStart;
903 
904  // find next glyph in substring
905  for(; pGlyphIter != pGlyphIterEnd; ++nStart, ++pGlyphIter )
906  {
907  int n = pGlyphIter->charPos();
908  if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
909  break;
910  }
911 
912  // return zero if no more glyph found
913  if( nStart >= static_cast<int>(m_GlyphItems.Impl()->size()) )
914  return false;
915 
916  if( pGlyphIter == pGlyphIterEnd )
917  return false;
918 
919  // update return data with glyph info
920  *pGlyph = &(*pGlyphIter);
921  if (pFallbackLevel)
922  *pFallbackLevel = 0;
923  ++nStart;
924 
925  // calculate absolute position in pixel units
926  Point aRelativePos = pGlyphIter->m_aLinearPos;
927 
928  aRelativePos.setX( aRelativePos.X() / mnUnitsPerPixel );
929  aRelativePos.setY( aRelativePos.Y() / mnUnitsPerPixel );
930  rPos = GetDrawPosition( aRelativePos );
931 
932  return true;
933 }
934 
935 void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
936 {
937  if( nStart >= static_cast<int>(m_GlyphItems.Impl()->size()) )
938  return;
939 
940  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.Impl()->begin();
941  pGlyphIter += nStart;
942 
943  // the nNewXPos argument determines the new cell position
944  // as RTL-glyphs are right justified in their cell
945  // the cell position needs to be adjusted to the glyph position
946  if( pGlyphIter->IsRTLGlyph() )
947  nNewXPos += pGlyphIter->m_nNewWidth - pGlyphIter->origWidth();
948  // calculate the x-offset to the old position
949  long nXDelta = nNewXPos - pGlyphIter->m_aLinearPos.getX();
950  // adjust all following glyph positions if needed
951  if( nXDelta != 0 )
952  {
953  for( std::vector<GlyphItem>::iterator pGlyphIterEnd = m_GlyphItems.Impl()->end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
954  {
955  pGlyphIter->m_aLinearPos.AdjustX(nXDelta );
956  }
957  }
958 }
959 
960 void GenericSalLayout::DropGlyph( int nStart )
961 {
962  if( nStart >= static_cast<int>(m_GlyphItems.Impl()->size()))
963  return;
964 
965  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.Impl()->begin();
966  pGlyphIter += nStart;
967  pGlyphIter->dropGlyph();
968 }
969 
970 void GenericSalLayout::Simplify( bool bIsBase )
971 {
972  // remove dropped glyphs inplace
973  size_t j = 0;
974  for(size_t i = 0; i < m_GlyphItems.Impl()->size(); i++ )
975  {
976  if (bIsBase && (*m_GlyphItems.Impl())[i].IsDropped())
977  continue;
978  if (!bIsBase && (*m_GlyphItems.Impl())[i].glyphId() == 0)
979  continue;
980 
981  if( i != j )
982  {
983  (*m_GlyphItems.Impl())[j] = (*m_GlyphItems.Impl())[i];
984  }
985  j += 1;
986  }
987  m_GlyphItems.Impl()->erase(m_GlyphItems.Impl()->begin() + j, m_GlyphItems.Impl()->end());
988 }
989 
990 MultiSalLayout::MultiSalLayout( std::unique_ptr<SalLayout> pBaseLayout )
991 : SalLayout()
992 , mnLevel( 1 )
993 , mbIncomplete( false )
994 {
995  assert(dynamic_cast<GenericSalLayout*>(pBaseLayout.get()));
996 
997  mpLayouts[ 0 ].reset(static_cast<GenericSalLayout*>(pBaseLayout.release()));
998  mnUnitsPerPixel = mpLayouts[ 0 ]->GetUnitsPerPixel();
999 }
1000 
1001 void MultiSalLayout::SetIncomplete(bool bIncomplete)
1002 {
1003  mbIncomplete = bIncomplete;
1005 }
1006 
1008 {
1009 }
1010 
1011 void MultiSalLayout::AddFallback( std::unique_ptr<SalLayout> pFallback,
1012  ImplLayoutRuns const & rFallbackRuns)
1013 {
1014  assert(dynamic_cast<GenericSalLayout*>(pFallback.get()));
1015  if( mnLevel >= MAX_FALLBACK )
1016  return;
1017 
1018  mpLayouts[ mnLevel ].reset(static_cast<GenericSalLayout*>(pFallback.release()));
1019  maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1020  ++mnLevel;
1021 }
1022 
1024 {
1025  if( mnLevel <= 1 )
1026  return false;
1027  if (!mbIncomplete)
1028  maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1029  return true;
1030 }
1031 
1033 {
1034  SalLayout::AdjustLayout( rArgs );
1035  ImplLayoutArgs aMultiArgs = rArgs;
1036  std::unique_ptr<DeviceCoordinate[]> pJustificationArray;
1037 
1038  if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1039  {
1040  // for stretched text in a MultiSalLayout the target width needs to be
1041  // distributed by individually adjusting its virtual character widths
1042  DeviceCoordinate nTargetWidth = aMultiArgs.mnLayoutWidth;
1043  nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1044  aMultiArgs.mnLayoutWidth = 0;
1045 
1046  // we need to get the original unmodified layouts ready
1047  for( int n = 0; n < mnLevel; ++n )
1048  mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1049  // then we can measure the unmodified metrics
1050  int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1051  pJustificationArray.reset(new DeviceCoordinate[nCharCount]);
1052  FillDXArray( pJustificationArray.get() );
1053  // #i17359# multilayout is not simplified yet, so calculating the
1054  // unjustified width needs handholding; also count the number of
1055  // stretchable virtual char widths
1056  DeviceCoordinate nOrigWidth = 0;
1057  int nStretchable = 0;
1058  for( int i = 0; i < nCharCount; ++i )
1059  {
1060  // convert array from widths to sum of widths
1061  nOrigWidth += pJustificationArray[i];
1062  if( pJustificationArray[i] > 0 )
1063  ++nStretchable;
1064  }
1065 
1066  // now we are able to distribute the extra width over the virtual char widths
1067  if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1068  {
1069  DeviceCoordinate nDiffWidth = nTargetWidth - nOrigWidth;
1070  DeviceCoordinate nWidthSum = 0;
1071  for( int i = 0; i < nCharCount; ++i )
1072  {
1073  DeviceCoordinate nJustWidth = pJustificationArray[i];
1074  if( (nJustWidth > 0) && (nStretchable > 0) )
1075  {
1076  DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
1077  nJustWidth += nDeltaWidth;
1078  nDiffWidth -= nDeltaWidth;
1079  --nStretchable;
1080  }
1081  nWidthSum += nJustWidth;
1082  pJustificationArray[i] = nWidthSum;
1083  }
1084  if( nWidthSum != nTargetWidth )
1085  pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1086 
1087  // the justification array is still in base level units
1088  // => convert it to pixel units
1089  if( mnUnitsPerPixel > 1 )
1090  {
1091  for( int i = 0; i < nCharCount; ++i )
1092  {
1093  DeviceCoordinate nVal = pJustificationArray[ i ];
1094  nVal += (mnUnitsPerPixel + 1) / 2;
1095  pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1096  }
1097  }
1098 
1099  // change the mpDXArray temporarily (just for the justification)
1100  aMultiArgs.mpDXArray = pJustificationArray.get();
1101  }
1102  }
1103 
1104  // Compute rtl flags, since in some scripts glyphs/char order can be
1105  // reversed for a few character sequences e.g. Myanmar
1106  std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1107  rArgs.ResetPos();
1108  bool bRtl;
1109  int nRunStart, nRunEnd;
1110  while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1111  {
1112  if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1113  vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1114  }
1115  rArgs.ResetPos();
1116 
1117  // prepare "merge sort"
1118  int nStartOld[ MAX_FALLBACK ];
1119  int nStartNew[ MAX_FALLBACK ];
1120  const GlyphItem* pGlyphs[MAX_FALLBACK];
1121  bool bValid[MAX_FALLBACK] = { false };
1122 
1123  Point aPos;
1124  int nLevel = 0, n;
1125  for( n = 0; n < mnLevel; ++n )
1126  {
1127  // now adjust the individual components
1128  if( n > 0 )
1129  {
1130  aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1131  aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback;
1132  }
1133  mpLayouts[n]->AdjustLayout( aMultiArgs );
1134 
1135  // remove unused parts of component
1136  if( n > 0 )
1137  {
1138  if (mbIncomplete && (n == mnLevel-1))
1139  mpLayouts[n]->Simplify( true );
1140  else
1141  mpLayouts[n]->Simplify( false );
1142  }
1143 
1144  // prepare merging components
1145  nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1146  bValid[nLevel] = mpLayouts[n]->GetNextGlyph(&pGlyphs[nLevel], aPos, nStartNew[nLevel]);
1147 
1148  if( (n > 0) && !bValid[ nLevel ] )
1149  {
1150  // an empty fallback layout can be released
1151  mpLayouts[n].reset();
1152  }
1153  else
1154  {
1155  // reshuffle used fallbacks if needed
1156  if( nLevel != n )
1157  {
1158  mpLayouts[ nLevel ] = std::move(mpLayouts[ n ]);
1159  maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1160  }
1161  ++nLevel;
1162  }
1163  }
1164  mnLevel = nLevel;
1165 
1166  // prepare merge the fallback levels
1167  long nXPos = 0;
1168  double fUnitMul = 1.0;
1169  for( n = 0; n < nLevel; ++n )
1170  maFallbackRuns[n].ResetPos();
1171 
1172  int nFirstValid = -1;
1173  for( n = 0; n < nLevel; ++n )
1174  {
1175  if(bValid[n])
1176  {
1177  nFirstValid = n;
1178  break;
1179  }
1180  }
1181  assert(nFirstValid >= 0);
1182 
1183  // get the next codepoint index that needs fallback
1184  int nActiveCharPos = pGlyphs[nFirstValid]->charPos();
1185  int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1186  // get the end index of the active run
1187  int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
1188  rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1189  int nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
1190  // merge the fallback levels
1191  while( bValid[nFirstValid] && (nLevel > 0))
1192  {
1193  // find best fallback level
1194  for( n = 0; n < nLevel; ++n )
1195  if( bValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1196  // fallback level n wins when it requested no further fallback
1197  break;
1198  int nFBLevel = n;
1199 
1200  if( n < nLevel )
1201  {
1202  // use base(n==0) or fallback(n>=1) level
1203  fUnitMul = mnUnitsPerPixel;
1204  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1205  long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1206  mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1207  }
1208  else
1209  {
1210  n = 0; // keep NotDef in base level
1211  fUnitMul = 1.0;
1212  }
1213 
1214  if( n > 0 )
1215  {
1216  // drop the NotDef glyphs in the base layout run if a fallback run exists
1217  while (
1218  (maFallbackRuns[n-1].PosIsInRun(pGlyphs[nFirstValid]->charPos())) &&
1219  (!maFallbackRuns[n].PosIsInAnyRun(pGlyphs[nFirstValid]->charPos()))
1220  )
1221  {
1222  mpLayouts[0]->DropGlyph( nStartOld[0] );
1223  nStartOld[0] = nStartNew[0];
1224  bValid[nFirstValid] = mpLayouts[0]->GetNextGlyph(&pGlyphs[nFirstValid], aPos, nStartNew[0]);
1225 
1226  if( !bValid[nFirstValid] )
1227  break;
1228  }
1229  }
1230 
1231  // skip to end of layout run and calculate its advance width
1232  DeviceCoordinate nRunAdvance = 0;
1233  bool bKeepNotDef = (nFBLevel >= nLevel);
1234  for(;;)
1235  {
1236  nRunAdvance += pGlyphs[n]->m_nNewWidth;
1237 
1238  // proceed to next glyph
1239  nStartOld[n] = nStartNew[n];
1240  int nOrigCharPos = pGlyphs[n]->charPos();
1241  bValid[n] = mpLayouts[n]->GetNextGlyph(&pGlyphs[n], aPos, nStartNew[n]);
1242  // break after last glyph of active layout
1243  if( !bValid[n] )
1244  {
1245  // performance optimization (when a fallback layout is no longer needed)
1246  if( n >= nLevel-1 )
1247  --nLevel;
1248  break;
1249  }
1250 
1251  //If the next character is one which belongs to the next level, then we
1252  //are finished here for now, and we'll pick up after the next level has
1253  //been processed
1254  if ((n+1 < nLevel) && (pGlyphs[n]->charPos() != nOrigCharPos))
1255  {
1256  if (nOrigCharPos < pGlyphs[n]->charPos())
1257  {
1258  if (pGlyphs[n+1]->charPos() > nOrigCharPos && (pGlyphs[n+1]->charPos() < pGlyphs[n]->charPos()))
1259  break;
1260  }
1261  else if (nOrigCharPos > pGlyphs[n]->charPos())
1262  {
1263  if (pGlyphs[n+1]->charPos() > pGlyphs[n]->charPos() && (pGlyphs[n+1]->charPos() < nOrigCharPos))
1264  break;
1265  }
1266  }
1267 
1268  // break at end of layout run
1269  if( n > 0 )
1270  {
1271  // skip until end of fallback run
1272  if (!maFallbackRuns[n-1].PosIsInRun(pGlyphs[n]->charPos()))
1273  break;
1274  }
1275  else
1276  {
1277  // break when a fallback is needed and available
1278  bool bNeedFallback = maFallbackRuns[0].PosIsInRun(pGlyphs[nFirstValid]->charPos());
1279  if( bNeedFallback )
1280  if (!maFallbackRuns[nLevel-1].PosIsInRun(pGlyphs[nFirstValid]->charPos()))
1281  break;
1282  // break when change from resolved to unresolved base layout run
1283  if( bKeepNotDef && !bNeedFallback )
1284  { maFallbackRuns[0].NextRun(); break; }
1285  bKeepNotDef = bNeedFallback;
1286  }
1287  // check for reordered glyphs
1288  if (aMultiArgs.mpDXArray &&
1289  nRunVisibleEndChar < mnEndCharPos &&
1290  nRunVisibleEndChar >= mnMinCharPos &&
1291  pGlyphs[n]->charPos() < mnEndCharPos &&
1292  pGlyphs[n]->charPos() >= mnMinCharPos)
1293  {
1294  if (vRtl[nActiveCharPos - mnMinCharPos])
1295  {
1296  if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1297  >= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
1298  {
1299  nRunVisibleEndChar = pGlyphs[n]->charPos();
1300  }
1301  }
1302  else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1303  <= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
1304  {
1305  nRunVisibleEndChar = pGlyphs[n]->charPos();
1306  }
1307  }
1308  }
1309 
1310  // if a justification array is available
1311  // => use it directly to calculate the corresponding run width
1312  if( aMultiArgs.mpDXArray )
1313  {
1314  // the run advance is the width from the first char
1315  // in the run to the first char in the next run
1316  nRunAdvance = 0;
1317  nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1318  if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
1319  {
1320  if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1321  nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1322  if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1323  nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
1324  }
1325  else
1326  {
1327  if (nRunVisibleEndChar >= mnMinCharPos)
1328  nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1329  if (nLastRunEndChar >= mnMinCharPos)
1330  nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
1331  }
1332  nLastRunEndChar = nRunVisibleEndChar;
1333  nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
1334  // the requested width is still in pixel units
1335  // => convert it to base level font units
1336  nRunAdvance *= mnUnitsPerPixel;
1337  }
1338  else
1339  {
1340  // the measured width is still in fallback font units
1341  // => convert it to base level font units
1342  if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1343  nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
1344  }
1345 
1346  // calculate new x position (in base level units)
1347  nXPos += nRunAdvance;
1348 
1349  // prepare for next fallback run
1350  nActiveCharPos = pGlyphs[nFirstValid]->charPos();
1351  // it essential that the runs don't get ahead of themselves and in the
1352  // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1353  // have already been reached on the base level
1354  for( int i = nFBLevel; --i >= 0;)
1355  {
1356  if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1357  {
1358  if (bRtl)
1359  {
1360  if (nRunStart > nActiveCharPos)
1362  }
1363  else
1364  {
1365  if (nRunEnd <= nActiveCharPos)
1367  }
1368  }
1369  }
1370  }
1371 
1372  mpLayouts[0]->Simplify( true );
1373 }
1374 
1376 {
1377  if( mnLevel > 0 )
1378  mpLayouts[0]->InitFont();
1379 }
1380 
1381 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1382 {
1383  for( int i = mnLevel; --i >= 0; )
1384  {
1385  SalLayout& rLayout = *mpLayouts[ i ];
1386  rLayout.DrawBase() += maDrawBase;
1387  rLayout.DrawOffset() += maDrawOffset;
1388  rLayout.InitFont();
1389  rLayout.DrawText( rGraphics );
1390  rLayout.DrawOffset() -= maDrawOffset;
1391  rLayout.DrawBase() -= maDrawBase;
1392  }
1393  // NOTE: now the baselevel font is active again
1394 }
1395 
1396 sal_Int32 MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1397 {
1398  if( mnLevel <= 0 )
1399  return -1;
1400  if( mnLevel == 1 )
1401  return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1402 
1403  int nCharCount = mnEndCharPos - mnMinCharPos;
1404  std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[nCharCount]);
1405  std::unique_ptr<DeviceCoordinate[]> const pFallbackCharWidths(new DeviceCoordinate[nCharCount]);
1406  mpLayouts[0]->FillDXArray( pCharWidths.get() );
1407 
1408  for( int n = 1; n < mnLevel; ++n )
1409  {
1410  SalLayout& rLayout = *mpLayouts[ n ];
1411  rLayout.FillDXArray( pFallbackCharWidths.get() );
1412  double fUnitMul = mnUnitsPerPixel;
1413  fUnitMul /= rLayout.GetUnitsPerPixel();
1414  for( int i = 0; i < nCharCount; ++i )
1415  {
1416  if( pCharWidths[ i ] == 0 )
1417  {
1418  DeviceCoordinate w = pFallbackCharWidths[i];
1419  w = static_cast<DeviceCoordinate>(w * fUnitMul + 0.5);
1420  pCharWidths[ i ] = w;
1421  }
1422  }
1423  }
1424 
1425  DeviceCoordinate nWidth = 0;
1426  for( int i = 0; i < nCharCount; ++i )
1427  {
1428  nWidth += pCharWidths[ i ] * nFactor;
1429  if( nWidth > nMaxWidth )
1430  return (i + mnMinCharPos);
1431  nWidth += nCharExtra;
1432  }
1433 
1434  return -1;
1435 }
1436 
1438 {
1439  DeviceCoordinate nMaxWidth = 0;
1440 
1441  // prepare merging of fallback levels
1442  std::unique_ptr<DeviceCoordinate[]> pTempWidths;
1443  const int nCharCount = mnEndCharPos - mnMinCharPos;
1444  if( pCharWidths )
1445  {
1446  for( int i = 0; i < nCharCount; ++i )
1447  pCharWidths[i] = 0;
1448  pTempWidths.reset(new DeviceCoordinate[nCharCount]);
1449  }
1450 
1451  for( int n = mnLevel; --n >= 0; )
1452  {
1453  // query every fallback level
1454  DeviceCoordinate nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths.get() );
1455  if( !nTextWidth )
1456  continue;
1457  // merge results from current level
1458  double fUnitMul = mnUnitsPerPixel;
1459  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1460  nTextWidth = static_cast<DeviceCoordinate>(nTextWidth * fUnitMul + 0.5);
1461  if( nMaxWidth < nTextWidth )
1462  nMaxWidth = nTextWidth;
1463  if( !pCharWidths )
1464  continue;
1465  // calculate virtual char widths using most probable fallback layout
1466  for( int i = 0; i < nCharCount; ++i )
1467  {
1468  // #i17359# restriction:
1469  // one char cannot be resolved from different fallbacks
1470  if( pCharWidths[i] != 0 )
1471  continue;
1472  DeviceCoordinate nCharWidth = pTempWidths[i];
1473  if( !nCharWidth )
1474  continue;
1475  nCharWidth = static_cast<DeviceCoordinate>(nCharWidth * fUnitMul + 0.5);
1476  pCharWidths[i] = nCharWidth;
1477  }
1478  }
1479 
1480  return nMaxWidth;
1481 }
1482 
1483 void MultiSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
1484 {
1485  SalLayout& rLayout = *mpLayouts[ 0 ];
1486  rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
1487 
1488  if( mnLevel > 1 )
1489  {
1490  std::unique_ptr<long[]> const pTempPos(new long[nMaxIndex]);
1491  for( int n = 1; n < mnLevel; ++n )
1492  {
1493  mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos.get() );
1494  double fUnitMul = mnUnitsPerPixel;
1495  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1496  for( int i = 0; i < nMaxIndex; ++i )
1497  if( pTempPos[i] >= 0 )
1498  {
1499  long w = pTempPos[i];
1500  w = static_cast<long>(w*fUnitMul + 0.5);
1501  pCaretXArray[i] = w;
1502  }
1503  }
1504  }
1505 }
1506 
1508  Point& rPos, int& nStart,
1509  const PhysicalFontFace** pFallbackFont,
1510  int* const pFallbackLevel) const
1511 {
1512  // NOTE: nStart is tagged with current font index
1513  int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
1514  nStart &= ~GF_FONTMASK;
1515  for(; nLevel < mnLevel; ++nLevel, nStart=0 )
1516  {
1517  GenericSalLayout& rLayout = *mpLayouts[ nLevel ];
1518  rLayout.InitFont();
1519  const PhysicalFontFace* pFontFace = rLayout.GetFont().GetFontFace();
1520  if (rLayout.GetNextGlyph(pGlyph, rPos, nStart))
1521  {
1522  int nFontTag = nLevel << GF_FONTSHIFT;
1523  nStart |= nFontTag;
1524  if (pFallbackFont)
1525  *pFallbackFont = pFontFace;
1526  if (pFallbackLevel)
1527  *pFallbackLevel = nLevel;
1528  rPos += maDrawBase;
1529  rPos += maDrawOffset;
1530  return true;
1531  }
1532  }
1533 
1534  // #111016# reset to base level font when done
1535  mpLayouts[0]->InitFont();
1536  return false;
1537 }
1538 
1540 {
1541  bool bRet = false;
1542 
1543  for( int i = mnLevel; --i >= 0; )
1544  {
1545  SalLayout& rLayout = *mpLayouts[ i ];
1546  rLayout.DrawBase() = maDrawBase;
1547  rLayout.DrawOffset() += maDrawOffset;
1548  rLayout.InitFont();
1549  bRet |= rLayout.GetOutline(rPPV);
1550  rLayout.DrawOffset() -= maDrawOffset;
1551  }
1552 
1553  return bRet;
1554 }
1555 
1556 bool MultiSalLayout::IsKashidaPosValid(int nCharPos) const
1557 {
1558  // Check the base layout
1559  bool bValid = mpLayouts[0]->IsKashidaPosValid(nCharPos);
1560 
1561  // If base layout returned false, it might be because the character was not
1562  // supported there, so we check fallback layouts.
1563  if (!bValid)
1564  {
1565  for (int i = 1; i < mnLevel; ++i)
1566  {
1567  // - 1 because there is no fallback run for the base layout, IIUC.
1568  if (maFallbackRuns[i - 1].PosIsInAnyRun(nCharPos))
1569  {
1570  bValid = mpLayouts[i]->IsKashidaPosValid(nCharPos);
1571  break;
1572  }
1573  }
1574  }
1575 
1576  return bValid;
1577 }
1578 
1580 {
1581  // No access to the glyphs by default.
1582  return nullptr;
1583 }
1584 
1585 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DeviceCoordinate GetTextWidth() const final override
Definition: sallayout.cxx:659
int const mnMinCharPos
Definition: sallayout.hxx:82
const size_t count(pCandidateA->getBorderLines().size())
virtual bool GetOutline(basegfx::B2DPolyPolygonVector &) const
Definition: sallayout.cxx:590
#define LANGUAGE_TELUGU
void AddFallback(std::unique_ptr< SalLayout > pFallbackLayout, ImplLayoutRuns const &)
Definition: sallayout.cxx:1011
int GetUnitsPerPixel() const
Definition: vcllayout.hxx:80
#define LANGUAGE_ARABIC_SAUDI_ARABIA
#define LANGUAGE_URDU_PAKISTAN
bool GetOutline(basegfx::B2DPolyPolygonVector &) const override
Definition: sallayout.cxx:1539
std::unique_ptr< GenericSalLayout > mpLayouts[MAX_FALLBACK]
Definition: sallayout.hxx:154
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr, int *const pFallbackLevel=nullptr) const override
Definition: sallayout.cxx:1507
#define LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
#define MAX_FALLBACK
Definition: sallayout.hxx:41
#define GF_FONTMASK
Definition: sallayout.cxx:48
void InitFont() const override
Definition: sallayout.cxx:1375
bool GetGlyphOutline(basegfx::B2DPolyPolygon &) const
#define LANGUAGE_AMHARIC_ETHIOPIA
void AddRun(int nMinCharPos, int nEndCharPos, bool bRTL)
Definition: sallayout.cxx:459
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
sal_UCS4 GetMirroredChar(sal_UCS4 nChar)
Definition: sallayout.cxx:131
void Justify(DeviceCoordinate nNewWidth)
Definition: sallayout.cxx:683
void MoveGlyph(int nStart, long nNewXPos)
Definition: sallayout.cxx:935
#define LANGUAGE_TAMIL
LogicalFontInstance & GetFont() const
Definition: sallayout.hxx:183
MultiSalLayout(std::unique_ptr< SalLayout > pBaseLayout)
Definition: sallayout.cxx:990
Point maDrawBase
Definition: vcllayout.hxx:115
bool IsEmpty() const
int const mnEndCharPos
Definition: sallayout.hxx:83
#define LANGUAGE_GUJARATI
sal_uInt16 sal_Unicode
std::vector< int > maRuns
Definition: sallayout.hxx:57
void SetIncomplete(bool bIncomplete)
Definition: sallayout.cxx:1001
#define LANGUAGE_ODIA
void AddPos(int nCharPos, bool bRTL)
Definition: sallayout.cxx:231
bool IsEmpty() const
Definition: sallayout.hxx:66
void setX(long nX)
void GetCaretPositions(int nArraySize, long *pCaretXArray) const final override
Definition: sallayout.cxx:847
ImplLayoutRuns maRuns
Definition: sallayout.hxx:94
static bool lcl_CanApplyAsianKerning(sal_Unicode cp)
Definition: sallayout.cxx:797
#define LANGUAGE_SINDHI
long DeviceCoordinate
bool IsKashidaPosValid(int nCharPos) const override
Definition: sallayout.cxx:1556
#define LANGUAGE_KHMER
#define LANGUAGE_PUNJABI
virtual void AdjustLayout(ImplLayoutArgs &)
Definition: sallayout.cxx:553
DeviceCoordinate mnLayoutWidth
Definition: sallayout.hxx:90
bool IsValid() const
void ApplyAsianKerning(const OUString &rStr)
Definition: sallayout.cxx:802
#define LANGUAGE_TIBETAN
#define LANGUAGE_BURMESE
void setY(long nY)
bool GetBoundRect(tools::Rectangle &) const
Definition: sallayout.cxx:622
virtual DeviceCoordinate FillDXArray(DeviceCoordinate *pDXArray) const =0
void GetCaretPositions(int nArraySize, long *pCaretXArray) const override
Definition: sallayout.cxx:1483
abstract base class for physical font faces
const OUString & mrStr
Definition: sallayout.hxx:81
int mnEndCharPos
Definition: vcllayout.hxx:109
virtual ~SalLayout()
Definition: sallayout.cxx:550
TEST
virtual void InitFont() const
Definition: vcllayout.hxx:77
bool PrepareFallback()
Definition: sallayout.cxx:489
#define GF_FONTSHIFT
Definition: sallayout.cxx:49
DeviceCoordinate FillDXArray(DeviceCoordinate *pDXArray) const final override
Definition: sallayout.cxx:650
virtual void DrawText(SalGraphics &) const =0
SalLayoutGlyphsImpl * Impl() const
Definition: glyphitem.hxx:42
int i
bool PosIsInRun(int nCharPos) const
Definition: sallayout.cxx:281
void GetCharWidths(DeviceCoordinate *pCharWidths) const
void DropGlyph(int nStart)
Definition: sallayout.cxx:960
void transform(const basegfx::B2DHomMatrix &rMatrix)
#define LANGUAGE_TIGRIGNA_ETHIOPIA
sal_Int32 w
Point GetDrawPosition(const Point &rRelative=Point(0, 0)) const
Definition: sallayout.cxx:560
constexpr LanguageType primary(LanguageType lt)
#define LANGUAGE_FARSI
virtual ~MultiSalLayout() override
Definition: sallayout.cxx:1007
#define LANGUAGE_MALAYALAM
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
Definition: sallayout.cxx:535
long X() const
#define LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
Point & DrawBase()
Definition: vcllayout.hxx:69
#define LANGUAGE_THAI
int charPos() const
bool GetNextPos(int *nCharPos, bool *bRTL)
Definition: sallayout.cxx:323
static bool IsControlChar(sal_UCS4 cChar)
Definition: sallayout.cxx:210
std::ostream & operator<<(std::ostream &s, ImplLayoutArgs const &rArgs)
Definition: sallayout.cxx:52
double mnLayoutWidth
int mnUnitsPerPixel
Definition: vcllayout.hxx:111
virtual void GetCaretPositions(int nArraySize, long *pCaretXArray) const =0
#define LANGUAGE_HINDI
bool GetRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL) const
Definition: sallayout.cxx:367
void AdjustLayout(ImplLayoutArgs &) override
Definition: sallayout.cxx:1032
SalLayoutFlags
Definition: outdev.hxx:121
int mnOrientation
Definition: vcllayout.hxx:112
sal_uInt32 count() const
static int lcl_CalcAsianKerning(sal_UCS4 c, bool bLeft)
Definition: sallayout.cxx:763
#define LANGUAGE_LAO
void NextRun()
Definition: sallayout.hxx:68
FuncFlags const mnFlags
tools::Rectangle & Union(const tools::Rectangle &rRect)
#define SAL_WARN_IF(condition, area, stream)
void DrawText(SalGraphics &) const override
Definition: sallayout.cxx:1381
bool LayoutText(ImplLayoutArgs &, const SalLayoutGlyphs *) override
Definition: sallayout.cxx:1023
#define LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
void AddRun(int nMinRunPos, int nEndRunPos, bool bRTL)
Definition: sallayout.cxx:257
sal_UCS4 GetLocalizedChar(sal_UCS4 nChar, LanguageType eLang)
Definition: sallayout.cxx:137
void ResetPos()
Definition: sallayout.hxx:67
const PhysicalFontFace * GetFontFace() const
ImplLayoutArgs(const OUString &rStr, int nMinCharPos, int nEndCharPos, SalLayoutFlags nFlags, const LanguageTag &rLanguageTag, vcl::TextLayoutCache const *pLayoutCache)
Definition: sallayout.cxx:388
virtual bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr, int *const pFallbackLevel=nullptr) const =0
SalLayoutFlags mnFlags
Definition: sallayout.hxx:80
#define LANGUAGE_BENGALI
SalLayoutGlyphs m_GlyphItems
Definition: sallayout.hxx:213
ImplLayoutRuns maFallbackRuns
Definition: sallayout.hxx:95
ImplLayoutRuns maFallbackRuns[MAX_FALLBACK]
Definition: sallayout.hxx:155
bool PosIsInAnyRun(int nCharPos) const
Definition: sallayout.cxx:302
DeviceCoordinate FillDXArray(DeviceCoordinate *pDXArray) const override
Definition: sallayout.cxx:1437
sal_uInt32 sal_UCS4
Definition: fontcharmap.hxx:29
int mnMinCharPos
Definition: vcllayout.hxx:108
Point maDrawOffset
Definition: vcllayout.hxx:114
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const final override
Definition: sallayout.cxx:878
#define LANGUAGE_KANNADA
sal_Int32 const nLength
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
bool GetGlyphBoundRect(tools::Rectangle &) const
virtual const SalLayoutGlyphs * GetGlyphs() const
Definition: sallayout.cxx:1579
void Simplify(bool bIsBase)
Definition: sallayout.cxx:970
Point & DrawOffset()
Definition: vcllayout.hxx:71
long Y() const
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override
Definition: sallayout.cxx:1396
const DeviceCoordinate * mpDXArray
Definition: sallayout.hxx:89
bool anyOf(strong_int v) const
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr, int *const pFallbackLevel=nullptr) const override
Definition: sallayout.cxx:896
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo