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 mbSubpixelPositioning(false)
131{}
132
134{}
135
137{
142}
143
145{
147 basegfx::B2DPoint 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 += basegfx::B2DPoint(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 += basegfx::B2DPoint(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
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
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
258double GenericSalLayout::FillDXArray( std::vector<double>* 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 double nWidth = 0;
273 for (auto const& aGlyphItem : m_GlyphItems)
274 nWidth += aGlyphItem.newWidth();
275
276 return nWidth;
277}
278
279void GenericSalLayout::Justify(double nNewWidth)
280{
281 double 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 double 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 double 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 double 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 = nNewWidth / nOldWidth;
339 if(m_GlyphItems.size() > 1)
340 {
341 for( pGlyphIter = m_GlyphItems.begin(); ++pGlyphIter != pGlyphIterRight;)
342 {
343 double nX = pGlyphIter->linearPos().getX();
344 nX = 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 double 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(std::vector<double>& rCaretPositions,
443 const OUString& rStr) const
444{
445 const int nCaretPositions = (mnEndCharPos - mnMinCharPos) * 2;
446
447 rCaretPositions.clear();
448 rCaretPositions.resize(nCaretPositions, -1);
449
450 if (m_GlyphItems.empty())
451 return;
452
453 std::vector<double> aCharWidths;
454 GetCharWidths(aCharWidths, rStr);
455
456 // calculate caret positions using glyph array
457 for (auto const& aGlyphItem : m_GlyphItems)
458 {
459 auto nCurrX = aGlyphItem.linearPos().getX() - aGlyphItem.xOffset();
460 auto nCharStart = aGlyphItem.charPos();
461 auto nCharEnd = nCharStart + aGlyphItem.charCount() - 1;
462 if (!aGlyphItem.IsRTLGlyph())
463 {
464 // unchanged positions for LTR case
465 for (int i = nCharStart; i <= nCharEnd; i++)
466 {
467 int n = i - mnMinCharPos;
468 int nCurrIdx = 2 * n;
469
470 auto nLeft = nCurrX;
471 nCurrX += aCharWidths[n];
472 auto nRight = nCurrX;
473
474 rCaretPositions[nCurrIdx] = nLeft;
475 rCaretPositions[nCurrIdx + 1] = nRight;
476 }
477 }
478 else
479 {
480 // reverse positions for RTL case
481 for (int i = nCharEnd; i >= nCharStart; i--)
482 {
483 int n = i - mnMinCharPos;
484 int nCurrIdx = 2 * n;
485
486 auto nRight = nCurrX;
487 nCurrX += aCharWidths[n];
488 auto nLeft = nCurrX;
489
490 rCaretPositions[nCurrIdx] = nLeft;
491 rCaretPositions[nCurrIdx + 1] = nRight;
492 }
493 }
494 }
495}
496
497sal_Int32 GenericSalLayout::GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const
498{
499 std::vector<double> aCharWidths;
500 GetCharWidths(aCharWidths, {});
501
502 double nWidth = 0;
503 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
504 {
505 double nDelta = aCharWidths[ i - mnMinCharPos ] * nFactor;
506
507 if (nDelta != 0)
508 {
509 nWidth += nDelta;
510 if( nWidth > nMaxWidth )
511 return i;
512
513 nWidth += nCharExtra;
514 }
515 }
516
517 return -1;
518}
519
521 basegfx::B2DPoint& rPos, int& nStart,
522 const LogicalFontInstance** ppGlyphFont) const
523{
524 std::vector<GlyphItem>::const_iterator pGlyphIter = m_GlyphItems.begin();
525 std::vector<GlyphItem>::const_iterator pGlyphIterEnd = m_GlyphItems.end();
526 pGlyphIter += nStart;
527
528 // find next glyph in substring
529 for(; pGlyphIter != pGlyphIterEnd; ++nStart, ++pGlyphIter )
530 {
531 int n = pGlyphIter->charPos();
532 if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
533 break;
534 }
535
536 // return zero if no more glyph found
537 if( nStart >= static_cast<int>(m_GlyphItems.size()) )
538 return false;
539
540 if( pGlyphIter == pGlyphIterEnd )
541 return false;
542
543 // update return data with glyph info
544 *pGlyph = &(*pGlyphIter);
545 ++nStart;
546 if (ppGlyphFont)
547 *ppGlyphFont = m_GlyphItems.GetFont().get();
548
549 // calculate absolute position in pixel units
550 basegfx::B2DPoint aRelativePos = pGlyphIter->linearPos();
551
552 rPos = GetDrawPosition( aRelativePos );
553
554 return true;
555}
556
557void GenericSalLayout::MoveGlyph(int nStart, double nNewXPos)
558{
559 if( nStart >= static_cast<int>(m_GlyphItems.size()) )
560 return;
561
562 std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
563 pGlyphIter += nStart;
564
565 // the nNewXPos argument determines the new cell position
566 // as RTL-glyphs are right justified in their cell
567 // the cell position needs to be adjusted to the glyph position
568 if( pGlyphIter->IsRTLGlyph() )
569 nNewXPos += pGlyphIter->newWidth() - pGlyphIter->origWidth();
570 // calculate the x-offset to the old position
571 double nXDelta = nNewXPos - pGlyphIter->linearPos().getX() + pGlyphIter->xOffset();
572 // adjust all following glyph positions if needed
573 if( nXDelta != 0 )
574 {
575 for( std::vector<GlyphItem>::iterator pGlyphIterEnd = m_GlyphItems.end(); pGlyphIter != pGlyphIterEnd; ++pGlyphIter )
576 {
577 pGlyphIter->adjustLinearPosX(nXDelta);
578 }
579 }
580}
581
583{
584 if( nStart >= static_cast<int>(m_GlyphItems.size()))
585 return;
586
587 std::vector<GlyphItem>::iterator pGlyphIter = m_GlyphItems.begin();
588 pGlyphIter += nStart;
589 pGlyphIter->dropGlyph();
590}
591
592void GenericSalLayout::Simplify( bool bIsBase )
593{
594 // remove dropped glyphs inplace
595 size_t j = 0;
596 for(size_t i = 0; i < m_GlyphItems.size(); i++ )
597 {
598 if (bIsBase && m_GlyphItems[i].IsDropped())
599 continue;
600 if (!bIsBase && m_GlyphItems[i].glyphId() == 0)
601 continue;
602
603 if( i != j )
604 {
606 }
607 j += 1;
608 }
609 m_GlyphItems.erase(m_GlyphItems.begin() + j, m_GlyphItems.end());
610}
611
612MultiSalLayout::MultiSalLayout( std::unique_ptr<SalLayout> pBaseLayout )
613: mnLevel( 1 )
614, mbIncomplete( false )
615{
616 assert(dynamic_cast<GenericSalLayout*>(pBaseLayout.get()));
617
618 mpLayouts[ 0 ].reset(static_cast<GenericSalLayout*>(pBaseLayout.release()));
619}
620
621std::unique_ptr<SalLayout> MultiSalLayout::ReleaseBaseLayout()
622{
623 return std::move(mpLayouts[0]);
624}
625
626void MultiSalLayout::SetIncomplete(bool bIncomplete)
627{
628 mbIncomplete = bIncomplete;
630}
631
633{
634}
635
636void MultiSalLayout::AddFallback( std::unique_ptr<SalLayout> pFallback,
637 ImplLayoutRuns const & rFallbackRuns)
638{
639 assert(dynamic_cast<GenericSalLayout*>(pFallback.get()));
640 if( mnLevel >= MAX_FALLBACK )
641 return;
642
643 mpLayouts[ mnLevel ].reset(static_cast<GenericSalLayout*>(pFallback.release()));
644 maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
645 ++mnLevel;
646}
647
649{
650 if( mnLevel <= 1 )
651 return false;
652 if (!mbIncomplete)
653 maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
654 return true;
655}
656
658{
660 vcl::text::ImplLayoutArgs aMultiArgs = rArgs;
661 std::vector<double> aJustificationArray;
662
663 if( !rArgs.HasDXArray() && rArgs.mnLayoutWidth )
664 {
665 // for stretched text in a MultiSalLayout the target width needs to be
666 // distributed by individually adjusting its virtual character widths
667 double nTargetWidth = aMultiArgs.mnLayoutWidth;
668 aMultiArgs.mnLayoutWidth = 0;
669
670 // we need to get the original unmodified layouts ready
671 for( int n = 0; n < mnLevel; ++n )
672 mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
673 // then we can measure the unmodified metrics
674 int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
675 FillDXArray( &aJustificationArray, {} );
676 // #i17359# multilayout is not simplified yet, so calculating the
677 // unjustified width needs handholding; also count the number of
678 // stretchable virtual char widths
679 double nOrigWidth = 0;
680 int nStretchable = 0;
681 for( int i = 0; i < nCharCount; ++i )
682 {
683 // convert array from widths to sum of widths
684 nOrigWidth += aJustificationArray[i];
685 if( aJustificationArray[i] > 0 )
686 ++nStretchable;
687 }
688
689 // now we are able to distribute the extra width over the virtual char widths
690 if( nOrigWidth && (nTargetWidth != nOrigWidth) )
691 {
692 double nDiffWidth = nTargetWidth - nOrigWidth;
693 double nWidthSum = 0;
694 for( int i = 0; i < nCharCount; ++i )
695 {
696 double nJustWidth = aJustificationArray[i];
697 if( (nJustWidth > 0) && (nStretchable > 0) )
698 {
699 double nDeltaWidth = nDiffWidth / nStretchable;
700 nJustWidth += nDeltaWidth;
701 nDiffWidth -= nDeltaWidth;
702 --nStretchable;
703 }
704 nWidthSum += nJustWidth;
705 aJustificationArray[i] = nWidthSum;
706 }
707 if( nWidthSum != nTargetWidth )
708 aJustificationArray[ nCharCount-1 ] = nTargetWidth;
709
710 // change the DXArray temporarily (just for the justification)
711 aMultiArgs.mpDXArray = aJustificationArray.data();
712 }
713 }
714
715 ImplAdjustMultiLayout(rArgs, aMultiArgs, aMultiArgs.mpDXArray);
716}
717
719 vcl::text::ImplLayoutArgs& rMultiArgs,
720 const double* pMultiDXArray)
721{
722 // Compute rtl flags, since in some scripts glyphs/char order can be
723 // reversed for a few character sequences e.g. Myanmar
724 std::vector<bool> vRtl(rArgs.mnEndCharPos - rArgs.mnMinCharPos, false);
725 rArgs.ResetPos();
726 bool bRtl;
727 int nRunStart, nRunEnd;
728 while (rArgs.GetNextRun(&nRunStart, &nRunEnd, &bRtl))
729 {
730 if (bRtl) std::fill(vRtl.begin() + (nRunStart - rArgs.mnMinCharPos),
731 vRtl.begin() + (nRunEnd - rArgs.mnMinCharPos), true);
732 }
733 rArgs.ResetPos();
734
735 // prepare "merge sort"
736 int nStartOld[ MAX_FALLBACK ];
737 int nStartNew[ MAX_FALLBACK ];
738 const GlyphItem* pGlyphs[MAX_FALLBACK];
739 bool bValid[MAX_FALLBACK] = { false };
740
742 int nLevel = 0, n;
743 for( n = 0; n < mnLevel; ++n )
744 {
745 // now adjust the individual components
746 if( n > 0 )
747 {
748 rMultiArgs.maRuns = maFallbackRuns[ n-1 ];
750 }
751 mpLayouts[n]->AdjustLayout( rMultiArgs );
752
753 // remove unused parts of component
754 if( n > 0 )
755 {
756 if (mbIncomplete && (n == mnLevel-1))
757 mpLayouts[n]->Simplify( true );
758 else
759 mpLayouts[n]->Simplify( false );
760 }
761
762 // prepare merging components
763 nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
764 bValid[nLevel] = mpLayouts[n]->GetNextGlyph(&pGlyphs[nLevel], aPos, nStartNew[nLevel]);
765
766 if( (n > 0) && !bValid[ nLevel ] )
767 {
768 // an empty fallback layout can be released
769 mpLayouts[n].reset();
770 }
771 else
772 {
773 // reshuffle used fallbacks if needed
774 if( nLevel != n )
775 {
776 mpLayouts[ nLevel ] = std::move(mpLayouts[ n ]);
777 maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
778 }
779 ++nLevel;
780 }
781 }
782 mnLevel = nLevel;
783
784 // prepare merge the fallback levels
785 double nXPos = 0;
786 for( n = 0; n < nLevel; ++n )
787 maFallbackRuns[n].ResetPos();
788
789 int nFirstValid = -1;
790 for( n = 0; n < nLevel; ++n )
791 {
792 if(bValid[n])
793 {
794 nFirstValid = n;
795 break;
796 }
797 }
798 assert(nFirstValid >= 0);
799
800 // get the next codepoint index that needs fallback
801 int nActiveCharPos = pGlyphs[nFirstValid]->charPos();
802 int nActiveCharIndex = nActiveCharPos - mnMinCharPos;
803 // get the end index of the active run
804 int nLastRunEndChar = (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex]) ?
805 rArgs.mnEndCharPos : rArgs.mnMinCharPos - 1;
806 int nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
807 // merge the fallback levels
808 while( bValid[nFirstValid] && (nLevel > 0))
809 {
810 // find best fallback level
811 for( n = 0; n < nLevel; ++n )
812 if( bValid[n] && !maFallbackRuns[n].PosIsInAnyRun( nActiveCharPos ) )
813 // fallback level n wins when it requested no further fallback
814 break;
815 int nFBLevel = n;
816
817 if( n < nLevel )
818 {
819 // use base(n==0) or fallback(n>=1) level
820 mpLayouts[n]->MoveGlyph( nStartOld[n], nXPos );
821 }
822 else
823 {
824 n = 0; // keep NotDef in base level
825 }
826
827 if( n > 0 )
828 {
829 // drop the NotDef glyphs in the base layout run if a fallback run exists
830 while (
831 (maFallbackRuns[n-1].PosIsInAnyRun(pGlyphs[nFirstValid]->charPos())) &&
832 (!maFallbackRuns[n].PosIsInAnyRun(pGlyphs[nFirstValid]->charPos()))
833 )
834 {
835 mpLayouts[0]->DropGlyph( nStartOld[0] );
836 nStartOld[0] = nStartNew[0];
837 bValid[nFirstValid] = mpLayouts[0]->GetNextGlyph(&pGlyphs[nFirstValid], aPos, nStartNew[0]);
838
839 if( !bValid[nFirstValid] )
840 break;
841 }
842 }
843
844 // skip to end of layout run and calculate its advance width
845 double nRunAdvance = 0;
846 bool bKeepNotDef = (nFBLevel >= nLevel);
847 for(;;)
848 {
849 nRunAdvance += pGlyphs[n]->newWidth();
850
851 // proceed to next glyph
852 nStartOld[n] = nStartNew[n];
853 int nOrigCharPos = pGlyphs[n]->charPos();
854 bValid[n] = mpLayouts[n]->GetNextGlyph(&pGlyphs[n], aPos, nStartNew[n]);
855 // break after last glyph of active layout
856 if( !bValid[n] )
857 {
858 // performance optimization (when a fallback layout is no longer needed)
859 if( n >= nLevel-1 )
860 --nLevel;
861 break;
862 }
863
864 //If the next character is one which belongs to the next level, then we
865 //are finished here for now, and we'll pick up after the next level has
866 //been processed
867 if ((n+1 < nLevel) && (pGlyphs[n]->charPos() != nOrigCharPos))
868 {
869 if (nOrigCharPos < pGlyphs[n]->charPos())
870 {
871 if (pGlyphs[n+1]->charPos() > nOrigCharPos && (pGlyphs[n+1]->charPos() < pGlyphs[n]->charPos()))
872 break;
873 }
874 else if (nOrigCharPos > pGlyphs[n]->charPos())
875 {
876 if (pGlyphs[n+1]->charPos() > pGlyphs[n]->charPos() && (pGlyphs[n+1]->charPos() < nOrigCharPos))
877 break;
878 }
879 }
880
881 // break at end of layout run
882 if( n > 0 )
883 {
884 // skip until end of fallback run
885 if (!maFallbackRuns[n-1].PosIsInRun(pGlyphs[n]->charPos()))
886 break;
887 }
888 else
889 {
890 // break when a fallback is needed and available
891 bool bNeedFallback = maFallbackRuns[0].PosIsInRun(pGlyphs[nFirstValid]->charPos());
892 if( bNeedFallback )
893 if (!maFallbackRuns[nLevel-1].PosIsInRun(pGlyphs[nFirstValid]->charPos()))
894 break;
895 // break when change from resolved to unresolved base layout run
896 if( bKeepNotDef && !bNeedFallback )
897 { maFallbackRuns[0].NextRun(); break; }
898 bKeepNotDef = bNeedFallback;
899 }
900 // check for reordered glyphs
901 if (pMultiDXArray &&
902 nRunVisibleEndChar < mnEndCharPos &&
903 nRunVisibleEndChar >= mnMinCharPos &&
904 pGlyphs[n]->charPos() < mnEndCharPos &&
905 pGlyphs[n]->charPos() >= mnMinCharPos)
906 {
907 if (vRtl[nActiveCharPos - mnMinCharPos])
908 {
909 if (pMultiDXArray[nRunVisibleEndChar-mnMinCharPos]
910 >= pMultiDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
911 {
912 nRunVisibleEndChar = pGlyphs[n]->charPos();
913 }
914 }
915 else if (pMultiDXArray[nRunVisibleEndChar-mnMinCharPos]
916 <= pMultiDXArray[pGlyphs[n]->charPos() - mnMinCharPos])
917 {
918 nRunVisibleEndChar = pGlyphs[n]->charPos();
919 }
920 }
921 }
922
923 // if a justification array is available
924 // => use it directly to calculate the corresponding run width
925 if (pMultiDXArray)
926 {
927 // the run advance is the width from the first char
928 // in the run to the first char in the next run
929 nRunAdvance = 0;
930 nActiveCharIndex = nActiveCharPos - mnMinCharPos;
931 if (nActiveCharIndex >= 0 && vRtl[nActiveCharIndex])
932 {
933 if (nRunVisibleEndChar > mnMinCharPos && nRunVisibleEndChar <= mnEndCharPos)
934 nRunAdvance -= pMultiDXArray[nRunVisibleEndChar - 1 - mnMinCharPos];
935 if (nLastRunEndChar > mnMinCharPos && nLastRunEndChar <= mnEndCharPos)
936 nRunAdvance += pMultiDXArray[nLastRunEndChar - 1 - mnMinCharPos];
937 }
938 else
939 {
940 if (nRunVisibleEndChar >= mnMinCharPos)
941 nRunAdvance += pMultiDXArray[nRunVisibleEndChar - mnMinCharPos];
942 if (nLastRunEndChar >= mnMinCharPos)
943 nRunAdvance -= pMultiDXArray[nLastRunEndChar - mnMinCharPos];
944 }
945 nLastRunEndChar = nRunVisibleEndChar;
946 nRunVisibleEndChar = pGlyphs[nFirstValid]->charPos();
947 }
948
949 // calculate new x position
950 nXPos += nRunAdvance;
951
952 // prepare for next fallback run
953 nActiveCharPos = pGlyphs[nFirstValid]->charPos();
954 // it essential that the runs don't get ahead of themselves and in the
955 // if( bKeepNotDef && !bNeedFallback ) statement above, the next run may
956 // have already been reached on the base level
957 for( int i = nFBLevel; --i >= 0;)
958 {
959 if (maFallbackRuns[i].GetRun(&nRunStart, &nRunEnd, &bRtl))
960 {
961 if (bRtl)
962 {
963 if (nRunStart > nActiveCharPos)
965 }
966 else
967 {
968 if (nRunEnd <= nActiveCharPos)
970 }
971 }
972 }
973 }
974
975 mpLayouts[0]->Simplify( true );
976}
977
979{
980 if( mnLevel > 0 )
981 mpLayouts[0]->InitFont();
982}
983
984void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
985{
986 for( int i = mnLevel; --i >= 0; )
987 {
988 SalLayout& rLayout = *mpLayouts[ i ];
989 rLayout.DrawBase() += maDrawBase;
990 rLayout.DrawOffset() += maDrawOffset;
991 rLayout.InitFont();
992 rLayout.DrawText( rGraphics );
993 rLayout.DrawOffset() -= maDrawOffset;
994 rLayout.DrawBase() -= maDrawBase;
995 }
996 // NOTE: now the baselevel font is active again
997}
998
999sal_Int32 MultiSalLayout::GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const
1000{
1001 if( mnLevel <= 0 )
1002 return -1;
1003 if( mnLevel == 1 )
1004 return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
1005
1006 int nCharCount = mnEndCharPos - mnMinCharPos;
1007 std::vector<double> aCharWidths;
1008 std::vector<double> aFallbackCharWidths;
1009 mpLayouts[0]->FillDXArray( &aCharWidths, {} );
1010
1011 for( int n = 1; n < mnLevel; ++n )
1012 {
1013 SalLayout& rLayout = *mpLayouts[ n ];
1014 rLayout.FillDXArray( &aFallbackCharWidths, {} );
1015 for( int i = 0; i < nCharCount; ++i )
1016 if( aCharWidths[ i ] == 0 )
1017 aCharWidths[i] = aFallbackCharWidths[i];
1018 }
1019
1020 double nWidth = 0;
1021 for( int i = 0; i < nCharCount; ++i )
1022 {
1023 nWidth += aCharWidths[ i ] * nFactor;
1024 if( nWidth > nMaxWidth )
1025 return (i + mnMinCharPos);
1026 nWidth += nCharExtra;
1027 }
1028
1029 return -1;
1030}
1031
1033{
1034 // Measure text width. There might be holes in each SalLayout due to
1035 // missing chars, so we use GetNextGlyph() to get the glyphs across all
1036 // layouts.
1037 int nStart = 0;
1038 basegfx::B2DPoint aPos;
1039 const GlyphItem* pGlyphItem;
1040
1041 double nWidth = 0;
1042 while (GetNextGlyph(&pGlyphItem, aPos, nStart))
1043 nWidth += pGlyphItem->newWidth();
1044
1045 return nWidth;
1046}
1047
1048double MultiSalLayout::FillDXArray( std::vector<double>* pCharWidths, const OUString& rStr ) const
1049{
1050 if (pCharWidths)
1051 {
1052 // prepare merging of fallback levels
1053 std::vector<double> aTempWidths;
1054 const int nCharCount = mnEndCharPos - mnMinCharPos;
1055 pCharWidths->clear();
1056 pCharWidths->resize(nCharCount, 0);
1057
1058 for (int n = mnLevel; --n >= 0;)
1059 {
1060 // query every fallback level
1061 mpLayouts[n]->FillDXArray(&aTempWidths, rStr);
1062
1063 // calculate virtual char widths using most probable fallback layout
1064 for (int i = 0; i < nCharCount; ++i)
1065 {
1066 // #i17359# restriction:
1067 // one char cannot be resolved from different fallbacks
1068 if ((*pCharWidths)[i] != 0)
1069 continue;
1070 double nCharWidth = aTempWidths[i];
1071 if (!nCharWidth)
1072 continue;
1073 (*pCharWidths)[i] = nCharWidth;
1074 }
1075 }
1076 }
1077
1078 return GetTextWidth();
1079}
1080
1081void MultiSalLayout::GetCaretPositions(std::vector<double>& rCaretPositions,
1082 const OUString& rStr) const
1083{
1084 // prepare merging of fallback levels
1085 std::vector<double> aTempPos;
1086 const int nCaretPositions = (mnEndCharPos - mnMinCharPos) * 2;
1087 rCaretPositions.clear();
1088 rCaretPositions.resize(nCaretPositions, -1);
1089
1090 for (int n = mnLevel; --n >= 0;)
1091 {
1092 // query every fallback level
1093 mpLayouts[n]->GetCaretPositions(aTempPos, rStr);
1094
1095 // calculate virtual char widths using most probable fallback layout
1096 for (int i = 0; i < nCaretPositions; ++i)
1097 {
1098 // one char cannot be resolved from different fallbacks
1099 if (rCaretPositions[i] != -1)
1100 continue;
1101 if (aTempPos[i] >= 0)
1102 rCaretPositions[i] = aTempPos[i];
1103 }
1104 }
1105}
1106
1108 basegfx::B2DPoint& rPos, int& nStart,
1109 const LogicalFontInstance** ppGlyphFont) const
1110{
1111 // NOTE: nStart is tagged with current font index
1112 int nLevel = static_cast<unsigned>(nStart) >> GF_FONTSHIFT;
1113 nStart &= ~GF_FONTMASK;
1114 for(; nLevel < mnLevel; ++nLevel, nStart=0 )
1115 {
1116 GenericSalLayout& rLayout = *mpLayouts[ nLevel ];
1117 rLayout.InitFont();
1118 if (rLayout.GetNextGlyph(pGlyph, rPos, nStart, ppGlyphFont))
1119 {
1120 int nFontTag = nLevel << GF_FONTSHIFT;
1121 nStart |= nFontTag;
1122 rPos.adjustX(maDrawBase.getX() + maDrawOffset.X());
1123 rPos.adjustY(maDrawBase.getY() + maDrawOffset.Y());
1124 return true;
1125 }
1126 }
1127
1128 // #111016# reset to base level font when done
1129 mpLayouts[0]->InitFont();
1130 return false;
1131}
1132
1134{
1135 bool bRet = false;
1136
1137 for( int i = mnLevel; --i >= 0; )
1138 {
1139 SalLayout& rLayout = *mpLayouts[ i ];
1140 rLayout.DrawBase() = maDrawBase;
1141 rLayout.DrawOffset() += maDrawOffset;
1142 rLayout.InitFont();
1143 bRet |= rLayout.GetOutline(rPPV);
1144 rLayout.DrawOffset() -= maDrawOffset;
1145 }
1146
1147 return bRet;
1148}
1149
1150bool MultiSalLayout::IsKashidaPosValid(int nCharPos, int nNextCharPos) const
1151{
1152 // Check the base layout
1153 bool bValid = mpLayouts[0]->IsKashidaPosValid(nCharPos, nNextCharPos);
1154
1155 // If base layout returned false, it might be because the character was not
1156 // supported there, so we check fallback layouts.
1157 if (!bValid)
1158 {
1159 for (int i = 1; i < mnLevel; ++i)
1160 {
1161 // - 1 because there is no fallback run for the base layout, IIUC.
1162 if (maFallbackRuns[i - 1].PosIsInAnyRun(nCharPos) &&
1163 maFallbackRuns[i - 1].PosIsInAnyRun(nNextCharPos))
1164 {
1165 bValid = mpLayouts[i]->IsKashidaPosValid(nCharPos, nNextCharPos);
1166 break;
1167 }
1168 }
1169 }
1170
1171 return bValid;
1172}
1173
1175{
1176 SalLayoutGlyphs glyphs;
1177 for( int n = 0; n < mnLevel; ++n )
1178 glyphs.AppendImpl(mpLayouts[n]->GlyphsImpl().clone());
1179 return glyphs;
1180}
1181
1182/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void DropGlyph(int nStart)
Definition: sallayout.cxx:582
SalLayoutGlyphsImpl m_GlyphItems
Definition: sallayout.hxx:158
void GetCharWidths(std::vector< double > &rCharWidths, const OUString &rStr) const
bool GetNextGlyph(const GlyphItem **pGlyph, basegfx::B2DPoint &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr) const override
Definition: sallayout.cxx:520
sal_Int32 GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const final override
Definition: sallayout.cxx:497
void MoveGlyph(int nStart, double nNewXPos)
Definition: sallayout.cxx:557
double FillDXArray(std::vector< double > *pDXArray, const OUString &rStr) const final override
Definition: sallayout.cxx:258
void GetCaretPositions(std::vector< double > &rCaretPositions, const OUString &rStr) const override
Definition: sallayout.cxx:442
void Justify(double nNewWidth)
Definition: sallayout.cxx:279
void Simplify(bool bIsBase)
Definition: sallayout.cxx:592
void ApplyAsianKerning(std::u16string_view rStr)
Definition: sallayout.cxx:397
double GetTextWidth() const final override
Definition: sallayout.cxx:267
bool GetGlyphBoundRect(const LogicalFontInstance *, tools::Rectangle &) const
bool GetGlyphOutline(const LogicalFontInstance *, basegfx::B2DPolyPolygon &) const
int charPos() const
double newWidth() const
bool PosIsInRun(int nCharPos) const
bool PosIsInAnyRun(int nCharPos) const
bool GetNextGlyph(const GlyphItem **pGlyph, basegfx::B2DPoint &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr) const override
Definition: sallayout.cxx:1107
bool GetOutline(basegfx::B2DPolyPolygonVector &) const override
Definition: sallayout.cxx:1133
void AdjustLayout(vcl::text::ImplLayoutArgs &) override
Definition: sallayout.cxx:657
void ImplAdjustMultiLayout(vcl::text::ImplLayoutArgs &rArgs, vcl::text::ImplLayoutArgs &rMultiArgs, const double *pMultiDXArray)
Definition: sallayout.cxx:718
void SetIncomplete(bool bIncomplete)
Definition: sallayout.cxx:626
std::unique_ptr< GenericSalLayout > mpLayouts[MAX_FALLBACK]
Definition: sallayout.hxx:95
std::unique_ptr< SalLayout > ReleaseBaseLayout()
Definition: sallayout.cxx:621
sal_Int32 GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const override
Definition: sallayout.cxx:999
void DrawText(SalGraphics &) const override
Definition: sallayout.cxx:984
void InitFont() const override
Definition: sallayout.cxx:978
double GetTextWidth() const final override
Definition: sallayout.cxx:1032
double FillDXArray(std::vector< double > *pDXArray, const OUString &rStr) const override
Definition: sallayout.cxx:1048
bool LayoutText(vcl::text::ImplLayoutArgs &, const SalLayoutGlyphsImpl *) override
Definition: sallayout.cxx:648
SalLayoutGlyphs GetGlyphs() const final override
Definition: sallayout.cxx:1174
ImplLayoutRuns maFallbackRuns[MAX_FALLBACK]
Definition: sallayout.hxx:96
void AddFallback(std::unique_ptr< SalLayout > pFallbackLayout, ImplLayoutRuns const &)
Definition: sallayout.cxx:636
virtual ~MultiSalLayout() override
Definition: sallayout.cxx:632
void GetCaretPositions(std::vector< double > &rCaretPositions, const OUString &rStr) const override
Definition: sallayout.cxx:1081
bool IsKashidaPosValid(int nCharPos, int nNextCharPos) const override
Definition: sallayout.cxx:1150
MultiSalLayout(std::unique_ptr< SalLayout > pBaseLayout)
Definition: sallayout.cxx:612
constexpr tools::Long Y() const
constexpr tools::Long X() const
bool IsValid() const
const rtl::Reference< LogicalFontInstance > & GetFont() const
void AppendImpl(SalLayoutGlyphsImpl *pImpl)
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 double FillDXArray(std::vector< double > *pDXArray, const OUString &rStr) const =0
virtual bool GetOutline(basegfx::B2DPolyPolygonVector &) const
Definition: sallayout.cxx:184
basegfx::B2DPoint GetDrawPosition(const basegfx::B2DPoint &rRelative=basegfx::B2DPoint(0, 0)) const
Definition: sallayout.cxx:144
bool mbSubpixelPositioning
Definition: vcllayout.hxx:128
int mnMinCharPos
Definition: vcllayout.hxx:119
bool GetBoundRect(tools::Rectangle &) const
Definition: sallayout.cxx:217
Point maDrawOffset
Definition: vcllayout.hxx:125
basegfx::B2DPoint & DrawBase()
Definition: vcllayout.hxx:72
Point & DrawOffset()
Definition: vcllayout.hxx:74
virtual void AdjustLayout(vcl::text::ImplLayoutArgs &)
Definition: sallayout.cxx:136
basegfx::B2DPoint maDrawBase
Definition: vcllayout.hxx:126
virtual void DrawText(SalGraphics &) const =0
virtual bool GetNextGlyph(const GlyphItem **pGlyph, basegfx::B2DPoint &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr) 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)
sal_Int16 mnLevel
double toRadians(D x)
sal_Int64 n
#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