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 <sal/config.h>
21 
22 #include <iostream>
23 #include <iomanip>
24 
25 #include <sal/log.hxx>
26 
27 #include <cstdio>
28 
29 #include <math.h>
30 
31 #include <ImplLayoutArgs.hxx>
32 #include <salgdi.hxx>
33 #include <sallayout.hxx>
36 
37 #include <i18nlangtag/lang.h>
38 
39 #include <vcl/svapp.hxx>
40 
41 #include <unicode/ubidi.h>
42 #include <unicode/uchar.h>
43 
44 #include <algorithm>
45 #include <memory>
46 
47 #include <impglyphitem.hxx>
48 
49 // Glyph Flags
50 #define GF_FONTMASK 0xF0000000
51 #define GF_FONTSHIFT 28
52 
53 
55 {
56  nChar = u_charMirror( nChar );
57  return nChar;
58 }
59 
61 {
62  // currently only conversion from ASCII digits is interesting
63  if( (nChar < '0') || ('9' < nChar) )
64  return nChar;
65 
66  int nOffset;
67  // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
68  // CAVEAT! To some like Mongolian MS assigned the same primary language
69  // although the script type is different!
70  LanguageType pri = primary(eLang);
72  nOffset = 0x0660 - '0'; // arabic-indic digits
73  else if ( pri.anyOf(
78  nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
79  else if ( pri == primary(LANGUAGE_BENGALI) )
80  nOffset = 0x09E6 - '0'; // bengali
81  else if ( pri == primary(LANGUAGE_HINDI) )
82  nOffset = 0x0966 - '0'; // devanagari
83  else if ( pri.anyOf(
86  // TODO case:
87  nOffset = 0x1369 - '0'; // ethiopic
88  else if ( pri == primary(LANGUAGE_GUJARATI) )
89  nOffset = 0x0AE6 - '0'; // gujarati
90 #ifdef LANGUAGE_GURMUKHI // TODO case:
91  else if ( pri == primary(LANGUAGE_GURMUKHI) )
92  nOffset = 0x0A66 - '0'; // gurmukhi
93 #endif
94  else if ( pri == primary(LANGUAGE_KANNADA) )
95  nOffset = 0x0CE6 - '0'; // kannada
96  else if ( pri == primary(LANGUAGE_KHMER))
97  nOffset = 0x17E0 - '0'; // khmer
98  else if ( pri == primary(LANGUAGE_LAO) )
99  nOffset = 0x0ED0 - '0'; // lao
100  else if ( pri == primary(LANGUAGE_MALAYALAM) )
101  nOffset = 0x0D66 - '0'; // malayalam
102  else if ( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_LSO))
103  {
104  if (eLang.anyOf(
108  nOffset = 0x1810 - '0'; // mongolian
109  else
110  nOffset = 0; // mongolian cyrillic
111  }
112  else if ( pri == primary(LANGUAGE_BURMESE) )
113  nOffset = 0x1040 - '0'; // myanmar
114  else if ( pri == primary(LANGUAGE_ODIA) )
115  nOffset = 0x0B66 - '0'; // odia
116  else if ( pri == primary(LANGUAGE_TAMIL) )
117  nOffset = 0x0BE7 - '0'; // tamil
118  else if ( pri == primary(LANGUAGE_TELUGU) )
119  nOffset = 0x0C66 - '0'; // telugu
120  else if ( pri == primary(LANGUAGE_THAI) )
121  nOffset = 0x0E50 - '0'; // thai
122  else if ( pri == primary(LANGUAGE_TIBETAN) )
123  nOffset = 0x0F20 - '0'; // tibetan
124  else
125  {
126  nOffset = 0;
127  }
128 
129  nChar += nOffset;
130  return nChar;
131 }
132 
134 : mnMinCharPos( -1 ),
135  mnEndCharPos( -1 ),
136  mnUnitsPerPixel( 1 ),
137  mnOrientation( 0 ),
138  maDrawOffset( 0, 0 )
139 {}
140 
142 {}
143 
145 {
146  mnMinCharPos = rArgs.mnMinCharPos;
147  mnEndCharPos = rArgs.mnEndCharPos;
149 }
150 
151 Point SalLayout::GetDrawPosition( const Point& rRelative ) const
152 {
153  Point aPos = maDrawBase;
154  Point aOfs = rRelative + maDrawOffset;
155 
156  if( mnOrientation == 0_deg10 )
157  aPos += aOfs;
158  else
159  {
160  // cache trigonometric results
161  static Degree10 nOldOrientation(0);
162  static double fCos = 1.0, fSin = 0.0;
163  if( nOldOrientation != mnOrientation )
164  {
165  nOldOrientation = mnOrientation;
166  double fRad = toRadians(mnOrientation);
167  fCos = cos( fRad );
168  fSin = sin( fRad );
169  }
170 
171  double fX = aOfs.X();
172  double fY = aOfs.Y();
173  tools::Long nX = static_cast<tools::Long>( +fCos * fX + fSin * fY );
174  tools::Long nY = static_cast<tools::Long>( +fCos * fY - fSin * fX );
175  aPos += Point( nX, nY );
176  }
177 
178  return aPos;
179 }
180 
182 {
183  bool bAllOk = true;
184  bool bOneOk = false;
185 
186  basegfx::B2DPolyPolygon aGlyphOutline;
187 
188  Point aPos;
189  const GlyphItem* pGlyph;
190  int nStart = 0;
191  const LogicalFontInstance* pGlyphFont;
192  while (GetNextGlyph(&pGlyph, aPos, nStart, &pGlyphFont))
193  {
194  // get outline of individual glyph, ignoring "empty" glyphs
195  bool bSuccess = pGlyph->GetGlyphOutline(pGlyphFont, aGlyphOutline);
196  bAllOk &= bSuccess;
197  bOneOk |= bSuccess;
198  // only add non-empty outlines
199  if( bSuccess && (aGlyphOutline.count() > 0) )
200  {
201  if( aPos.X() || aPos.Y() )
202  {
203  aGlyphOutline.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos.X(), aPos.Y()));
204  }
205 
206  // insert outline at correct position
207  rVector.push_back( aGlyphOutline );
208  }
209  }
210 
211  return (bAllOk && bOneOk);
212 }
213 
215 {
216  bool bRet = false;
217  rRect.SetEmpty();
218 
219  tools::Rectangle aRectangle;
220 
221  Point aPos;
222  const GlyphItem* pGlyph;
223  int nStart = 0;
224  const LogicalFontInstance* pGlyphFont;
225  while (GetNextGlyph(&pGlyph, aPos, nStart, &pGlyphFont))
226  {
227  // get bounding rectangle of individual glyph
228  if (pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle))
229  {
230  // merge rectangle
231  aRectangle += aPos;
232  if (rRect.IsEmpty())
233  rRect = aRectangle;
234  else
235  rRect.Union(aRectangle);
236  bRet = true;
237  }
238  }
239 
240  return bRet;
241 }
242 
244 {
245  return SalLayoutGlyphs(); // invalid
246 }
247 
248 DeviceCoordinate GenericSalLayout::FillDXArray( std::vector<DeviceCoordinate>* pCharWidths ) const
249 {
250  if (pCharWidths)
251  GetCharWidths(*pCharWidths);
252 
253  return GetTextWidth();
254 }
255 
256 // the text width is the maximum logical extent of all glyphs
258 {
259  if (!m_GlyphItems.IsValid())
260  return 0;
261 
262  // initialize the extent
263  DeviceCoordinate nMinPos = 0;
264  DeviceCoordinate nMaxPos = 0;
265 
266  for (auto const& aGlyphItem : m_GlyphItems)
267  {
268  // update the text extent with the glyph extent
269  DeviceCoordinate nXPos = aGlyphItem.m_aLinearPos.getX();
270  if( nMinPos > nXPos )
271  nMinPos = nXPos;
272  nXPos += aGlyphItem.m_nNewWidth - aGlyphItem.xOffset();
273  if( nMaxPos < nXPos )
274  nMaxPos = nXPos;
275  }
276 
277  DeviceCoordinate nWidth = nMaxPos - nMinPos;
278  return nWidth;
279 }
280 
282 {
283  nNewWidth *= mnUnitsPerPixel;
284  DeviceCoordinate nOldWidth = GetTextWidth();
285  if( !nOldWidth || nNewWidth==nOldWidth )
286  return;
287 
288  if (!m_GlyphItems.IsValid())
289  {
290  return;
291  }
292  // find rightmost glyph, it won't get stretched
293  std::vector<GlyphItem>::iterator pGlyphIterRight = m_GlyphItems.begin();
294  pGlyphIterRight += m_GlyphItems.size() - 1;
295  std::vector<GlyphItem>::iterator pGlyphIter;
296  // count stretchable glyphs
297  int nStretchable = 0;
298  int nMaxGlyphWidth = 0;
299  for(pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter)
300  {
301  if( !pGlyphIter->IsDiacritic() )
302  ++nStretchable;
303  if (nMaxGlyphWidth < pGlyphIter->origWidth())
304  nMaxGlyphWidth = pGlyphIter->origWidth();
305  }
306 
307  // move rightmost glyph to requested position
308  nOldWidth -= pGlyphIterRight->origWidth();
309  if( nOldWidth <= 0 )
310  return;
311  if( nNewWidth < nMaxGlyphWidth)
312  nNewWidth = nMaxGlyphWidth;
313  nNewWidth -= pGlyphIterRight->origWidth();
314  pGlyphIterRight->m_aLinearPos.setX( nNewWidth );
315 
316  // justify glyph widths and positions
317  int nDiffWidth = nNewWidth - nOldWidth;
318  if( nDiffWidth >= 0) // expanded case
319  {
320  // expand width by distributing space between glyphs evenly
321  int nDeltaSum = 0;
322  for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
323  {
324  // move glyph to justified position
325  pGlyphIter->m_aLinearPos.AdjustX(nDeltaSum );
326 
327  // do not stretch non-stretchable glyphs
328  if( pGlyphIter->IsDiacritic() || (nStretchable <= 0) )
329  continue;
330 
331  // distribute extra space equally to stretchable glyphs
332  int nDeltaWidth = nDiffWidth / nStretchable--;
333  nDiffWidth -= nDeltaWidth;
334  pGlyphIter->m_nNewWidth += nDeltaWidth;
335  nDeltaSum += nDeltaWidth;
336  }
337  }
338  else // condensed case
339  {
340  // squeeze width by moving glyphs proportionally
341  double fSqueeze = static_cast<double>(nNewWidth) / nOldWidth;
342  if(m_GlyphItems.size() > 1)
343  {
344  for( pGlyphIter = m_GlyphItems.begin(); ++pGlyphIter != pGlyphIterRight;)
345  {
346  int nX = pGlyphIter->m_aLinearPos.getX();
347  nX = static_cast<int>(nX * fSqueeze);
348  pGlyphIter->m_aLinearPos.setX( nX );
349  }
350  }
351  // adjust glyph widths to new positions
352  for( pGlyphIter = m_GlyphItems.begin(); pGlyphIter != pGlyphIterRight; ++pGlyphIter )
353  pGlyphIter->m_nNewWidth = pGlyphIter[1].m_aLinearPos.getX() - pGlyphIter[0].m_aLinearPos.getX();
354  }
355 }
356 
357 // returns asian kerning values in quarter of character width units
358 // to enable automatic halfwidth substitution for fullwidth punctuation
359 // return value is negative for l, positive for r, zero for neutral
360 // TODO: handle vertical layout as proposed in commit 43bf2ad49c2b3989bbbe893e4fee2e032a3920f5?
361 static int lcl_CalcAsianKerning(sal_UCS4 c, bool bLeft)
362 {
363  // http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
364  static const signed char nTable[0x30] =
365  {
366  0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
367  +2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
368  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
369  };
370 
371  int nResult = 0;
372  if( (c >= 0x3000) && (c < 0x3030) )
373  nResult = nTable[ c - 0x3000 ];
374  else switch( c )
375  {
376  case 0x30FB:
377  nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
378  break;
379  case 0x2019: case 0x201D:
380  case 0xFF01: case 0xFF09: case 0xFF0C:
381  case 0xFF1A: case 0xFF1B:
382  nResult = -2;
383  break;
384  case 0x2018: case 0x201C:
385  case 0xFF08:
386  nResult = +2;
387  break;
388  default:
389  break;
390  }
391 
392  return nResult;
393 }
394 
396 {
397  return (0x3000 == (cp & 0xFF00)) || (0xFF00 == (cp & 0xFF00)) || (0x2010 == (cp & 0xFFF0));
398 }
399 
400 void GenericSalLayout::ApplyAsianKerning(const OUString& rStr)
401 {
402  const int nLength = rStr.getLength();
403  tools::Long nOffset = 0;
404 
405  for (std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin(),
406  pGlyphIterEnd = m_GlyphItems.end();
407  pGlyphIter != pGlyphIterEnd; ++pGlyphIter)
408  {
409  const int n = pGlyphIter->charPos();
410  if (n < nLength - 1)
411  {
412  // ignore code ranges that are not affected by asian punctuation compression
413  const sal_Unicode cCurrent = rStr[n];
414  if (!lcl_CanApplyAsianKerning(cCurrent))
415  continue;
416  const sal_Unicode cNext = rStr[n + 1];
417  if (!lcl_CanApplyAsianKerning(cNext))
418  continue;
419 
420  // calculate compression values
421  const int nKernCurrent = +lcl_CalcAsianKerning(cCurrent, true);
422  if (nKernCurrent == 0)
423  continue;
424  const int nKernNext = -lcl_CalcAsianKerning(cNext, false);
425  if (nKernNext == 0)
426  continue;
427 
428  // apply punctuation compression to logical glyph widths
429  int nDelta = (nKernCurrent < nKernNext) ? nKernCurrent : nKernNext;
430  if (nDelta < 0)
431  {
432  nDelta = (nDelta * pGlyphIter->origWidth() + 2) / 4;
433  if( pGlyphIter+1 == pGlyphIterEnd )
434  pGlyphIter->m_nNewWidth += nDelta;
435  nOffset += nDelta;
436  }
437  }
438 
439  // adjust the glyph positions to the new glyph widths
440  if( pGlyphIter+1 != pGlyphIterEnd )
441  pGlyphIter->m_aLinearPos.AdjustX(nOffset);
442  }
443 }
444 
445 void GenericSalLayout::GetCaretPositions( int nMaxIndex, tools::Long* pCaretXArray ) const
446 {
447  // initialize result array
448  for (int i = 0; i < nMaxIndex; ++i)
449  pCaretXArray[i] = -1;
450 
451  // calculate caret positions using glyph array
452  for (auto const& aGlyphItem : m_GlyphItems)
453  {
454  tools::Long nXPos = aGlyphItem.m_aLinearPos.getX();
455  tools::Long nXRight = nXPos + aGlyphItem.origWidth();
456  int n = aGlyphItem.charPos();
457  int nCurrIdx = 2 * (n - mnMinCharPos);
458  // tdf#86399 if this is not the start of a cluster, don't overwrite the caret bounds of the cluster start
459  if (aGlyphItem.IsInCluster() && pCaretXArray[nCurrIdx] != -1)
460  continue;
461  if (!aGlyphItem.IsRTLGlyph() )
462  {
463  // normal positions for LTR case
464  pCaretXArray[ nCurrIdx ] = nXPos;
465  pCaretXArray[ nCurrIdx+1 ] = nXRight;
466  }
467  else
468  {
469  // reverse positions for RTL case
470  pCaretXArray[ nCurrIdx ] = nXRight;
471  pCaretXArray[ nCurrIdx+1 ] = nXPos;
472  }
473  }
474 }
475 
476 sal_Int32 GenericSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
477 {
478  std::vector<DeviceCoordinate> aCharWidths;
479  GetCharWidths(aCharWidths);
480 
481  DeviceCoordinate nWidth = 0;
482  for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
483  {
484  nWidth += aCharWidths[ i - mnMinCharPos ] * nFactor;
485  if( nWidth > nMaxWidth )
486  return i;
487  nWidth += nCharExtra;
488  }
489 
490  return -1;
491 }
492 
494  Point& rPos, int& nStart,
495  const LogicalFontInstance** ppGlyphFont,
496  const PhysicalFontFace**) const
497 {
498  std::vector<GlyphItem>::const_iterator pGlyphIter = m_GlyphItems.begin();
499  std::vector<GlyphItem>::const_iterator pGlyphIterEnd = m_GlyphItems.end();
500  pGlyphIter += nStart;
501 
502  // find next glyph in substring
503  for(; pGlyphIter != pGlyphIterEnd; ++nStart, ++pGlyphIter )
504  {
505  int n = pGlyphIter->charPos();
506  if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
507  break;
508  }
509 
510  // return zero if no more glyph found
511  if( nStart >= static_cast<int>(m_GlyphItems.size()) )
512  return false;
513 
514  if( pGlyphIter == pGlyphIterEnd )
515  return false;
516 
517  // update return data with glyph info
518  *pGlyph = &(*pGlyphIter);
519  ++nStart;
520  if (ppGlyphFont)
521  *ppGlyphFont = m_GlyphItems.GetFont().get();
522 
523  // calculate absolute position in pixel units
524  Point aRelativePos = pGlyphIter->m_aLinearPos;
525 
526  aRelativePos.setX( aRelativePos.X() / mnUnitsPerPixel );
527  aRelativePos.setY( aRelativePos.Y() / mnUnitsPerPixel );
528  rPos = GetDrawPosition( aRelativePos );
529 
530  return true;
531 }
532 
533 void GenericSalLayout::MoveGlyph( int nStart, tools::Long nNewXPos )
534 {
535  if( nStart >= static_cast<int>(m_GlyphItems.size()) )
536  return;
537 
538  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
539  pGlyphIter += nStart;
540 
541  // the nNewXPos argument determines the new cell position
542  // as RTL-glyphs are right justified in their cell
543  // the cell position needs to be adjusted to the glyph position
544  if( pGlyphIter->IsRTLGlyph() )
545  nNewXPos += pGlyphIter->m_nNewWidth - pGlyphIter->origWidth();
546  // calculate the x-offset to the old position
547  tools::Long nXDelta = nNewXPos - pGlyphIter->m_aLinearPos.getX() + pGlyphIter->xOffset();
548  // adjust all following glyph positions if needed
549  if( nXDelta != 0 )
550  {
551  for( std::vector<GlyphItem>::iterator pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
552  {
553  pGlyphIter->m_aLinearPos.AdjustX(nXDelta );
554  }
555  }
556 }
557 
558 void GenericSalLayout::DropGlyph( int nStart )
559 {
560  if( nStart >= static_cast<int>(m_GlyphItems.size()))
561  return;
562 
563  std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
564  pGlyphIter += nStart;
565  pGlyphIter->dropGlyph();
566 }
567 
568 void GenericSalLayout::Simplify( bool bIsBase )
569 {
570  // remove dropped glyphs inplace
571  size_t j = 0;
572  for(size_t i = 0; i < m_GlyphItems.size(); i++ )
573  {
574  if (bIsBase && m_GlyphItems[i].IsDropped())
575  continue;
576  if (!bIsBase && m_GlyphItems[i].glyphId() == 0)
577  continue;
578 
579  if( i != j )
580  {
582  }
583  j += 1;
584  }
585  m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
586 }
587 
588 MultiSalLayout::MultiSalLayout( std::unique_ptr<SalLayout> pBaseLayout )
589 : mnLevel( 1 )
590 , mbIncomplete( false )
591 {
592  assert(dynamic_cast<GenericSalLayout*>(pBaseLayout.get()));
593 
594  mpLayouts[ 0 ].reset(static_cast<GenericSalLayout*>(pBaseLayout.release()));
595  mnUnitsPerPixel = mpLayouts[ 0 ]->GetUnitsPerPixel();
596 }
597 
598 std::unique_ptr<SalLayout> MultiSalLayout::ReleaseBaseLayout()
599 {
600  return std::move(mpLayouts[0]);
601 }
602 
603 void MultiSalLayout::SetIncomplete(bool bIncomplete)
604 {
605  mbIncomplete = bIncomplete;
607 }
608 
610 {
611 }
612 
613 void MultiSalLayout::AddFallback( std::unique_ptr<SalLayout> pFallback,
614  ImplLayoutRuns const & rFallbackRuns)
615 {
616  assert(dynamic_cast<GenericSalLayout*>(pFallback.get()));
617  if( mnLevel >= MAX_FALLBACK )
618  return;
619 
620  mpLayouts[ mnLevel ].reset(static_cast<GenericSalLayout*>(pFallback.release()));
621  maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
622  ++mnLevel;
623 }
624 
626 {
627  if( mnLevel <= 1 )
628  return false;
629  if (!mbIncomplete)
630  maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
631  return true;
632 }
633 
635 {
636  SalLayout::AdjustLayout( rArgs );
637  vcl::text::ImplLayoutArgs aMultiArgs = rArgs;
638  std::vector<DeviceCoordinate> aJustificationArray;
639 
640  if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
641  {
642  // for stretched text in a MultiSalLayout the target width needs to be
643  // distributed by individually adjusting its virtual character widths
644  DeviceCoordinate nTargetWidth = aMultiArgs.mnLayoutWidth;
645  nTargetWidth *= mnUnitsPerPixel; // convert target width to base font units
646  aMultiArgs.mnLayoutWidth = 0;
647 
648  // we need to get the original unmodified layouts ready
649  for( int n = 0; n < mnLevel; ++n )
650  mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
651  // then we can measure the unmodified metrics
652  int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
653  FillDXArray( &aJustificationArray );
654  // #i17359# multilayout is not simplified yet, so calculating the
655  // unjustified width needs handholding; also count the number of
656  // stretchable virtual char widths
657  DeviceCoordinate nOrigWidth = 0;
658  int nStretchable = 0;
659  for( int i = 0; i < nCharCount; ++i )
660  {
661  // convert array from widths to sum of widths
662  nOrigWidth += aJustificationArray[i];
663  if( aJustificationArray[i] > 0 )
664  ++nStretchable;
665  }
666 
667  // now we are able to distribute the extra width over the virtual char widths
668  if( nOrigWidth && (nTargetWidth != nOrigWidth) )
669  {
670  DeviceCoordinate nDiffWidth = nTargetWidth - nOrigWidth;
671  DeviceCoordinate nWidthSum = 0;
672  for( int i = 0; i < nCharCount; ++i )
673  {
674  DeviceCoordinate nJustWidth = aJustificationArray[i];
675  if( (nJustWidth > 0) && (nStretchable > 0) )
676  {
677  DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
678  nJustWidth += nDeltaWidth;
679  nDiffWidth -= nDeltaWidth;
680  --nStretchable;
681  }
682  nWidthSum += nJustWidth;
683  aJustificationArray[i] = nWidthSum;
684  }
685  if( nWidthSum != nTargetWidth )
686  aJustificationArray[ nCharCount-1 ] = nTargetWidth;
687 
688  // the justification array is still in base level units
689  // => convert it to pixel units
690  if( mnUnitsPerPixel > 1 )
691  {
692  for( int i = 0; i < nCharCount; ++i )
693  {
694  DeviceCoordinate nVal = aJustificationArray[ i ];
695  nVal += (mnUnitsPerPixel + 1) / 2;
696  aJustificationArray[ i ] = nVal / mnUnitsPerPixel;
697  }
698  }
699 
700  // change the mpDXArray temporarily (just for the justification)
701  aMultiArgs.mpDXArray = aJustificationArray.data();
702  }
703  }
704 
705  // Compute rtl flags, since in some scripts glyphs/char order can be
706  // reversed for a few character sequences e.g. Myanmar
707  std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
708  rArgs.ResetPos();
709  bool bRtl;
710  int nRunStart, nRunEnd;
711  while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
712  {
713  if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
714  vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
715  }
716  rArgs.ResetPos();
717 
718  // prepare "merge sort"
719  int nStartOld[ MAX_FALLBACK ];
720  int nStartNew[ MAX_FALLBACK ];
721  const GlyphItem* pGlyphs[MAX_FALLBACK];
722  bool bValid[MAX_FALLBACK] = { false };
723 
724  Point aPos;
725  int nLevel = 0, n;
726  for( n = 0; n < mnLevel; ++n )
727  {
728  // now adjust the individual components
729  if( n > 0 )
730  {
731  aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
732  aMultiArgs.mnFlags |= SalLayoutFlags::ForFallback;
733  }
734  mpLayouts[n]->AdjustLayout( aMultiArgs );
735 
736  // remove unused parts of component
737  if( n > 0 )
738  {
739  if (mbIncomplete && (n == mnLevel-1))
740  mpLayouts[n]->Simplify( true );
741  else
742  mpLayouts[n]->Simplify( false );
743  }
744 
745  // prepare merging components
746  nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
747  bValid[nLevel] = mpLayouts[n]->GetNextGlyph(&pGlyphs[nLevel], aPos, nStartNew[nLevel]);
748 
749  if( (n > 0) && !bValid[ nLevel ] )
750  {
751  // an empty fallback layout can be released
752  mpLayouts[n].reset();
753  }
754  else
755  {
756  // reshuffle used fallbacks if needed
757  if( nLevel != n )
758  {
759  mpLayouts[ nLevel ] = std::move(mpLayouts[ n ]);
760  maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
761  }
762  ++nLevel;
763  }
764  }
765  mnLevel = nLevel;
766 
767  // prepare merge the fallback levels
768  tools::Long nXPos = 0;
769  double fUnitMul = 1.0;
770  for( n = 0; n < nLevel; ++n )
771  maFallbackRuns[n].ResetPos();
772 
773  int nFirstValid = -1;
774  for( n = 0; n < nLevel; ++n )
775  {
776  if(bValid[n])
777  {
778  nFirstValid = n;
779  break;
780  }
781  }
782  assert(nFirstValid >= 0);
783 
784  // get the next codepoint index that needs fallback
785  int nActiveCharPos = pGlyphs[nFirstValid]->charPos();
786  int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
787  // get the end index of the active run
788  int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
789  rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
790  int nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
791  // merge the fallback levels
792  while( bValid[nFirstValid] && (nLevel > 0))
793  {
794  // find best fallback level
795  for( n = 0; n < nLevel; ++n )
796  if( bValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
797  // fallback level n wins when it requested no further fallback
798  break;
799  int nFBLevel = n;
800 
801  if( n < nLevel )
802  {
803  // use base(n==0) or fallback(n>=1) level
804  fUnitMul = mnUnitsPerPixel;
805  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
806  tools::Long nNewPos = static_cast<tools::Long>(nXPos/fUnitMul + 0.5);
807  mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
808  }
809  else
810  {
811  n = 0; // keep NotDef in base level
812  fUnitMul = 1.0;
813  }
814 
815  if( n > 0 )
816  {
817  // drop the NotDef glyphs in the base layout run if a fallback run exists
818  while (
819  (maFallbackRuns[n-1].PosIsInRun(pGlyphs[nFirstValid]->charPos())) &&
820  (!maFallbackRuns[n].PosIsInAnyRun(pGlyphs[nFirstValid]->charPos()))
821  )
822  {
823  mpLayouts[0]->DropGlyph( nStartOld[0] );
824  nStartOld[0] = nStartNew[0];
825  bValid[nFirstValid] = mpLayouts[0]->GetNextGlyph(&pGlyphs[nFirstValid], aPos, nStartNew[0]);
826 
827  if( !bValid[nFirstValid] )
828  break;
829  }
830  }
831 
832  // skip to end of layout run and calculate its advance width
833  DeviceCoordinate nRunAdvance = 0;
834  bool bKeepNotDef = (nFBLevel >= nLevel);
835  for(;;)
836  {
837  nRunAdvance += pGlyphs[n]->m_nNewWidth;
838 
839  // proceed to next glyph
840  nStartOld[n] = nStartNew[n];
841  int nOrigCharPos = pGlyphs[n]->charPos();
842  bValid[n] = mpLayouts[n]->GetNextGlyph(&pGlyphs[n], aPos, nStartNew[n]);
843  // break after last glyph of active layout
844  if( !bValid[n] )
845  {
846  // performance optimization (when a fallback layout is no longer needed)
847  if( n >= nLevel-1 )
848  --nLevel;
849  break;
850  }
851 
852  //If the next character is one which belongs to the next level, then we
853  //are finished here for now, and we'll pick up after the next level has
854  //been processed
855  if ((n+1 < nLevel) && (pGlyphs[n]->charPos() != nOrigCharPos))
856  {
857  if (nOrigCharPos < pGlyphs[n]->charPos())
858  {
859  if (pGlyphs[n+1]->charPos() > nOrigCharPos && (pGlyphs[n+1]->charPos() < pGlyphs[n]->charPos()))
860  break;
861  }
862  else if (nOrigCharPos > pGlyphs[n]->charPos())
863  {
864  if (pGlyphs[n+1]->charPos() > pGlyphs[n]->charPos() && (pGlyphs[n+1]->charPos() < nOrigCharPos))
865  break;
866  }
867  }
868 
869  // break at end of layout run
870  if( n > 0 )
871  {
872  // skip until end of fallback run
873  if (!maFallbackRuns[n-1].PosIsInRun(pGlyphs[n]->charPos()))
874  break;
875  }
876  else
877  {
878  // break when a fallback is needed and available
879  bool bNeedFallback = maFallbackRuns[0].PosIsInRun(pGlyphs[nFirstValid]->charPos());
880  if( bNeedFallback )
881  if (!maFallbackRuns[nLevel-1].PosIsInRun(pGlyphs[nFirstValid]->charPos()))
882  break;
883  // break when change from resolved to unresolved base layout run
884  if( bKeepNotDef && !bNeedFallback )
885  { maFallbackRuns[0].NextRun(); break; }
886  bKeepNotDef = bNeedFallback;
887  }
888  // check for reordered glyphs
889  if (aMultiArgs.mpDXArray &&
890  nRunVisibleEndChar < mnEndCharPos &&
891  nRunVisibleEndChar >= mnMinCharPos &&
892  pGlyphs[n]->charPos() < mnEndCharPos &&
893  pGlyphs[n]->charPos() >= mnMinCharPos)
894  {
895  if (vRtl[nActiveCharPos - mnMinCharPos])
896  {
897  if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
898  >= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
899  {
900  nRunVisibleEndChar = pGlyphs[n]->charPos();
901  }
902  }
903  else if (aMultiArgs.mpDXArray[nRunVisibleEndChar-mnMinCharPos]
904  <= aMultiArgs.mpDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
905  {
906  nRunVisibleEndChar = pGlyphs[n]->charPos();
907  }
908  }
909  }
910 
911  // if a justification array is available
912  // => use it directly to calculate the corresponding run width
913  if( aMultiArgs.mpDXArray )
914  {
915  // the run advance is the width from the first char
916  // in the run to the first char in the next run
917  nRunAdvance = 0;
918  nActiveCharIndex = nActiveCharPos - mnMinCharPos;
919  if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
920  {
921  if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
922  nRunAdvance -= aMultiArgs.mpDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
923  if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
924  nRunAdvance += aMultiArgs.mpDXArray[nLastRunEndChar - 1 - mnMinCharPos];
925  }
926  else
927  {
928  if (nRunVisibleEndChar >= mnMinCharPos)
929  nRunAdvance += aMultiArgs.mpDXArray[nRunVisibleEndChar - mnMinCharPos];
930  if (nLastRunEndChar >= mnMinCharPos)
931  nRunAdvance -= aMultiArgs.mpDXArray[nLastRunEndChar - mnMinCharPos];
932  }
933  nLastRunEndChar = nRunVisibleEndChar;
934  nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
935  // the requested width is still in pixel units
936  // => convert it to base level font units
937  nRunAdvance *= mnUnitsPerPixel;
938  }
939  else
940  {
941  // the measured width is still in fallback font units
942  // => convert it to base level font units
943  if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
944  nRunAdvance = static_cast<tools::Long>(nRunAdvance*fUnitMul + 0.5);
945  }
946 
947  // calculate new x position (in base level units)
948  nXPos += nRunAdvance;
949 
950  // prepare for next fallback run
951  nActiveCharPos = pGlyphs[nFirstValid]->charPos();
952  // it essential that the runs don't get ahead of themselves and in the
953  // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
954  // have already been reached on the base level
955  for( int i = nFBLevel; --i >= 0;)
956  {
957  if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
958  {
959  if (bRtl)
960  {
961  if (nRunStart > nActiveCharPos)
963  }
964  else
965  {
966  if (nRunEnd <= nActiveCharPos)
968  }
969  }
970  }
971  }
972 
973  mpLayouts[0]->Simplify( true );
974 }
975 
977 {
978  if( mnLevel > 0 )
979  mpLayouts[0]->InitFont();
980 }
981 
982 void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
983 {
984  for( int i = mnLevel; --i >= 0; )
985  {
986  SalLayout& rLayout = *mpLayouts[ i ];
987  rLayout.DrawBase() += maDrawBase;
988  rLayout.DrawOffset() += maDrawOffset;
989  rLayout.InitFont();
990  rLayout.DrawText( rGraphics );
991  rLayout.DrawOffset() -= maDrawOffset;
992  rLayout.DrawBase() -= maDrawBase;
993  }
994  // NOTE: now the baselevel font is active again
995 }
996 
997 sal_Int32 MultiSalLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
998 {
999  if( mnLevel <= 0 )
1000  return -1;
1001  if( mnLevel == 1 )
1002  return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1003 
1004  int nCharCount = mnEndCharPos - mnMinCharPos;
1005  std::vector<DeviceCoordinate> aCharWidths;
1006  std::vector<DeviceCoordinate> aFallbackCharWidths;
1007  mpLayouts[0]->FillDXArray( &aCharWidths );
1008 
1009  for( int n = 1; n < mnLevel; ++n )
1010  {
1011  SalLayout& rLayout = *mpLayouts[ n ];
1012  rLayout.FillDXArray( &aFallbackCharWidths );
1013  double fUnitMul = mnUnitsPerPixel;
1014  fUnitMul /= rLayout.GetUnitsPerPixel();
1015  for( int i = 0; i < nCharCount; ++i )
1016  {
1017  if( aCharWidths[ i ] == 0 )
1018  {
1019  DeviceCoordinate w = aFallbackCharWidths[i];
1020  w = static_cast<DeviceCoordinate>(w * fUnitMul + 0.5);
1021  aCharWidths[ i ] = w;
1022  }
1023  }
1024  }
1025 
1026  DeviceCoordinate nWidth = 0;
1027  for( int i = 0; i < nCharCount; ++i )
1028  {
1029  nWidth += aCharWidths[ i ] * nFactor;
1030  if( nWidth > nMaxWidth )
1031  return (i + mnMinCharPos);
1032  nWidth += nCharExtra;
1033  }
1034 
1035  return -1;
1036 }
1037 
1038 DeviceCoordinate MultiSalLayout::FillDXArray( std::vector<DeviceCoordinate>* pCharWidths ) const
1039 {
1040  DeviceCoordinate nMaxWidth = 0;
1041 
1042  // prepare merging of fallback levels
1043  std::vector<DeviceCoordinate> aTempWidths;
1044  const int nCharCount = mnEndCharPos - mnMinCharPos;
1045  if( pCharWidths )
1046  {
1047  pCharWidths->clear();
1048  pCharWidths->resize(nCharCount, 0);
1049  }
1050 
1051  for( int n = mnLevel; --n >= 0; )
1052  {
1053  // query every fallback level
1054  DeviceCoordinate nTextWidth = mpLayouts[n]->FillDXArray( &aTempWidths );
1055  if( !nTextWidth )
1056  continue;
1057  // merge results from current level
1058  double fUnitMul = mnUnitsPerPixel;
1059  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1060  nTextWidth = static_cast<DeviceCoordinate>(nTextWidth * fUnitMul + 0.5);
1061  if( nMaxWidth < nTextWidth )
1062  nMaxWidth = nTextWidth;
1063  if( !pCharWidths )
1064  continue;
1065  // calculate virtual char widths using most probable fallback layout
1066  for( int i = 0; i < nCharCount; ++i )
1067  {
1068  // #i17359# restriction:
1069  // one char cannot be resolved from different fallbacks
1070  if( (*pCharWidths)[i] != 0 )
1071  continue;
1072  DeviceCoordinate nCharWidth = aTempWidths[i];
1073  if( !nCharWidth )
1074  continue;
1075  nCharWidth = static_cast<DeviceCoordinate>(nCharWidth * fUnitMul + 0.5);
1076  (*pCharWidths)[i] = nCharWidth;
1077  }
1078  }
1079 
1080  return nMaxWidth;
1081 }
1082 
1083 void MultiSalLayout::GetCaretPositions( int nMaxIndex, tools::Long* pCaretXArray ) const
1084 {
1085  SalLayout& rLayout = *mpLayouts[ 0 ];
1086  rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
1087 
1088  if( mnLevel <= 1 )
1089  return;
1090 
1091  std::unique_ptr<tools::Long[]> const pTempPos(new tools::Long[nMaxIndex]);
1092  for( int n = 1; n < mnLevel; ++n )
1093  {
1094  mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos.get() );
1095  double fUnitMul = mnUnitsPerPixel;
1096  fUnitMul /= mpLayouts[n]->GetUnitsPerPixel();
1097  for( int i = 0; i < nMaxIndex; ++i )
1098  if( pTempPos[i] >= 0 )
1099  {
1100  tools::Long w = pTempPos[i];
1101  w = static_cast<tools::Long>(w*fUnitMul + 0.5);
1102  pCaretXArray[i] = w;
1103  }
1104  }
1105 }
1106 
1108  Point& rPos, int& nStart,
1109  const LogicalFontInstance** ppGlyphFont,
1110  const PhysicalFontFace** pFallbackFont) const
1111 {
1112  // NOTE: nStart is tagged with current font index
1113  int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
1114  nStart &= ~GF_FONTMASK;
1115  for(; nLevel < mnLevel; ++nLevel, nStart=0 )
1116  {
1117  GenericSalLayout& rLayout = *mpLayouts[ nLevel ];
1118  rLayout.InitFont();
1119  const PhysicalFontFace* pFontFace = rLayout.GetFont().GetFontFace();
1120  if (rLayout.GetNextGlyph(pGlyph, rPos, nStart, ppGlyphFont))
1121  {
1122  int nFontTag = nLevel << GF_FONTSHIFT;
1123  nStart |= nFontTag;
1124  if (pFallbackFont)
1125  *pFallbackFont = pFontFace;
1126  rPos += maDrawBase;
1127  rPos += maDrawOffset;
1128  return true;
1129  }
1130  }
1131 
1132  // #111016# reset to base level font when done
1133  mpLayouts[0]->InitFont();
1134  return false;
1135 }
1136 
1138 {
1139  bool bRet = false;
1140 
1141  for( int i = mnLevel; --i >= 0; )
1142  {
1143  SalLayout& rLayout = *mpLayouts[ i ];
1144  rLayout.DrawBase() = maDrawBase;
1145  rLayout.DrawOffset() += maDrawOffset;
1146  rLayout.InitFont();
1147  bRet |= rLayout.GetOutline(rPPV);
1148  rLayout.DrawOffset() -= maDrawOffset;
1149  }
1150 
1151  return bRet;
1152 }
1153 
1154 bool MultiSalLayout::IsKashidaPosValid(int nCharPos) const
1155 {
1156  // Check the base layout
1157  bool bValid = mpLayouts[0]->IsKashidaPosValid(nCharPos);
1158 
1159  // If base layout returned false, it might be because the character was not
1160  // supported there, so we check fallback layouts.
1161  if (!bValid)
1162  {
1163  for (int i = 1; i < mnLevel; ++i)
1164  {
1165  // - 1 because there is no fallback run for the base layout, IIUC.
1166  if (maFallbackRuns[i - 1].PosIsInAnyRun(nCharPos))
1167  {
1168  bValid = mpLayouts[i]->IsKashidaPosValid(nCharPos);
1169  break;
1170  }
1171  }
1172  }
1173 
1174  return bValid;
1175 }
1176 
1178 {
1179  SalLayoutGlyphs glyphs;
1180  for( int n = 0; n < mnLevel; ++n )
1181  glyphs.AppendImpl(mpLayouts[n]->GlyphsImpl().clone());
1182  return glyphs;
1183 }
1184 
1185 
1186 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DeviceCoordinate GetTextWidth() const final override
Definition: sallayout.cxx:257
virtual bool GetOutline(basegfx::B2DPolyPolygonVector &) const
Definition: sallayout.cxx:181
DeviceCoordinate FillDXArray(std::vector< DeviceCoordinate > *pDXArray) const override
Definition: sallayout.cxx:1038
#define LANGUAGE_TELUGU
void AddFallback(std::unique_ptr< SalLayout > pFallbackLayout, ImplLayoutRuns const &)
Definition: sallayout.cxx:613
void GetCharWidths(std::vector< DeviceCoordinate > &rCharWidths) const
int GetUnitsPerPixel() const
Definition: vcllayout.hxx:83
#define LANGUAGE_ARABIC_SAUDI_ARABIA
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:192
std::unique_ptr< SalLayout > ReleaseBaseLayout()
Definition: sallayout.cxx:598
#define LANGUAGE_URDU_PAKISTAN
sal_Int16 mnLevel
bool GetOutline(basegfx::B2DPolyPolygonVector &) const override
Definition: sallayout.cxx:1137
std::unique_ptr< GenericSalLayout > mpLayouts[MAX_FALLBACK]
Definition: sallayout.hxx:88
#define LANGUAGE_MONGOLIAN_MONGOLIAN_LSO
#define MAX_FALLBACK
Definition: sallayout.hxx:46
#define GF_FONTMASK
Definition: sallayout.cxx:50
long Long
void InitFont() const override
Definition: sallayout.cxx:976
#define LANGUAGE_AMHARIC_ETHIOPIA
sal_Int64 n
sal_UCS4 GetMirroredChar(sal_UCS4 nChar)
Definition: sallayout.cxx:54
void Justify(DeviceCoordinate nNewWidth)
Definition: sallayout.cxx:281
bool LayoutText(vcl::text::ImplLayoutArgs &, const SalLayoutGlyphsImpl *) override
Definition: sallayout.cxx:625
bool GetGlyphOutline(const LogicalFontInstance *, basegfx::B2DPolyPolygon &) const
double toRadians(Degree10 x)
#define LANGUAGE_TAMIL
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
bool GetGlyphBoundRect(const LogicalFontInstance *, tools::Rectangle &) const
bool IsValid() const
LogicalFontInstance & GetFont() const
Definition: sallayout.hxx:117
MultiSalLayout(std::unique_ptr< SalLayout > pBaseLayout)
Definition: sallayout.cxx:588
void AppendImpl(SalLayoutGlyphsImpl *pImpl)
Point maDrawBase
Definition: vcllayout.hxx:118
const DeviceCoordinate * mpDXArray
void GetCaretPositions(int nArraySize, tools::Long *pCaretXArray) const override
Definition: sallayout.cxx:1083
#define LANGUAGE_GUJARATI
sal_uInt16 sal_Unicode
void SetIncomplete(bool bIncomplete)
Definition: sallayout.cxx:603
#define LANGUAGE_ODIA
static bool lcl_CanApplyAsianKerning(sal_Unicode cp)
Definition: sallayout.cxx:395
void GetCaretPositions(int nArraySize, tools::Long *pCaretXArray) const final override
Definition: sallayout.cxx:445
#define LANGUAGE_SINDHI
bool IsKashidaPosValid(int nCharPos) const override
Definition: sallayout.cxx:1154
#define LANGUAGE_KHMER
#define LANGUAGE_PUNJABI
void ApplyAsianKerning(const OUString &rStr)
Definition: sallayout.cxx:400
#define LANGUAGE_TIBETAN
#define LANGUAGE_BURMESE
bool GetBoundRect(tools::Rectangle &) const
Definition: sallayout.cxx:214
SalLayoutGlyphsImpl m_GlyphItems
Definition: sallayout.hxx:149
constexpr bool IsEmpty() const
virtual void AdjustLayout(vcl::text::ImplLayoutArgs &)
Definition: sallayout.cxx:144
abstract base class for physical font faces
int mnEndCharPos
Definition: vcllayout.hxx:112
virtual ~SalLayout()
Definition: sallayout.cxx:141
void AdjustLayout(vcl::text::ImplLayoutArgs &) override
Definition: sallayout.cxx:634
virtual void InitFont() const
Definition: vcllayout.hxx:80
int i
#define GF_FONTSHIFT
Definition: sallayout.cxx:51
virtual void DrawText(SalGraphics &) const =0
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr, const PhysicalFontFace **pFallbackFont=nullptr) const override
Definition: sallayout.cxx:1107
bool PosIsInRun(int nCharPos) const
void DropGlyph(int nStart)
Definition: sallayout.cxx:558
DeviceCoordinate FillDXArray(std::vector< DeviceCoordinate > *pDXArray) const final override
Definition: sallayout.cxx:248
tools::Long DeviceCoordinate
sal_Int32 m_nNewWidth
void transform(const basegfx::B2DHomMatrix &rMatrix)
#define LANGUAGE_TIGRIGNA_ETHIOPIA
sal_Int32 w
Point GetDrawPosition(const Point &rRelative=Point(0, 0)) const
Definition: sallayout.cxx:151
constexpr LanguageType primary(LanguageType lt)
#define LANGUAGE_FARSI
virtual ~MultiSalLayout() override
Definition: sallayout.cxx:609
#define LANGUAGE_MALAYALAM
virtual void GetCaretPositions(int nArraySize, tools::Long *pCaretXArray) const =0
const rtl::Reference< LogicalFontInstance > & GetFont() const
virtual SalLayoutGlyphs GetGlyphs() const
Definition: sallayout.cxx:243
#define LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA
Point & DrawBase()
Definition: vcllayout.hxx:72
#define LANGUAGE_THAI
int charPos() const
int mnUnitsPerPixel
Definition: vcllayout.hxx:114
Degree10 mnOrientation
Definition: vcllayout.hxx:115
void MoveGlyph(int nStart, tools::Long nNewXPos)
Definition: sallayout.cxx:533
#define LANGUAGE_HINDI
bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr, const PhysicalFontFace **pFallbackFont=nullptr) const override
Definition: sallayout.cxx:493
sal_uInt32 count() const
static int lcl_CalcAsianKerning(sal_UCS4 c, bool bLeft)
Definition: sallayout.cxx:361
#define LANGUAGE_LAO
tools::Rectangle & Union(const tools::Rectangle &rRect)
void DrawText(SalGraphics &) const override
Definition: sallayout.cxx:982
virtual bool GetNextGlyph(const GlyphItem **pGlyph, Point &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr, const PhysicalFontFace **pFallbackFont=nullptr) const =0
#define LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA
sal_UCS4 GetLocalizedChar(sal_UCS4 nChar, LanguageType eLang)
Definition: sallayout.cxx:60
SalLayoutGlyphs GetGlyphs() const final override
Definition: sallayout.cxx:1177
const PhysicalFontFace * GetFontFace() const
#define LANGUAGE_BENGALI
ImplLayoutRuns maFallbackRuns[MAX_FALLBACK]
Definition: sallayout.hxx:89
int mnMinCharPos
Definition: vcllayout.hxx:111
sal_Int32 nLength
Point maDrawOffset
Definition: vcllayout.hxx:117
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const final override
Definition: sallayout.cxx:476
virtual DeviceCoordinate FillDXArray(std::vector< DeviceCoordinate > *pDXArray) const =0
#define LANGUAGE_KANNADA
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
void Simplify(bool bIsBase)
Definition: sallayout.cxx:568
Point & DrawOffset()
Definition: vcllayout.hxx:74
DeviceCoordinate mnLayoutWidth
sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override
Definition: sallayout.cxx:997
bool anyOf(strong_int v) const
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector