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**) 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  ++nStart;
922 
923  // calculate absolute position in pixel units
924  Point aRelativePos = pGlyphIter->m_aLinearPos;
925 
926  aRelativePos.setX( aRelativePos.X() / mnUnitsPerPixel );
927  aRelativePos.setY( aRelativePos.Y() / mnUnitsPerPixel );
928  rPos = GetDrawPosition( aRelativePos );
929 
930  return true;
931 }
932 
933 void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
934 {
935  if( nStart >= static_cast<int>(m_GlyphItems.Impl()->size()) )
936  return;
937 
938  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.Impl()->begin();
939  pGlyphIter += nStart;
940 
941  // the nNewXPos argument determines the new cell position
942  // as RTL-glyphs are right justified in their cell
943  // the cell position needs to be adjusted to the glyph position
944  if( pGlyphIter->IsRTLGlyph() )
945  nNewXPos += pGlyphIter->m_nNewWidth - pGlyphIter->origWidth();
946  // calculate the x-offset to the old position
947  long nXDelta = nNewXPos - pGlyphIter->m_aLinearPos.getX();
948  // adjust all following glyph positions if needed
949  if( nXDelta != 0 )
950  {
951  for( std::vector<GlyphItem>::iterator pGlyphIterEnd = m_GlyphItems.Impl()->end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
952  {
953  pGlyphIter->m_aLinearPos.AdjustX(nXDelta );
954  }
955  }
956 }
957 
958 void GenericSalLayout::DropGlyph( int nStart )
959 {
960  if( nStart >= static_cast<int>(m_GlyphItems.Impl()->size()))
961  return;
962 
963  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.Impl()->begin();
964  pGlyphIter += nStart;
965  pGlyphIter->dropGlyph();
966 }
967 
968 void GenericSalLayout::Simplify( bool bIsBase )
969 {
970  // remove dropped glyphs inplace
971  size_t j = 0;
972  for(size_t i = 0; i < m_GlyphItems.Impl()->size(); i++ )
973  {
974  if (bIsBase && (*m_GlyphItems.Impl())[i].IsDropped())
975  continue;
976  if (!bIsBase && (*m_GlyphItems.Impl())[i].glyphId() == 0)
977  continue;
978 
979  if( i != j )
980  {
981  (*m_GlyphItems.Impl())[j] = (*m_GlyphItems.Impl())[i];
982  }
983  j += 1;
984  }
985  m_GlyphItems.Impl()->erase(m_GlyphItems.Impl()->begin() + j, m_GlyphItems.Impl()->end());
986 }
987 
988 MultiSalLayout::MultiSalLayout( std::unique_ptr<SalLayout> pBaseLayout )
989 : SalLayout()
990 , mnLevel( 1 )
991 , mbIncomplete( false )
992 {
993  assert(dynamic_cast<GenericSalLayout*>(pBaseLayout.get()));
994 
995  mpLayouts[ 0 ].reset(static_cast<GenericSalLayout*>(pBaseLayout.release()));
996  mnUnitsPerPixel = mpLayouts[ 0 ]->GetUnitsPerPixel();
997 }
998 
999 void MultiSalLayout::SetIncomplete(bool bIncomplete)
1000 {
1001  mbIncomplete = bIncomplete;
1003 }
1004 
1006 {
1007 }
1008 
1009 void MultiSalLayout::AddFallback( std::unique_ptr<SalLayout> pFallback,
1010  ImplLayoutRuns const & rFallbackRuns)
1011 {
1012  assert(dynamic_cast<GenericSalLayout*>(pFallback.get()));
1013  if( mnLevel >= MAX_FALLBACK )
1014  return;
1015 
1016  mpLayouts[ mnLevel ].reset(static_cast<GenericSalLayout*>(pFallback.release()));
1017  maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1018  ++mnLevel;
1019 }
1020 
1022 {
1023  if( mnLevel <= 1 )
1024  return false;
1025  if (!mbIncomplete)
1026  maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1027  return true;
1028 }
1029 
1031 {
1032  SalLayout::AdjustLayout( rArgs );
1033  ImplLayoutArgs aMultiArgs = rArgs;
1034  std::unique_ptr<DeviceCoordinate[]> pJustificationArray;
1035 
1036  if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1037  {
1038  // for stretched text in a MultiSalLayout the target width needs to be
1039  // distributed by individually adjusting its virtual character widths
1040  DeviceCoordinate nTargetWidth = aMultiArgs.mnLayoutWidth;
1041  nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1042  aMultiArgs.mnLayoutWidth = 0;
1043 
1044  // we need to get the original unmodified layouts ready
1045  for( int n = 0; n < mnLevel; ++n )
1046  mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1047  // then we can measure the unmodified metrics
1048  int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1049  pJustificationArray.reset(new DeviceCoordinate[nCharCount]);
1050  FillDXArray( pJustificationArray.get() );
1051  // #i17359# multilayout is not simplified yet, so calculating the
1052  // unjustified width needs handholding; also count the number of
1053  // stretchable virtual char widths
1054  DeviceCoordinate nOrigWidth = 0;
1055  int nStretchable = 0;
1056  for( int i = 0; i < nCharCount; ++i )
1057  {
1058  // convert array from widths to sum of widths
1059  nOrigWidth += pJustificationArray[i];
1060  if( pJustificationArray[i] > 0 )
1061  ++nStretchable;
1062  }
1063 
1064  // now we are able to distribute the extra width over the virtual char widths
1065  if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1066  {
1067  DeviceCoordinate nDiffWidth = nTargetWidth - nOrigWidth;
1068  DeviceCoordinate nWidthSum = 0;
1069  for( int i = 0; i < nCharCount; ++i )
1070  {
1071  DeviceCoordinate nJustWidth = pJustificationArray[i];
1072  if( (nJustWidth > 0) && (nStretchable > 0) )
1073  {
1074  DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
1075  nJustWidth += nDeltaWidth;
1076  nDiffWidth -= nDeltaWidth;
1077  --nStretchable;
1078  }
1079  nWidthSum += nJustWidth;
1080  pJustificationArray[i] = nWidthSum;
1081  }
1082  if( nWidthSum != nTargetWidth )
1083  pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1084 
1085  // the justification array is still in base level units
1086  // => convert it to pixel units
1087  if( mnUnitsPerPixel > 1 )
1088  {
1089  for( int i = 0; i < nCharCount; ++i )
1090  {
1091  DeviceCoordinate nVal = pJustificationArray[ i ];
1092  nVal += (mnUnitsPerPixel + 1) / 2;
1093  pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1094  }
1095  }
1096 
1097  // change the mpDXArray temporarily (just for the justification)
1098  aMultiArgs.mpDXArray = pJustificationArray.get();
1099  }
1100  }
1101 
1102  // Compute rtl flags, since in some scripts glyphs/char order can be
1103  // reversed for a few character sequences e.g. Myanmar
1104  std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1105  rArgs.ResetPos();
1106  bool bRtl;
1107  int nRunStart, nRunEnd;
1108  while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1109  {
1110  if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1111  vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1112  }
1113  rArgs.ResetPos();
1114 
1115  // prepare "merge sort"
1116  int nStartOld[ MAX_FALLBACK ];
1117  int nStartNew[ MAX_FALLBACK ];
1118  const GlyphItem* pGlyphs[MAX_FALLBACK];
1119  bool bValid[MAX_FALLBACK] = { false };
1120 
1121  Point aPos;
1122  int nLevel = 0, n;
1123  for( n = 0; n < mnLevel; ++n )
1124  {
1125  // now adjust the individual components
1126  if( n > 0 )
1127  {
1128  aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1129  aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback;
1130  }
1131  mpLayouts[n]->AdjustLayout( aMultiArgs );
1132 
1133  // remove unused parts of component
1134  if( n > 0 )
1135  {
1136  if (mbIncomplete && (n == mnLevel-1))
1137  mpLayouts[n]->Simplify( true );
1138  else
1139  mpLayouts[n]->Simplify( false );
1140  }
1141 
1142  // prepare merging components
1143  nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1144  bValid[nLevel] = mpLayouts[n]->GetNextGlyph(&pGlyphs[nLevel], aPos, nStartNew[nLevel]);
1145 
1146  if( (n > 0) && !bValid[ nLevel ] )
1147  {
1148  // an empty fallback layout can be released
1149  mpLayouts[n].reset();
1150  }
1151  else
1152  {
1153  // reshuffle used fallbacks if needed
1154  if( nLevel != n )
1155  {
1156  mpLayouts[ nLevel ] = std::move(mpLayouts[ n ]);
1157  maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1158  }
1159  ++nLevel;
1160  }
1161  }
1162  mnLevel = nLevel;
1163 
1164  // prepare merge the fallback levels
1165  long nXPos = 0;
1166  double fUnitMul = 1.0;
1167  for( n = 0; n < nLevel; ++n )
1168  maFallbackRuns[n].ResetPos();
1169 
1170  int nFirstValid = -1;
1171  for( n = 0; n < nLevel; ++n )
1172  {
1173  if(bValid[n])
1174  {
1175  nFirstValid = n;
1176  break;
1177  }
1178  }
1179  assert(nFirstValid >= 0);
1180 
1181  // get the next codepoint index that needs fallback
1182  int nActiveCharPos = pGlyphs[nFirstValid]->charPos();
1183  int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1184  // get the end index of the active run
1185  int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
1186  rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1187  int nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
1188  // merge the fallback levels
1189  while( bValid[nFirstValid] && (nLevel > 0))
1190  {
1191  // find best fallback level
1192  for( n = 0; n < nLevel; ++n )
1193  if( bValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1194  // fallback level n wins when it requested no further fallback
1195  break;
1196  int nFBLevel = n;
1197 
1198  if( n < nLevel )
1199  {
1200  // use base(n==0) or fallback(n>=1) level
1201  fUnitMul = mnUnitsPerPixel;
1202  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1203  long nNewPos = static_cast<long>(nXPos/fUnitMul + 0.5);
1204  mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1205  }
1206  else
1207  {
1208  n = 0; // keep NotDef in base level
1209  fUnitMul = 1.0;
1210  }
1211 
1212  if( n > 0 )
1213  {
1214  // drop the NotDef glyphs in the base layout run if a fallback run exists
1215  while (
1216  (maFallbackRuns[n-1].PosIsInRun(pGlyphs[nFirstValid]->charPos())) &&
1217  (!maFallbackRuns[n].PosIsInAnyRun(pGlyphs[nFirstValid]->charPos()))
1218  )
1219  {
1220  mpLayouts[0]->DropGlyph( nStartOld[0] );
1221  nStartOld[0] = nStartNew[0];
1222  bValid[nFirstValid] = mpLayouts[0]->GetNextGlyph(&pGlyphs[nFirstValid], aPos, nStartNew[0]);
1223 
1224  if( !bValid[nFirstValid] )
1225  break;
1226  }
1227  }
1228 
1229  // skip to end of layout run and calculate its advance width
1230  DeviceCoordinate nRunAdvance = 0;
1231  bool bKeepNotDef = (nFBLevel >= nLevel);
1232  for(;;)
1233  {
1234  nRunAdvance += pGlyphs[n]->m_nNewWidth;
1235 
1236  // proceed to next glyph
1237  nStartOld[n] = nStartNew[n];
1238  int nOrigCharPos = pGlyphs[n]->charPos();
1239  bValid[n] = mpLayouts[n]->GetNextGlyph(&pGlyphs[n], aPos, nStartNew[n]);
1240  // break after last glyph of active layout
1241  if( !bValid[n] )
1242  {
1243  // performance optimization (when a fallback layout is no longer needed)
1244  if( n >= nLevel-1 )
1245  --nLevel;
1246  break;
1247  }
1248 
1249  //If the next character is one which belongs to the next level, then we
1250  //are finished here for now, and we'll pick up after the next level has
1251  //been processed
1252  if ((n+1 < nLevel) && (pGlyphs[n]->charPos() != nOrigCharPos))
1253  {
1254  if (nOrigCharPos < pGlyphs[n]->charPos())
1255  {
1256  if (pGlyphs[n+1]->charPos() > nOrigCharPos && (pGlyphs[n+1]->charPos() < pGlyphs[n]->charPos()))
1257  break;
1258  }
1259  else if (nOrigCharPos > pGlyphs[n]->charPos())
1260  {
1261  if (pGlyphs[n+1]->charPos() > pGlyphs[n]->charPos() && (pGlyphs[n+1]->charPos() < nOrigCharPos))
1262  break;
1263  }
1264  }
1265 
1266  // break at end of layout run
1267  if( n > 0 )
1268  {
1269  // skip until end of fallback run
1270  if (!maFallbackRuns[n-1].PosIsInRun(pGlyphs[n]->charPos()))
1271  break;
1272  }
1273  else
1274  {
1275  // break when a fallback is needed and available
1276  bool bNeedFallback = maFallbackRuns[0].PosIsInRun(pGlyphs[nFirstValid]->charPos());
1277  if( bNeedFallback )
1278  if (!maFallbackRuns[nLevel-1].PosIsInRun(pGlyphs[nFirstValid]->charPos()))
1279  break;
1280  // break when change from resolved to unresolved base layout run
1281  if( bKeepNotDef && !bNeedFallback )
1282  { maFallbackRuns[0].NextRun(); break; }
1283  bKeepNotDef = bNeedFallback;
1284  }
1285  // check for reordered glyphs
1286  if (aMultiArgs.mpDXArray &&
1287  nRunVisibleEndChar < mnEndCharPos &&
1288  nRunVisibleEndChar >= mnMinCharPos &&
1289  pGlyphs[n]->charPos() < mnEndCharPos &&
1290  pGlyphs[n]->charPos() >= mnMinCharPos)
1291  {
1292  if (vRtl[nActiveCharPos - mnMinCharPos])
1293  {
1294  if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1295  >= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
1296  {
1297  nRunVisibleEndChar = pGlyphs[n]->charPos();
1298  }
1299  }
1300  else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1301  <= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
1302  {
1303  nRunVisibleEndChar = pGlyphs[n]->charPos();
1304  }
1305  }
1306  }
1307 
1308  // if a justification array is available
1309  // => use it directly to calculate the corresponding run width
1310  if( aMultiArgs.mpDXArray )
1311  {
1312  // the run advance is the width from the first char
1313  // in the run to the first char in the next run
1314  nRunAdvance = 0;
1315  nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1316  if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
1317  {
1318  if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1319  nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1320  if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1321  nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
1322  }
1323  else
1324  {
1325  if (nRunVisibleEndChar >= mnMinCharPos)
1326  nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1327  if (nLastRunEndChar >= mnMinCharPos)
1328  nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
1329  }
1330  nLastRunEndChar = nRunVisibleEndChar;
1331  nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
1332  // the requested width is still in pixel units
1333  // => convert it to base level font units
1334  nRunAdvance *= mnUnitsPerPixel;
1335  }
1336  else
1337  {
1338  // the measured width is still in fallback font units
1339  // => convert it to base level font units
1340  if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1341  nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
1342  }
1343 
1344  // calculate new x position (in base level units)
1345  nXPos += nRunAdvance;
1346 
1347  // prepare for next fallback run
1348  nActiveCharPos = pGlyphs[nFirstValid]->charPos();
1349  // it essential that the runs don't get ahead of themselves and in the
1350  // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1351  // have already been reached on the base level
1352  for( int i = nFBLevel; --i >= 0;)
1353  {
1354  if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1355  {
1356  if (bRtl)
1357  {
1358  if (nRunStart > nActiveCharPos)
1360  }
1361  else
1362  {
1363  if (nRunEnd <= nActiveCharPos)
1365  }
1366  }
1367  }
1368  }
1369 
1370  mpLayouts[0]->Simplify( true );
1371 }
1372 
1374 {
1375  if( mnLevel > 0 )
1376  mpLayouts[0]->InitFont();
1377 }
1378 
1379 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1380 {
1381  for( int i = mnLevel; --i >= 0; )
1382  {
1383  SalLayout& rLayout = *mpLayouts[ i ];
1384  rLayout.DrawBase() += maDrawBase;
1385  rLayout.DrawOffset() += maDrawOffset;
1386  rLayout.InitFont();
1387  rLayout.DrawText( rGraphics );
1388  rLayout.DrawOffset() -= maDrawOffset;
1389  rLayout.DrawBase() -= maDrawBase;
1390  }
1391  // NOTE: now the baselevel font is active again
1392 }
1393 
1394 sal_Int32 MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1395 {
1396  if( mnLevel <= 0 )
1397  return -1;
1398  if( mnLevel == 1 )
1399  return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1400 
1401  int nCharCount = mnEndCharPos - mnMinCharPos;
1402  std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[nCharCount]);
1403  std::unique_ptr<DeviceCoordinate[]> const pFallbackCharWidths(new DeviceCoordinate[nCharCount]);
1404  mpLayouts[0]->FillDXArray( pCharWidths.get() );
1405 
1406  for( int n = 1; n < mnLevel; ++n )
1407  {
1408  SalLayout& rLayout = *mpLayouts[ n ];
1409  rLayout.FillDXArray( pFallbackCharWidths.get() );
1410  double fUnitMul = mnUnitsPerPixel;
1411  fUnitMul /= rLayout.GetUnitsPerPixel();
1412  for( int i = 0; i < nCharCount; ++i )
1413  {
1414  if( pCharWidths[ i ] == 0 )
1415  {
1416  DeviceCoordinate w = pFallbackCharWidths[i];
1417  w = static_cast<DeviceCoordinate>(w * fUnitMul + 0.5);
1418  pCharWidths[ i ] = w;
1419  }
1420  }
1421  }
1422 
1423  DeviceCoordinate nWidth = 0;
1424  for( int i = 0; i < nCharCount; ++i )
1425  {
1426  nWidth += pCharWidths[ i ] * nFactor;
1427  if( nWidth > nMaxWidth )
1428  return (i + mnMinCharPos);
1429  nWidth += nCharExtra;
1430  }
1431 
1432  return -1;
1433 }
1434 
1436 {
1437  DeviceCoordinate nMaxWidth = 0;
1438 
1439  // prepare merging of fallback levels
1440  std::unique_ptr<DeviceCoordinate[]> pTempWidths;
1441  const int nCharCount = mnEndCharPos - mnMinCharPos;
1442  if( pCharWidths )
1443  {
1444  for( int i = 0; i < nCharCount; ++i )
1445  pCharWidths[i] = 0;
1446  pTempWidths.reset(new DeviceCoordinate[nCharCount]);
1447  }
1448 
1449  for( int n = mnLevel; --n >= 0; )
1450  {
1451  // query every fallback level
1452  DeviceCoordinate nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths.get() );
1453  if( !nTextWidth )
1454  continue;
1455  // merge results from current level
1456  double fUnitMul = mnUnitsPerPixel;
1457  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1458  nTextWidth = static_cast<DeviceCoordinate>(nTextWidth * fUnitMul + 0.5);
1459  if( nMaxWidth < nTextWidth )
1460  nMaxWidth = nTextWidth;
1461  if( !pCharWidths )
1462  continue;
1463  // calculate virtual char widths using most probable fallback layout
1464  for( int i = 0; i < nCharCount; ++i )
1465  {
1466  // #i17359# restriction:
1467  // one char cannot be resolved from different fallbacks
1468  if( pCharWidths[i] != 0 )
1469  continue;
1470  DeviceCoordinate nCharWidth = pTempWidths[i];
1471  if( !nCharWidth )
1472  continue;
1473  nCharWidth = static_cast<DeviceCoordinate>(nCharWidth * fUnitMul + 0.5);
1474  pCharWidths[i] = nCharWidth;
1475  }
1476  }
1477 
1478  return nMaxWidth;
1479 }
1480 
1481 void MultiSalLayout::GetCaretPositions( int nMaxIndex, long* pCaretXArray ) const
1482 {
1483  SalLayout& rLayout = *mpLayouts[ 0 ];
1484  rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
1485 
1486  if( mnLevel <= 1 )
1487  return;
1488 
1489  std::unique_ptr<long[]> const pTempPos(new long[nMaxIndex]);
1490  for( int n = 1; n < mnLevel; ++n )
1491  {
1492  mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos.get() );
1493  double fUnitMul = mnUnitsPerPixel;
1494  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1495  for( int i = 0; i < nMaxIndex; ++i )
1496  if( pTempPos[i] >= 0 )
1497  {
1498  long w = pTempPos[i];
1499  w = static_cast<long>(w*fUnitMul + 0.5);
1500  pCaretXArray[i] = w;
1501  }
1502  }
1503 }
1504 
1506  Point& rPos, int& nStart,
1507  const PhysicalFontFace** pFallbackFont) const
1508 {
1509  // NOTE: nStart is tagged with current font index
1510  int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
1511  nStart &= ~GF_FONTMASK;
1512  for(; nLevel < mnLevel; ++nLevel, nStart=0 )
1513  {
1514  GenericSalLayout& rLayout = *mpLayouts[ nLevel ];
1515  rLayout.InitFont();
1516  const PhysicalFontFace* pFontFace = rLayout.GetFont().GetFontFace();
1517  if (rLayout.GetNextGlyph(pGlyph, rPos, nStart))
1518  {
1519  int nFontTag = nLevel << GF_FONTSHIFT;
1520  nStart |= nFontTag;
1521  if (pFallbackFont)
1522  *pFallbackFont = pFontFace;
1523  rPos += maDrawBase;
1524  rPos += maDrawOffset;
1525  return true;
1526  }
1527  }
1528 
1529  // #111016# reset to base level font when done
1530  mpLayouts[0]->InitFont();
1531  return false;
1532 }
1533 
1535 {
1536  bool bRet = false;
1537 
1538  for( int i = mnLevel; --i >= 0; )
1539  {
1540  SalLayout& rLayout = *mpLayouts[ i ];
1541  rLayout.DrawBase() = maDrawBase;
1542  rLayout.DrawOffset() += maDrawOffset;
1543  rLayout.InitFont();
1544  bRet |= rLayout.GetOutline(rPPV);
1545  rLayout.DrawOffset() -= maDrawOffset;
1546  }
1547 
1548  return bRet;
1549 }
1550 
1551 bool MultiSalLayout::IsKashidaPosValid(int nCharPos) const
1552 {
1553  // Check the base layout
1554  bool bValid = mpLayouts[0]->IsKashidaPosValid(nCharPos);
1555 
1556  // If base layout returned false, it might be because the character was not
1557  // supported there, so we check fallback layouts.
1558  if (!bValid)
1559  {
1560  for (int i = 1; i < mnLevel; ++i)
1561  {
1562  // - 1 because there is no fallback run for the base layout, IIUC.
1563  if (maFallbackRuns[i - 1].PosIsInAnyRun(nCharPos))
1564  {
1565  bValid = mpLayouts[i]->IsKashidaPosValid(nCharPos);
1566  break;
1567  }
1568  }
1569  }
1570 
1571  return bValid;
1572 }
1573 
1575 {
1576  // No access to the glyphs by default.
1577  return nullptr;
1578 }
1579 
1580 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DeviceCoordinate GetTextWidth() const final override
Definition: sallayout.cxx:659
const size_t count(pCandidateA->getBorderLines().size())
sal_Int32 nIndex
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:1009
int GetUnitsPerPixel() const
Definition: vcllayout.hxx:80
#define LANGUAGE_ARABIC_SAUDI_ARABIA
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:196
#define LANGUAGE_URDU_PAKISTAN
sal_Int16 mnLevel
bool GetOutline(basegfx::B2DPolyPolygonVector &) const override
Definition: sallayout.cxx:1534
std::unique_ptr< GenericSalLayout > mpLayouts[MAX_FALLBACK]
Definition: sallayout.hxx:153
#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:1373
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_Int64 n
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:933
#define LANGUAGE_TAMIL
LogicalFontInstance & GetFont() const
Definition: sallayout.hxx:182
MultiSalLayout(std::unique_ptr< SalLayout > pBaseLayout)
Definition: sallayout.cxx:988
Point maDrawBase
Definition: vcllayout.hxx:114
bool IsEmpty() const
FuncFlags mnFlags
#define LANGUAGE_GUJARATI
sal_uInt16 sal_Unicode
std::vector< int > maRuns
Definition: sallayout.hxx:57
void SetIncomplete(bool bIncomplete)
Definition: sallayout.cxx:999
#define LANGUAGE_ODIA
void AddPos(int nCharPos, bool bRTL)
Definition: sallayout.cxx:231
bool IsEmpty() const
Definition: sallayout.hxx:66
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
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:1551
#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
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:1481
abstract base class for physical font faces
const OUString & mrStr
Definition: sallayout.hxx:81
int mnEndCharPos
Definition: vcllayout.hxx:108
virtual ~SalLayout()
Definition: sallayout.cxx:550
TEST
virtual void InitFont() const
Definition: vcllayout.hxx:77
int i
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
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr) const override
Definition: sallayout.cxx:1505
bool PosIsInRun(int nCharPos) const
Definition: sallayout.cxx:281
void GetCharWidths(DeviceCoordinate *pCharWidths) const
void DropGlyph(int nStart)
Definition: sallayout.cxx:958
void transform(const basegfx::B2DHomMatrix &rMatrix)
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr) const override
Definition: sallayout.cxx:896
#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:1005
#define LANGUAGE_MALAYALAM
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
Definition: sallayout.cxx:535
#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:110
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:1030
SalLayoutFlags
Definition: outdev.hxx:119
int mnOrientation
Definition: vcllayout.hxx:111
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
tools::Rectangle & Union(const tools::Rectangle &rRect)
#define SAL_WARN_IF(condition, area, stream)
void DrawText(SalGraphics &) const override
Definition: sallayout.cxx:1379
bool LayoutText(ImplLayoutArgs &, const SalLayoutGlyphs *) override
Definition: sallayout.cxx:1021
#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
SalLayoutFlags mnFlags
Definition: sallayout.hxx:80
LanguageTag maLanguageTag
#define LANGUAGE_BENGALI
SalLayoutGlyphs m_GlyphItems
Definition: sallayout.hxx:211
ImplLayoutRuns maFallbackRuns
Definition: sallayout.hxx:95
ImplLayoutRuns maFallbackRuns[MAX_FALLBACK]
Definition: sallayout.hxx:154
virtual bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr) const =0
bool PosIsInAnyRun(int nCharPos) const
Definition: sallayout.cxx:302
DeviceCoordinate FillDXArray(DeviceCoordinate *pDXArray) const override
Definition: sallayout.cxx:1435
int mnMinCharPos
Definition: vcllayout.hxx:107
sal_Int32 nLength
Point maDrawOffset
Definition: vcllayout.hxx:113
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const final override
Definition: sallayout.cxx:878
#define LANGUAGE_KANNADA
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
bool GetGlyphBoundRect(tools::Rectangle &) const
virtual const SalLayoutGlyphs * GetGlyphs() const
Definition: sallayout.cxx:1574
void Simplify(bool bIsBase)
Definition: sallayout.cxx:968
Point & DrawOffset()
Definition: vcllayout.hxx:71
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override
Definition: sallayout.cxx:1394
const DeviceCoordinate * mpDXArray
Definition: sallayout.hxx:89
bool anyOf(strong_int v) const
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo