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