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