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