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_deg10 )
566  aPos += aOfs;
567  else
568  {
569  // cache trigonometric results
570  static Degree10 nOldOrientation(0);
571  static double fCos = 1.0, fSin = 0.0;
572  if( nOldOrientation != mnOrientation )
573  {
574  nOldOrientation = mnOrientation;
575  double fRad = toRadians(mnOrientation);
576  fCos = cos( fRad );
577  fSin = sin( fRad );
578  }
579 
580  double fX = aOfs.X();
581  double fY = aOfs.Y();
582  tools::Long nX = static_cast<tools::Long>( +fCos * fX + fSin * fY );
583  tools::Long nY = static_cast<tools::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  return SalLayoutGlyphs(); // invalid
653 }
654 
656 {
657  if (pCharWidths)
658  GetCharWidths(pCharWidths);
659 
660  return GetTextWidth();
661 }
662 
663 // the text width is the maximum logical extent of all glyphs
665 {
666  if (!m_GlyphItems.IsValid())
667  return 0;
668 
669  // initialize the extent
670  DeviceCoordinate nMinPos = 0;
671  DeviceCoordinate nMaxPos = 0;
672 
673  for (auto const& aGlyphItem : m_GlyphItems)
674  {
675  // update the text extent with the glyph extent
676  DeviceCoordinate nXPos = aGlyphItem.m_aLinearPos.getX();
677  if( nMinPos > nXPos )
678  nMinPos = nXPos;
679  nXPos += aGlyphItem.m_nNewWidth - aGlyphItem.xOffset();
680  if( nMaxPos < nXPos )
681  nMaxPos = nXPos;
682  }
683 
684  DeviceCoordinate nWidth = nMaxPos - nMinPos;
685  return nWidth;
686 }
687 
689 {
690  nNewWidth *= mnUnitsPerPixel;
691  DeviceCoordinate nOldWidth = GetTextWidth();
692  if( !nOldWidth || nNewWidth==nOldWidth )
693  return;
694 
695  if (!m_GlyphItems.IsValid())
696  {
697  return;
698  }
699  // find rightmost glyph, it won't get stretched
700  std::vector<GlyphItem>::iterator pGlyphIterRight = m_GlyphItems.begin();
701  pGlyphIterRight += m_GlyphItems.size() - 1;
702  std::vector<GlyphItem>::iterator pGlyphIter;
703  // count stretchable glyphs
704  int nStretchable = 0;
705  int nMaxGlyphWidth = 0;
706  for(pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter)
707  {
708  if( !pGlyphIter->IsDiacritic() )
709  ++nStretchable;
710  if (nMaxGlyphWidth < pGlyphIter->origWidth())
711  nMaxGlyphWidth = pGlyphIter->origWidth();
712  }
713 
714  // move rightmost glyph to requested position
715  nOldWidth -= pGlyphIterRight->origWidth();
716  if( nOldWidth <= 0 )
717  return;
718  if( nNewWidth < nMaxGlyphWidth)
719  nNewWidth = nMaxGlyphWidth;
720  nNewWidth -= pGlyphIterRight->origWidth();
721  pGlyphIterRight->m_aLinearPos.setX( nNewWidth );
722 
723  // justify glyph widths and positions
724  int nDiffWidth = nNewWidth - nOldWidth;
725  if( nDiffWidth >= 0) // expanded case
726  {
727  // expand width by distributing space between glyphs evenly
728  int nDeltaSum = 0;
729  for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
730  {
731  // move glyph to justified position
732  pGlyphIter->m_aLinearPos.AdjustX(nDeltaSum );
733 
734  // do not stretch non-stretchable glyphs
735  if( pGlyphIter->IsDiacritic() || (nStretchable <= 0) )
736  continue;
737 
738  // distribute extra space equally to stretchable glyphs
739  int nDeltaWidth = nDiffWidth / nStretchable--;
740  nDiffWidth -= nDeltaWidth;
741  pGlyphIter->m_nNewWidth += nDeltaWidth;
742  nDeltaSum += nDeltaWidth;
743  }
744  }
745  else // condensed case
746  {
747  // squeeze width by moving glyphs proportionally
748  double fSqueeze = static_cast<double>(nNewWidth) / nOldWidth;
749  if(m_GlyphItems.size() > 1)
750  {
751  for( pGlyphIter = m_GlyphItems.begin(); ++pGlyphIter != pGlyphIterRight;)
752  {
753  int nX = pGlyphIter->m_aLinearPos.getX();
754  nX = static_cast<int>(nX * fSqueeze);
755  pGlyphIter->m_aLinearPos.setX( nX );
756  }
757  }
758  // adjust glyph widths to new positions
759  for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
760  pGlyphIter->m_nNewWidth = pGlyphIter[1].m_aLinearPos.getX() - pGlyphIter[0].m_aLinearPos.getX();
761  }
762 }
763 
764 // returns asian kerning values in quarter of character width units
765 // to enable automatic halfwidth substitution for fullwidth punctuation
766 // return value is negative for l, positive for r, zero for neutral
767 // TODO: handle vertical layout as proposed in commit 43bf2ad49c2b3989bbbe893e4fee2e032a3920f5?
768 static int lcl_CalcAsianKerning(sal_UCS4 c, bool bLeft)
769 {
770  // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
771  static const signed char nTable[0x30] =
772  {
773  0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
774  +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
775  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
776  };
777 
778  int nResult = 0;
779  if( (c >= 0x3000) && (c < 0x3030) )
780  nResult = nTable[ c - 0x3000 ];
781  else switch( c )
782  {
783  case 0x30FB:
784  nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
785  break;
786  case 0x2019: case 0x201D:
787  case 0xFF01: case 0xFF09: case 0xFF0C:
788  case 0xFF1A: case 0xFF1B:
789  nResult = -2;
790  break;
791  case 0x2018: case 0x201C:
792  case 0xFF08:
793  nResult = +2;
794  break;
795  default:
796  break;
797  }
798 
799  return nResult;
800 }
801 
803 {
804  return (0x3000 == (cp & 0xFF00)) || (0xFF00 == (cp & 0xFF00)) || (0x2010 == (cp & 0xFFF0));
805 }
806 
807 void GenericSalLayout::ApplyAsianKerning(const OUString& rStr)
808 {
809  const int nLength = rStr.getLength();
810  tools::Long nOffset = 0;
811 
812  for (std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin(),
813  pGlyphIterEnd = m_GlyphItems.end();
814  pGlyphIter != pGlyphIterEnd; ++pGlyphIter)
815  {
816  const int n = pGlyphIter->charPos();
817  if (n < nLength - 1)
818  {
819  // ignore code ranges that are not affected by asian punctuation compression
820  const sal_Unicode cCurrent = rStr[n];
821  if (!lcl_CanApplyAsianKerning(cCurrent))
822  continue;
823  const sal_Unicode cNext = rStr[n + 1];
824  if (!lcl_CanApplyAsianKerning(cNext))
825  continue;
826 
827  // calculate compression values
828  const int nKernCurrent = +lcl_CalcAsianKerning(cCurrent, true);
829  if (nKernCurrent == 0)
830  continue;
831  const int nKernNext = -lcl_CalcAsianKerning(cNext, false);
832  if (nKernNext == 0)
833  continue;
834 
835  // apply punctuation compression to logical glyph widths
836  int nDelta = (nKernCurrent < nKernNext) ? nKernCurrent : nKernNext;
837  if (nDelta < 0)
838  {
839  nDelta = (nDelta * pGlyphIter->origWidth() + 2) / 4;
840  if( pGlyphIter+1 == pGlyphIterEnd )
841  pGlyphIter->m_nNewWidth += nDelta;
842  nOffset += nDelta;
843  }
844  }
845 
846  // adjust the glyph positions to the new glyph widths
847  if( pGlyphIter+1 != pGlyphIterEnd )
848  pGlyphIter->m_aLinearPos.AdjustX(nOffset);
849  }
850 }
851 
852 void GenericSalLayout::GetCaretPositions( int nMaxIndex, tools::Long* pCaretXArray ) const
853 {
854  // initialize result array
855  for (int i = 0; i < nMaxIndex; ++i)
856  pCaretXArray[i] = -1;
857 
858  // calculate caret positions using glyph array
859  for (auto const& aGlyphItem : m_GlyphItems)
860  {
861  tools::Long nXPos = aGlyphItem.m_aLinearPos.getX();
862  tools::Long nXRight = nXPos + aGlyphItem.origWidth();
863  int n = aGlyphItem.charPos();
864  int nCurrIdx = 2 * (n - mnMinCharPos);
865  // tdf#86399 if this is not the start of a cluster, don't overwrite the caret bounds of the cluster start
866  if (aGlyphItem.IsInCluster() && pCaretXArray[nCurrIdx] != -1)
867  continue;
868  if (!aGlyphItem.IsRTLGlyph() )
869  {
870  // normal positions for LTR case
871  pCaretXArray[ nCurrIdx ] = nXPos;
872  pCaretXArray[ nCurrIdx+1 ] = nXRight;
873  }
874  else
875  {
876  // reverse positions for RTL case
877  pCaretXArray[ nCurrIdx ] = nXRight;
878  pCaretXArray[ nCurrIdx+1 ] = nXPos;
879  }
880  }
881 }
882 
883 sal_Int32 GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
884 {
885  int nCharCapacity = mnEndCharPos - mnMinCharPos;
886  std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[nCharCapacity]);
887  GetCharWidths(pCharWidths.get());
888 
889  DeviceCoordinate nWidth = 0;
890  for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
891  {
892  nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
893  if( nWidth > nMaxWidth )
894  return i;
895  nWidth += nCharExtra;
896  }
897 
898  return -1;
899 }
900 
902  Point& rPos, int& nStart,
903  const PhysicalFontFace**) const
904 {
905  std::vector<GlyphItem>::const_iterator pGlyphIter = m_GlyphItems.begin();
906  std::vector<GlyphItem>::const_iterator pGlyphIterEnd = m_GlyphItems.end();
907  pGlyphIter += nStart;
908 
909  // find next glyph in substring
910  for(; pGlyphIter != pGlyphIterEnd; ++nStart, ++pGlyphIter )
911  {
912  int n = pGlyphIter->charPos();
913  if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
914  break;
915  }
916 
917  // return zero if no more glyph found
918  if( nStart >= static_cast<int>(m_GlyphItems.size()) )
919  return false;
920 
921  if( pGlyphIter == pGlyphIterEnd )
922  return false;
923 
924  // update return data with glyph info
925  *pGlyph = &(*pGlyphIter);
926  ++nStart;
927 
928  // calculate absolute position in pixel units
929  Point aRelativePos = pGlyphIter->m_aLinearPos;
930 
931  aRelativePos.setX( aRelativePos.X() / mnUnitsPerPixel );
932  aRelativePos.setY( aRelativePos.Y() / mnUnitsPerPixel );
933  rPos = GetDrawPosition( aRelativePos );
934 
935  return true;
936 }
937 
938 void GenericSalLayout::MoveGlyph( int nStart, tools::Long nNewXPos )
939 {
940  if( nStart >= static_cast<int>(m_GlyphItems.size()) )
941  return;
942 
943  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
944  pGlyphIter += nStart;
945 
946  // the nNewXPos argument determines the new cell position
947  // as RTL-glyphs are right justified in their cell
948  // the cell position needs to be adjusted to the glyph position
949  if( pGlyphIter->IsRTLGlyph() )
950  nNewXPos += pGlyphIter->m_nNewWidth - pGlyphIter->origWidth();
951  // calculate the x-offset to the old position
952  tools::Long nXDelta = nNewXPos - pGlyphIter->m_aLinearPos.getX();
953  // adjust all following glyph positions if needed
954  if( nXDelta != 0 )
955  {
956  for( std::vector<GlyphItem>::iterator pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
957  {
958  pGlyphIter->m_aLinearPos.AdjustX(nXDelta );
959  }
960  }
961 }
962 
963 void GenericSalLayout::DropGlyph( int nStart )
964 {
965  if( nStart >= static_cast<int>(m_GlyphItems.size()))
966  return;
967 
968  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
969  pGlyphIter += nStart;
970  pGlyphIter->dropGlyph();
971 }
972 
973 void GenericSalLayout::Simplify( bool bIsBase )
974 {
975  // remove dropped glyphs inplace
976  size_t j = 0;
977  for(size_t i = 0; i < m_GlyphItems.size(); i++ )
978  {
979  if (bIsBase && m_GlyphItems[i].IsDropped())
980  continue;
981  if (!bIsBase && m_GlyphItems[i].glyphId() == 0)
982  continue;
983 
984  if( i != j )
985  {
987  }
988  j += 1;
989  }
990  m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
991 }
992 
993 MultiSalLayout::MultiSalLayout( std::unique_ptr<SalLayout> pBaseLayout )
994 : SalLayout()
995 , mnLevel( 1 )
996 , mbIncomplete( false )
997 {
998  assert(dynamic_cast<GenericSalLayout*>(pBaseLayout.get()));
999 
1000  mpLayouts[ 0 ].reset(static_cast<GenericSalLayout*>(pBaseLayout.release()));
1001  mnUnitsPerPixel = mpLayouts[ 0 ]->GetUnitsPerPixel();
1002 }
1003 
1004 std::unique_ptr<SalLayout> MultiSalLayout::ReleaseBaseLayout()
1005 {
1006  return std::move(mpLayouts[0]);
1007 }
1008 
1009 void MultiSalLayout::SetIncomplete(bool bIncomplete)
1010 {
1011  mbIncomplete = bIncomplete;
1013 }
1014 
1016 {
1017 }
1018 
1019 void MultiSalLayout::AddFallback( std::unique_ptr<SalLayout> pFallback,
1020  ImplLayoutRuns const & rFallbackRuns)
1021 {
1022  assert(dynamic_cast<GenericSalLayout*>(pFallback.get()));
1023  if( mnLevel >= MAX_FALLBACK )
1024  return;
1025 
1026  mpLayouts[ mnLevel ].reset(static_cast<GenericSalLayout*>(pFallback.release()));
1027  maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
1028  ++mnLevel;
1029 }
1030 
1032 {
1033  if( mnLevel <= 1 )
1034  return false;
1035  if (!mbIncomplete)
1036  maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
1037  return true;
1038 }
1039 
1041 {
1042  SalLayout::AdjustLayout( rArgs );
1043  ImplLayoutArgs aMultiArgs = rArgs;
1044  std::unique_ptr<DeviceCoordinate[]> pJustificationArray;
1045 
1046  if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
1047  {
1048  // for stretched text in a MultiSalLayout the target width needs to be
1049  // distributed by individually adjusting its virtual character widths
1050  DeviceCoordinate nTargetWidth = aMultiArgs.mnLayoutWidth;
1051  nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
1052  aMultiArgs.mnLayoutWidth = 0;
1053 
1054  // we need to get the original unmodified layouts ready
1055  for( int n = 0; n < mnLevel; ++n )
1056  mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
1057  // then we can measure the unmodified metrics
1058  int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
1059  pJustificationArray.reset(new DeviceCoordinate[nCharCount]);
1060  FillDXArray( pJustificationArray.get() );
1061  // #i17359# multilayout is not simplified yet, so calculating the
1062  // unjustified width needs handholding; also count the number of
1063  // stretchable virtual char widths
1064  DeviceCoordinate nOrigWidth = 0;
1065  int nStretchable = 0;
1066  for( int i = 0; i < nCharCount; ++i )
1067  {
1068  // convert array from widths to sum of widths
1069  nOrigWidth += pJustificationArray[i];
1070  if( pJustificationArray[i] > 0 )
1071  ++nStretchable;
1072  }
1073 
1074  // now we are able to distribute the extra width over the virtual char widths
1075  if( nOrigWidth && (nTargetWidth != nOrigWidth) )
1076  {
1077  DeviceCoordinate nDiffWidth = nTargetWidth - nOrigWidth;
1078  DeviceCoordinate nWidthSum = 0;
1079  for( int i = 0; i < nCharCount; ++i )
1080  {
1081  DeviceCoordinate nJustWidth = pJustificationArray[i];
1082  if( (nJustWidth > 0) && (nStretchable > 0) )
1083  {
1084  DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
1085  nJustWidth += nDeltaWidth;
1086  nDiffWidth -= nDeltaWidth;
1087  --nStretchable;
1088  }
1089  nWidthSum += nJustWidth;
1090  pJustificationArray[i] = nWidthSum;
1091  }
1092  if( nWidthSum != nTargetWidth )
1093  pJustificationArray[ nCharCount-1 ] = nTargetWidth;
1094 
1095  // the justification array is still in base level units
1096  // => convert it to pixel units
1097  if( mnUnitsPerPixel > 1 )
1098  {
1099  for( int i = 0; i < nCharCount; ++i )
1100  {
1101  DeviceCoordinate nVal = pJustificationArray[ i ];
1102  nVal += (mnUnitsPerPixel + 1) / 2;
1103  pJustificationArray[ i ] = nVal / mnUnitsPerPixel;
1104  }
1105  }
1106 
1107  // change the mpDXArray temporarily (just for the justification)
1108  aMultiArgs.mpDXArray = pJustificationArray.get();
1109  }
1110  }
1111 
1112  // Compute rtl flags, since in some scripts glyphs/char order can be
1113  // reversed for a few character sequences e.g. Myanmar
1114  std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
1115  rArgs.ResetPos();
1116  bool bRtl;
1117  int nRunStart, nRunEnd;
1118  while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
1119  {
1120  if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
1121  vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
1122  }
1123  rArgs.ResetPos();
1124 
1125  // prepare "merge sort"
1126  int nStartOld[ MAX_FALLBACK ];
1127  int nStartNew[ MAX_FALLBACK ];
1128  const GlyphItem* pGlyphs[MAX_FALLBACK];
1129  bool bValid[MAX_FALLBACK] = { false };
1130 
1131  Point aPos;
1132  int nLevel = 0, n;
1133  for( n = 0; n < mnLevel; ++n )
1134  {
1135  // now adjust the individual components
1136  if( n > 0 )
1137  {
1138  aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
1139  aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback;
1140  }
1141  mpLayouts[n]->AdjustLayout( aMultiArgs );
1142 
1143  // remove unused parts of component
1144  if( n > 0 )
1145  {
1146  if (mbIncomplete && (n == mnLevel-1))
1147  mpLayouts[n]->Simplify( true );
1148  else
1149  mpLayouts[n]->Simplify( false );
1150  }
1151 
1152  // prepare merging components
1153  nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
1154  bValid[nLevel] = mpLayouts[n]->GetNextGlyph(&pGlyphs[nLevel], aPos, nStartNew[nLevel]);
1155 
1156  if( (n > 0) && !bValid[ nLevel ] )
1157  {
1158  // an empty fallback layout can be released
1159  mpLayouts[n].reset();
1160  }
1161  else
1162  {
1163  // reshuffle used fallbacks if needed
1164  if( nLevel != n )
1165  {
1166  mpLayouts[ nLevel ] = std::move(mpLayouts[ n ]);
1167  maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
1168  }
1169  ++nLevel;
1170  }
1171  }
1172  mnLevel = nLevel;
1173 
1174  // prepare merge the fallback levels
1175  tools::Long nXPos = 0;
1176  double fUnitMul = 1.0;
1177  for( n = 0; n < nLevel; ++n )
1178  maFallbackRuns[n].ResetPos();
1179 
1180  int nFirstValid = -1;
1181  for( n = 0; n < nLevel; ++n )
1182  {
1183  if(bValid[n])
1184  {
1185  nFirstValid = n;
1186  break;
1187  }
1188  }
1189  assert(nFirstValid >= 0);
1190 
1191  // get the next codepoint index that needs fallback
1192  int nActiveCharPos = pGlyphs[nFirstValid]->charPos();
1193  int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1194  // get the end index of the active run
1195  int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
1196  rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
1197  int nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
1198  // merge the fallback levels
1199  while( bValid[nFirstValid] && (nLevel > 0))
1200  {
1201  // find best fallback level
1202  for( n = 0; n < nLevel; ++n )
1203  if( bValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
1204  // fallback level n wins when it requested no further fallback
1205  break;
1206  int nFBLevel = n;
1207 
1208  if( n < nLevel )
1209  {
1210  // use base(n==0) or fallback(n>=1) level
1211  fUnitMul = mnUnitsPerPixel;
1212  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1213  tools::Long nNewPos = static_cast<tools::Long>(nXPos/fUnitMul + 0.5);
1214  mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
1215  }
1216  else
1217  {
1218  n = 0; // keep NotDef in base level
1219  fUnitMul = 1.0;
1220  }
1221 
1222  if( n > 0 )
1223  {
1224  // drop the NotDef glyphs in the base layout run if a fallback run exists
1225  while (
1226  (maFallbackRuns[n-1].PosIsInRun(pGlyphs[nFirstValid]->charPos())) &&
1227  (!maFallbackRuns[n].PosIsInAnyRun(pGlyphs[nFirstValid]->charPos()))
1228  )
1229  {
1230  mpLayouts[0]->DropGlyph( nStartOld[0] );
1231  nStartOld[0] = nStartNew[0];
1232  bValid[nFirstValid] = mpLayouts[0]->GetNextGlyph(&pGlyphs[nFirstValid], aPos, nStartNew[0]);
1233 
1234  if( !bValid[nFirstValid] )
1235  break;
1236  }
1237  }
1238 
1239  // skip to end of layout run and calculate its advance width
1240  DeviceCoordinate nRunAdvance = 0;
1241  bool bKeepNotDef = (nFBLevel >= nLevel);
1242  for(;;)
1243  {
1244  nRunAdvance += pGlyphs[n]->m_nNewWidth;
1245 
1246  // proceed to next glyph
1247  nStartOld[n] = nStartNew[n];
1248  int nOrigCharPos = pGlyphs[n]->charPos();
1249  bValid[n] = mpLayouts[n]->GetNextGlyph(&pGlyphs[n], aPos, nStartNew[n]);
1250  // break after last glyph of active layout
1251  if( !bValid[n] )
1252  {
1253  // performance optimization (when a fallback layout is no longer needed)
1254  if( n >= nLevel-1 )
1255  --nLevel;
1256  break;
1257  }
1258 
1259  //If the next character is one which belongs to the next level, then we
1260  //are finished here for now, and we'll pick up after the next level has
1261  //been processed
1262  if ((n+1 < nLevel) && (pGlyphs[n]->charPos() != nOrigCharPos))
1263  {
1264  if (nOrigCharPos < pGlyphs[n]->charPos())
1265  {
1266  if (pGlyphs[n+1]->charPos() > nOrigCharPos && (pGlyphs[n+1]->charPos() < pGlyphs[n]->charPos()))
1267  break;
1268  }
1269  else if (nOrigCharPos > pGlyphs[n]->charPos())
1270  {
1271  if (pGlyphs[n+1]->charPos() > pGlyphs[n]->charPos() && (pGlyphs[n+1]->charPos() < nOrigCharPos))
1272  break;
1273  }
1274  }
1275 
1276  // break at end of layout run
1277  if( n > 0 )
1278  {
1279  // skip until end of fallback run
1280  if (!maFallbackRuns[n-1].PosIsInRun(pGlyphs[n]->charPos()))
1281  break;
1282  }
1283  else
1284  {
1285  // break when a fallback is needed and available
1286  bool bNeedFallback = maFallbackRuns[0].PosIsInRun(pGlyphs[nFirstValid]->charPos());
1287  if( bNeedFallback )
1288  if (!maFallbackRuns[nLevel-1].PosIsInRun(pGlyphs[nFirstValid]->charPos()))
1289  break;
1290  // break when change from resolved to unresolved base layout run
1291  if( bKeepNotDef && !bNeedFallback )
1292  { maFallbackRuns[0].NextRun(); break; }
1293  bKeepNotDef = bNeedFallback;
1294  }
1295  // check for reordered glyphs
1296  if (aMultiArgs.mpDXArray &&
1297  nRunVisibleEndChar < mnEndCharPos &&
1298  nRunVisibleEndChar >= mnMinCharPos &&
1299  pGlyphs[n]->charPos() < mnEndCharPos &&
1300  pGlyphs[n]->charPos() >= mnMinCharPos)
1301  {
1302  if (vRtl[nActiveCharPos - mnMinCharPos])
1303  {
1304  if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1305  >= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
1306  {
1307  nRunVisibleEndChar = pGlyphs[n]->charPos();
1308  }
1309  }
1310  else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
1311  <= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
1312  {
1313  nRunVisibleEndChar = pGlyphs[n]->charPos();
1314  }
1315  }
1316  }
1317 
1318  // if a justification array is available
1319  // => use it directly to calculate the corresponding run width
1320  if( aMultiArgs.mpDXArray )
1321  {
1322  // the run advance is the width from the first char
1323  // in the run to the first char in the next run
1324  nRunAdvance = 0;
1325  nActiveCharIndex = nActiveCharPos - mnMinCharPos;
1326  if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
1327  {
1328  if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
1329  nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
1330  if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
1331  nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
1332  }
1333  else
1334  {
1335  if (nRunVisibleEndChar >= mnMinCharPos)
1336  nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
1337  if (nLastRunEndChar >= mnMinCharPos)
1338  nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
1339  }
1340  nLastRunEndChar = nRunVisibleEndChar;
1341  nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
1342  // the requested width is still in pixel units
1343  // => convert it to base level font units
1344  nRunAdvance *= mnUnitsPerPixel;
1345  }
1346  else
1347  {
1348  // the measured width is still in fallback font units
1349  // => convert it to base level font units
1350  if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
1351  nRunAdvance = static_cast<tools::Long>(nRunAdvance*fUnitMul + 0.5);
1352  }
1353 
1354  // calculate new x position (in base level units)
1355  nXPos += nRunAdvance;
1356 
1357  // prepare for next fallback run
1358  nActiveCharPos = pGlyphs[nFirstValid]->charPos();
1359  // it essential that the runs don't get ahead of themselves and in the
1360  // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
1361  // have already been reached on the base level
1362  for( int i = nFBLevel; --i >= 0;)
1363  {
1364  if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
1365  {
1366  if (bRtl)
1367  {
1368  if (nRunStart > nActiveCharPos)
1370  }
1371  else
1372  {
1373  if (nRunEnd <= nActiveCharPos)
1375  }
1376  }
1377  }
1378  }
1379 
1380  mpLayouts[0]->Simplify( true );
1381 }
1382 
1384 {
1385  if( mnLevel > 0 )
1386  mpLayouts[0]->InitFont();
1387 }
1388 
1389 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
1390 {
1391  for( int i = mnLevel; --i >= 0; )
1392  {
1393  SalLayout& rLayout = *mpLayouts[ i ];
1394  rLayout.DrawBase() += maDrawBase;
1395  rLayout.DrawOffset() += maDrawOffset;
1396  rLayout.InitFont();
1397  rLayout.DrawText( rGraphics );
1398  rLayout.DrawOffset() -= maDrawOffset;
1399  rLayout.DrawBase() -= maDrawBase;
1400  }
1401  // NOTE: now the baselevel font is active again
1402 }
1403 
1404 sal_Int32 MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1405 {
1406  if( mnLevel <= 0 )
1407  return -1;
1408  if( mnLevel == 1 )
1409  return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1410 
1411  int nCharCount = mnEndCharPos - mnMinCharPos;
1412  std::unique_ptr<DeviceCoordinate[]> const pCharWidths(new DeviceCoordinate[nCharCount]);
1413  std::unique_ptr<DeviceCoordinate[]> const pFallbackCharWidths(new DeviceCoordinate[nCharCount]);
1414  mpLayouts[0]->FillDXArray( pCharWidths.get() );
1415 
1416  for( int n = 1; n < mnLevel; ++n )
1417  {
1418  SalLayout& rLayout = *mpLayouts[ n ];
1419  rLayout.FillDXArray( pFallbackCharWidths.get() );
1420  double fUnitMul = mnUnitsPerPixel;
1421  fUnitMul /= rLayout.GetUnitsPerPixel();
1422  for( int i = 0; i < nCharCount; ++i )
1423  {
1424  if( pCharWidths[ i ] == 0 )
1425  {
1426  DeviceCoordinate w = pFallbackCharWidths[i];
1427  w = static_cast<DeviceCoordinate>(w * fUnitMul + 0.5);
1428  pCharWidths[ i ] = w;
1429  }
1430  }
1431  }
1432 
1433  DeviceCoordinate nWidth = 0;
1434  for( int i = 0; i < nCharCount; ++i )
1435  {
1436  nWidth += pCharWidths[ i ] * nFactor;
1437  if( nWidth > nMaxWidth )
1438  return (i + mnMinCharPos);
1439  nWidth += nCharExtra;
1440  }
1441 
1442  return -1;
1443 }
1444 
1446 {
1447  DeviceCoordinate nMaxWidth = 0;
1448 
1449  // prepare merging of fallback levels
1450  std::unique_ptr<DeviceCoordinate[]> pTempWidths;
1451  const int nCharCount = mnEndCharPos - mnMinCharPos;
1452  if( pCharWidths )
1453  {
1454  for( int i = 0; i < nCharCount; ++i )
1455  pCharWidths[i] = 0;
1456  pTempWidths.reset(new DeviceCoordinate[nCharCount]);
1457  }
1458 
1459  for( int n = mnLevel; --n >= 0; )
1460  {
1461  // query every fallback level
1462  DeviceCoordinate nTextWidth = mpLayouts[n]->FillDXArray( pTempWidths.get() );
1463  if( !nTextWidth )
1464  continue;
1465  // merge results from current level
1466  double fUnitMul = mnUnitsPerPixel;
1467  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1468  nTextWidth = static_cast<DeviceCoordinate>(nTextWidth * fUnitMul + 0.5);
1469  if( nMaxWidth < nTextWidth )
1470  nMaxWidth = nTextWidth;
1471  if( !pCharWidths )
1472  continue;
1473  // calculate virtual char widths using most probable fallback layout
1474  for( int i = 0; i < nCharCount; ++i )
1475  {
1476  // #i17359# restriction:
1477  // one char cannot be resolved from different fallbacks
1478  if( pCharWidths[i] != 0 )
1479  continue;
1480  DeviceCoordinate nCharWidth = pTempWidths[i];
1481  if( !nCharWidth )
1482  continue;
1483  nCharWidth = static_cast<DeviceCoordinate>(nCharWidth * fUnitMul + 0.5);
1484  pCharWidths[i] = nCharWidth;
1485  }
1486  }
1487 
1488  return nMaxWidth;
1489 }
1490 
1491 void MultiSalLayout::GetCaretPositions( int nMaxIndex, tools::Long* pCaretXArray ) const
1492 {
1493  SalLayout& rLayout = *mpLayouts[ 0 ];
1494  rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
1495 
1496  if( mnLevel <= 1 )
1497  return;
1498 
1499  std::unique_ptr<tools::Long[]> const pTempPos(new tools::Long[nMaxIndex]);
1500  for( int n = 1; n < mnLevel; ++n )
1501  {
1502  mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos.get() );
1503  double fUnitMul = mnUnitsPerPixel;
1504  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1505  for( int i = 0; i < nMaxIndex; ++i )
1506  if( pTempPos[i] >= 0 )
1507  {
1508  tools::Long w = pTempPos[i];
1509  w = static_cast<tools::Long>(w*fUnitMul + 0.5);
1510  pCaretXArray[i] = w;
1511  }
1512  }
1513 }
1514 
1516  Point& rPos, int& nStart,
1517  const PhysicalFontFace** pFallbackFont) const
1518 {
1519  // NOTE: nStart is tagged with current font index
1520  int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
1521  nStart &= ~GF_FONTMASK;
1522  for(; nLevel < mnLevel; ++nLevel, nStart=0 )
1523  {
1524  GenericSalLayout& rLayout = *mpLayouts[ nLevel ];
1525  rLayout.InitFont();
1526  const PhysicalFontFace* pFontFace = rLayout.GetFont().GetFontFace();
1527  if (rLayout.GetNextGlyph(pGlyph, rPos, nStart))
1528  {
1529  int nFontTag = nLevel << GF_FONTSHIFT;
1530  nStart |= nFontTag;
1531  if (pFallbackFont)
1532  *pFallbackFont = pFontFace;
1533  rPos += maDrawBase;
1534  rPos += maDrawOffset;
1535  return true;
1536  }
1537  }
1538 
1539  // #111016# reset to base level font when done
1540  mpLayouts[0]->InitFont();
1541  return false;
1542 }
1543 
1545 {
1546  bool bRet = false;
1547 
1548  for( int i = mnLevel; --i >= 0; )
1549  {
1550  SalLayout& rLayout = *mpLayouts[ i ];
1551  rLayout.DrawBase() = maDrawBase;
1552  rLayout.DrawOffset() += maDrawOffset;
1553  rLayout.InitFont();
1554  bRet |= rLayout.GetOutline(rPPV);
1555  rLayout.DrawOffset() -= maDrawOffset;
1556  }
1557 
1558  return bRet;
1559 }
1560 
1561 bool MultiSalLayout::IsKashidaPosValid(int nCharPos) const
1562 {
1563  // Check the base layout
1564  bool bValid = mpLayouts[0]->IsKashidaPosValid(nCharPos);
1565 
1566  // If base layout returned false, it might be because the character was not
1567  // supported there, so we check fallback layouts.
1568  if (!bValid)
1569  {
1570  for (int i = 1; i < mnLevel; ++i)
1571  {
1572  // - 1 because there is no fallback run for the base layout, IIUC.
1573  if (maFallbackRuns[i - 1].PosIsInAnyRun(nCharPos))
1574  {
1575  bValid = mpLayouts[i]->IsKashidaPosValid(nCharPos);
1576  break;
1577  }
1578  }
1579  }
1580 
1581  return bValid;
1582 }
1583 
1585 {
1586  SalLayoutGlyphs glyphs;
1587  for( int n = 0; n < mnLevel; ++n )
1588  glyphs.AppendImpl(mpLayouts[n]->GlyphsImpl().clone());
1589  return glyphs;
1590 }
1591 
1592 
1593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DeviceCoordinate GetTextWidth() const final override
Definition: sallayout.cxx:664
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:1019
int GetUnitsPerPixel() const
Definition: vcllayout.hxx:81
#define LANGUAGE_ARABIC_SAUDI_ARABIA
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:194
std::unique_ptr< SalLayout > ReleaseBaseLayout()
Definition: sallayout.cxx:1004
#define LANGUAGE_URDU_PAKISTAN
sal_Int16 mnLevel
bool GetOutline(basegfx::B2DPolyPolygonVector &) const override
Definition: sallayout.cxx:1544
std::unique_ptr< GenericSalLayout > mpLayouts[MAX_FALLBACK]
Definition: sallayout.hxx:157
#define LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
#define MAX_FALLBACK
Definition: sallayout.hxx:42
#define GF_FONTMASK
Definition: sallayout.cxx:48
long Long
void InitFont() const override
Definition: sallayout.cxx:1383
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:688
double toRadians(Degree10 x)
Degree10 mnOrientation
Definition: sallayout.hxx:92
#define LANGUAGE_TAMIL
bool IsValid() const
LogicalFontInstance & GetFont() const
Definition: sallayout.hxx:186
MultiSalLayout(std::unique_ptr< SalLayout > pBaseLayout)
Definition: sallayout.cxx:993
void AppendImpl(SalLayoutGlyphsImpl *pImpl)
Point maDrawBase
Definition: vcllayout.hxx:115
bool IsEmpty() const
void GetCaretPositions(int nArraySize, tools::Long *pCaretXArray) const override
Definition: sallayout.cxx:1491
FuncFlags mnFlags
#define LANGUAGE_GUJARATI
sal_uInt16 sal_Unicode
std::vector< int > maRuns
Definition: sallayout.hxx:58
void SetIncomplete(bool bIncomplete)
Definition: sallayout.cxx:1009
#define LANGUAGE_ODIA
void AddPos(int nCharPos, bool bRTL)
Definition: sallayout.cxx:231
bool IsEmpty() const
Definition: sallayout.hxx:67
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
ImplLayoutRuns maRuns
Definition: sallayout.hxx:95
static bool lcl_CanApplyAsianKerning(sal_Unicode cp)
Definition: sallayout.cxx:802
void GetCaretPositions(int nArraySize, tools::Long *pCaretXArray) const final override
Definition: sallayout.cxx:852
#define LANGUAGE_SINDHI
bool IsKashidaPosValid(int nCharPos) const override
Definition: sallayout.cxx:1561
#define LANGUAGE_KHMER
#define LANGUAGE_PUNJABI
virtual void AdjustLayout(ImplLayoutArgs &)
Definition: sallayout.cxx:553
DeviceCoordinate mnLayoutWidth
Definition: sallayout.hxx:91
void ApplyAsianKerning(const OUString &rStr)
Definition: sallayout.cxx:807
#define LANGUAGE_TIBETAN
#define LANGUAGE_BURMESE
bool GetBoundRect(tools::Rectangle &) const
Definition: sallayout.cxx:622
SalLayoutGlyphsImpl m_GlyphItems
Definition: sallayout.hxx:217
virtual DeviceCoordinate FillDXArray(DeviceCoordinate *pDXArray) const =0
abstract base class for physical font faces
const OUString & mrStr
Definition: sallayout.hxx:82
int mnEndCharPos
Definition: vcllayout.hxx:109
virtual ~SalLayout()
Definition: sallayout.cxx:550
TEST
virtual void InitFont() const
Definition: vcllayout.hxx:78
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:655
virtual void DrawText(SalGraphics &) const =0
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr) const override
Definition: sallayout.cxx:1515
bool PosIsInRun(int nCharPos) const
Definition: sallayout.cxx:281
void GetCharWidths(DeviceCoordinate *pCharWidths) const
void DropGlyph(int nStart)
Definition: sallayout.cxx:963
tools::Long DeviceCoordinate
void transform(const basegfx::B2DHomMatrix &rMatrix)
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const PhysicalFontFace **pFallbackFont=nullptr) const override
Definition: sallayout.cxx:901
#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:1015
#define LANGUAGE_MALAYALAM
virtual void GetCaretPositions(int nArraySize, tools::Long *pCaretXArray) const =0
virtual SalLayoutGlyphs GetGlyphs() const
Definition: sallayout.cxx:650
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
Definition: sallayout.cxx:535
#define LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
Point & DrawBase()
Definition: vcllayout.hxx:70
#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
Degree10 mnOrientation
Definition: vcllayout.hxx:112
void MoveGlyph(int nStart, tools::Long nNewXPos)
Definition: sallayout.cxx:938
#define LANGUAGE_HINDI
bool GetRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL) const
Definition: sallayout.cxx:367
void AdjustLayout(ImplLayoutArgs &) override
Definition: sallayout.cxx:1040
SalLayoutFlags
Definition: outdev.hxx:120
sal_uInt32 count() const
static int lcl_CalcAsianKerning(sal_UCS4 c, bool bLeft)
Definition: sallayout.cxx:768
#define LANGUAGE_LAO
void NextRun()
Definition: sallayout.hxx:69
tools::Rectangle & Union(const tools::Rectangle &rRect)
#define SAL_WARN_IF(condition, area, stream)
void DrawText(SalGraphics &) const override
Definition: sallayout.cxx:1389
bool LayoutText(ImplLayoutArgs &, const SalLayoutGlyphsImpl *) override
Definition: sallayout.cxx:1031
#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:68
SalLayoutGlyphs GetGlyphs() const final override
Definition: sallayout.cxx:1584
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:81
LanguageTag maLanguageTag
#define LANGUAGE_BENGALI
ImplLayoutRuns maFallbackRuns
Definition: sallayout.hxx:96
ImplLayoutRuns maFallbackRuns[MAX_FALLBACK]
Definition: sallayout.hxx:158
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:1445
int mnMinCharPos
Definition: vcllayout.hxx:108
sal_Int32 nLength
Point maDrawOffset
Definition: vcllayout.hxx:114
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const final override
Definition: sallayout.cxx:883
#define LANGUAGE_KANNADA
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
bool GetGlyphBoundRect(tools::Rectangle &) const
void Simplify(bool bIsBase)
Definition: sallayout.cxx:973
Point & DrawOffset()
Definition: vcllayout.hxx:72
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override
Definition: sallayout.cxx:1404
const DeviceCoordinate * mpDXArray
Definition: sallayout.hxx:90
bool anyOf(strong_int v) const
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo