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