LibreOffice Module vcl (master) 1
impglyphitem.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 <impglyphitem.hxx>
21#include <utility>
23#include <vcl/vcllayout.hxx>
24#include <vcl/lazydelete.hxx>
25#include <tools/stream.hxx>
27#include <TextLayoutCache.hxx>
28#include <officecfg/Office/Common.hxx>
29
30#include <unicode/ubidi.h>
31#include <unicode/uchar.h>
32
33// These need being explicit because of SalLayoutGlyphsImpl being private in vcl.
35
37
39{
40 std::swap(m_pImpl, rOther.m_pImpl);
41 std::swap(m_pExtraImpls, rOther.m_pExtraImpls);
42}
43
45{
46 if (this != &rOther)
47 {
48 std::swap(m_pImpl, rOther.m_pImpl);
49 std::swap(m_pExtraImpls, rOther.m_pExtraImpls);
50 }
51 return *this;
52}
53
55{
56 if (m_pImpl == nullptr)
57 return false;
58 if (!m_pImpl->IsValid())
59 return false;
60 if (m_pExtraImpls)
61 for (std::unique_ptr<SalLayoutGlyphsImpl> const& impl : *m_pExtraImpls)
62 if (!impl->IsValid())
63 return false;
64 return true;
65}
66
68{
69 // Invalidating is in fact simply clearing.
70 m_pImpl.reset();
71 m_pExtraImpls.reset();
72}
73
74SalLayoutGlyphsImpl* SalLayoutGlyphs::Impl(unsigned int nLevel) const
75{
76 if (nLevel == 0)
77 return m_pImpl.get();
78 if (m_pExtraImpls != nullptr && nLevel - 1 < m_pExtraImpls->size())
79 return (*m_pExtraImpls)[nLevel - 1].get();
80 return nullptr;
81}
82
84{
85 if (!m_pImpl)
86 m_pImpl.reset(pImpl);
87 else
88 {
89 if (!m_pExtraImpls)
90 m_pExtraImpls.reset(new std::vector<std::unique_ptr<SalLayoutGlyphsImpl>>);
91 m_pExtraImpls->emplace_back(pImpl);
92 }
93}
94
96
97// Clone, but only glyphs in the given range in the original text string.
98// It is possible the given range may not be cloned, in which case this returns nullptr.
99SalLayoutGlyphsImpl* SalLayoutGlyphsImpl::cloneCharRange(sal_Int32 index, sal_Int32 length) const
100{
101 std::unique_ptr<SalLayoutGlyphsImpl> copy(new SalLayoutGlyphsImpl(*GetFont()));
102 copy->SetFlags(GetFlags());
103 if (empty())
104 return copy.release();
105 bool rtl = front().IsRTLGlyph();
106 // Avoid mixing LTR/RTL or layouts that do not have it set explicitly (BiDiStrong). Otherwise
107 // the subset may not quite match what would a real layout call give (e.g. some characters with neutral
108 // direction such as space might have different LTR/RTL flag). It seems bailing out here mostly
109 // avoid relatively rare corner cases and doesn't matter for performance.
110 // This is also checked in SalLayoutGlyphsCache::GetLayoutGlyphs() below.
112 || rtl != bool(GetFlags() & SalLayoutFlags::BiDiRtl))
113 return nullptr;
114 copy->reserve(std::min<size_t>(size(), length));
115 sal_Int32 beginPos = index;
116 sal_Int32 endPos = index + length;
117 const_iterator pos;
118 if (rtl)
119 {
120 // Glyphs are in reverse order for RTL.
121 beginPos = index + length - 1;
122 endPos = index - 1;
123 // Skip glyphs that are in the string after the given index, i.e. are before the glyphs
124 // we want.
125 pos = std::partition_point(
126 begin(), end(), [beginPos](const GlyphItem& it) { return it.charPos() > beginPos; });
127 }
128 else
129 {
130 // Skip glyphs that are in the string before the given index (glyphs are sorted by charPos()).
131 pos = std::partition_point(
132 begin(), end(), [beginPos](const GlyphItem& it) { return it.charPos() < beginPos; });
133 }
134 if (pos == end())
135 return nullptr;
136 // Require a start at the exact position given, otherwise bail out.
137 if (pos->charPos() != beginPos)
138 return nullptr;
139 // For RTL make sure we're not cutting in the middle of a multi-character glyph
140 // (for non-RTL charPos is always the start of a multi-character glyph).
141 if (rtl && pos->charPos() + pos->charCount() > beginPos + 1)
142 return nullptr;
143 if (!isSafeToBreak(pos, rtl))
144 return nullptr;
145 // LinearPos needs adjusting to start at xOffset/yOffset for the first item,
146 // that's how it's computed in GenericSalLayout::LayoutText().
147 DevicePoint zeroPoint = pos->linearPos() - DevicePoint(pos->xOffset(), pos->yOffset());
148 // Add and adjust all glyphs until the given length.
149 // The check is written as 'charPos + charCount <= endPos' rather than 'charPos < endPos'
150 // (or similarly for RTL) to make sure we include complete glyphs. If a glyph is composed
151 // from several characters, we should not cut in the middle of those characters, so this
152 // checks the glyph is entirely in the given character range. If it is not, this will end
153 // the loop and the later 'pos->charPos() != endPos' check will fail and bail out.
154 // CppunitTest_sw_layoutwriter's testCombiningCharacterCursorPosition would fail without this.
155 while (pos != end()
156 && (rtl ? pos->charPos() - pos->charCount() >= endPos
157 : pos->charPos() + pos->charCount() <= endPos))
158 {
159 if (pos->IsRTLGlyph() != rtl)
160 return nullptr; // Don't mix RTL and non-RTL runs.
161 // HACK: When running CppunitTest_sw_uiwriter3's testTdf104649 on Mac there's glyph
162 // with id 1232 that has 0 charCount, 0 origWidth and inconsistent xOffset (sometimes 0,
163 // but sometimes not). Possibly font or Harfbuzz bug? It's extremely rare, so simply bail out.
164 if (pos->charCount() == 0 && pos->origWidth() == 0)
165 return nullptr;
166 copy->push_back(*pos);
167 copy->back().setLinearPos(copy->back().linearPos() - zeroPoint);
168 ++pos;
169 }
170 if (pos != end())
171 {
172 // Fail if the next character is not at the expected past-end position. For RTL check
173 // that we're not cutting in the middle of a multi-character glyph.
174 if (rtl ? pos->charPos() + pos->charCount() != endPos + 1 : pos->charPos() != endPos)
175 return nullptr;
176 if (!isSafeToBreak(pos, rtl))
177 return nullptr;
178 }
179 return copy.release();
180}
181
182bool SalLayoutGlyphsImpl::isSafeToBreak(const_iterator pos, bool rtl) const
183{
184 if (rtl)
185 {
186 // RTL is more complicated, because HB_GLYPH_FLAG_UNSAFE_TO_BREAK talks about beginning
187 // of a cluster, which refers to the text, not glyphs. This function is called
188 // for the first glyph of the subset and the first glyph after the subset, but since
189 // the glyphs are backwards, and we need the beginning of cluster at the start of the text
190 // and beginning of the cluster after the text, we need to check glyphs before this position.
191 if (pos == begin())
192 return true;
193 --pos;
194 }
195 // Don't create a subset if it's not safe to break at the beginning or end of the sequence
196 // (https://harfbuzz.github.io/harfbuzz-hb-buffer.html#hb-glyph-flags-t).
197 if (pos->IsUnsafeToBreak() || (pos->IsInCluster() && !pos->IsClusterStart()))
198 return false;
199 return true;
200}
201
202#ifdef DBG_UTIL
204{
205 if (!GetFont()->mxFontMetric->CompareDeviceIndependentFontAttributes(
206 *other->GetFont()->mxFontMetric))
207 return false;
208 if (GetFlags() != other->GetFlags())
209 return false;
210 if (empty() || other->empty())
211 return empty() == other->empty();
212 if (size() != other->size())
213 return false;
214 for (size_t pos = 0; pos < size(); ++pos)
215 {
216 if (!(*this)[pos].isLayoutEquivalent((*other)[pos]))
217 return false;
218 }
219 return true;
220}
221#endif
222
224{
225 if (!m_rFontInstance.is())
226 return false;
227 return true;
228}
229
231
233{
236 ? officecfg::Office::Common::Cache::Font::GlyphsCacheSize::get()
237 : 20000);
238 return cache.get();
239}
240
241static UBiDiDirection getBiDiDirection(const OUString& text, sal_Int32 index, sal_Int32 len)
242{
243 // Return whether all character are LTR, RTL, neutral or whether it's mixed.
244 // This is sort of ubidi_getBaseDirection() and ubidi_getDirection(),
245 // but it's meant to be fast but also check all characters.
246 sal_Int32 end = index + len;
247 UBiDiDirection direction = UBIDI_NEUTRAL;
248 while (index < end)
249 {
250 switch (u_charDirection(text.iterateCodePoints(&index)))
251 {
252 // Only characters with strong direction.
253 case U_LEFT_TO_RIGHT:
254 if (direction == UBIDI_RTL)
255 return UBIDI_MIXED;
256 direction = UBIDI_LTR;
257 break;
258 case U_RIGHT_TO_LEFT:
259 case U_RIGHT_TO_LEFT_ARABIC:
260 if (direction == UBIDI_LTR)
261 return UBIDI_MIXED;
262 direction = UBIDI_RTL;
263 break;
264 default:
265 break;
266 }
267 }
268 return direction;
269}
270
272 const OutputDevice* outputDevice, const OUString& text,
273 sal_Int32 index, sal_Int32 len)
274{
275 // tdf#149264: We need to check if the text is LTR, RTL or mixed. Apparently
276 // harfbuzz doesn't give reproducible results (or possibly HB_GLYPH_FLAG_UNSAFE_TO_BREAK
277 // is not reliable?) when asked to lay out RTL text as LTR. So require that the whole
278 // subset ir either LTR or RTL.
279 UBiDiDirection direction = getBiDiDirection(text, index, len);
280 if (direction == UBIDI_MIXED)
281 return SalLayoutGlyphs();
282 SalLayoutGlyphs ret;
283 for (int level = 0;; ++level)
284 {
285 const SalLayoutGlyphsImpl* sourceLevel = source.Impl(level);
286 if (sourceLevel == nullptr)
287 break;
288 bool sourceRtl = bool(sourceLevel->GetFlags() & SalLayoutFlags::BiDiRtl);
289 if ((direction == UBIDI_LTR && sourceRtl) || (direction == UBIDI_RTL && !sourceRtl))
290 return SalLayoutGlyphs();
291 SalLayoutGlyphsImpl* cloned = sourceLevel->cloneCharRange(index, len);
292 // If the glyphs range cannot be cloned, bail out.
293 if (cloned == nullptr)
294 return SalLayoutGlyphs();
295 // If the entire string is mixed LTR/RTL but the subset is only LTR,
296 // then make sure the flags match that, otherwise checkGlyphsEqual()
297 // would assert on flags being different.
298 cloned->SetFlags(cloned->GetFlags()
299 | outputDevice->GetBiDiLayoutFlags(text, index, index + len));
300 ret.AppendImpl(cloned);
301 }
302 return ret;
303}
304
305#ifdef DBG_UTIL
306static void checkGlyphsEqual(const SalLayoutGlyphs& g1, const SalLayoutGlyphs& g2)
307{
308 for (int level = 0;; ++level)
309 {
310 const SalLayoutGlyphsImpl* l1 = g1.Impl(level);
311 const SalLayoutGlyphsImpl* l2 = g2.Impl(level);
312 if (l1 == nullptr || l2 == nullptr)
313 {
314 assert(l1 == l2);
315 break;
316 }
317 assert(l1->isLayoutEquivalent(l2));
318 }
319}
320#endif
321
322const SalLayoutGlyphs*
324 sal_Int32 nIndex, sal_Int32 nLen, tools::Long nLogicWidth,
325 const vcl::text::TextLayoutCache* layoutCache)
326{
327 if (nLen == 0)
328 return nullptr;
329 const CachedGlyphsKey key(outputDevice, text, nIndex, nLen, nLogicWidth);
330 // for now disable if the font is the one seen in tdf#119074
331 // https://github.com/harfbuzz/harfbuzz/issues/3824
332 if (key.fontMetric.GetFamilyName() == "XB Roya")
333 return nullptr;
335 if (it != mCachedGlyphs.end())
336 {
337 if (it->second.IsValid())
338 return &it->second;
339 // Do not try to create the layout here. If a cache item exists, it's already
340 // been attempted and the layout was invalid (this happens with MultiSalLayout).
341 // So in that case this is a cached failure.
342 return nullptr;
343 }
344 bool resetLastSubstringKey = true;
345 const sal_Unicode nbSpace = 0xa0; // non-breaking space
346 // SalLayoutGlyphsImpl::cloneCharRange() requires BiDiStrong, so if not set, do not even try.
347 bool skipGlyphSubsets
349 if ((nIndex != 0 || nLen != text.getLength()) && !skipGlyphSubsets)
350 {
351 // The glyphs functions are often called first for an entire string
352 // and then with an increasing starting index until the end of the string.
353 // Which means it's possible to get the glyphs faster by just copying
354 // a subset of the full glyphs and adjusting as necessary.
355 if (mLastTemporaryKey.has_value() && mLastTemporaryKey == key)
356 return &mLastTemporaryGlyphs;
357 const CachedGlyphsKey keyWhole(outputDevice, text, 0, text.getLength(), nLogicWidth);
359 if (itWhole == mCachedGlyphs.end())
360 {
361 // This function may often be called repeatedly for segments of the same string,
362 // in which case it is more efficient to cache glyphs for the entire string
363 // and then return subsets of them. So if a second call either starts at the same
364 // position or starts at the end of the previous call, cache the entire string.
365 // This used to do this only for the first two segments of the string,
366 // but that missed the case when the font slightly changed e.g. because of the first
367 // part being underlined. Doing this for any two segments allows this optimization
368 // even when the prefix of the string would use a different font.
369 // TODO: Can those font differences be ignored?
370 // Writer layouts tests enable SAL_NON_APPLICATION_FONT_USE=abort in order
371 // to make PrintFontManager::Substitute() abort if font fallback happens. When
372 // laying out the entire string the chance this happens increases (e.g. testAbi11870
373 // normally calls this function only for a part of a string, but this optimization
374 // lays out the entire string and causes a fallback). Since this optimization
375 // does not change result of this function, simply disable it for those tests.
376 static const bool bAbortOnFontSubstitute = [] {
377 const char* pEnv = getenv("SAL_NON_APPLICATION_FONT_USE");
378 return pEnv && strcmp(pEnv, "abort") == 0;
379 }();
380 if (mLastSubstringKey.has_value() && !bAbortOnFontSubstitute)
381 {
382 sal_Int32 pos = nIndex;
383 if (mLastSubstringKey->len < pos && text[pos - 1] == nbSpace)
384 --pos; // Writer skips a non-breaking space, so skip that character too.
385 if ((mLastSubstringKey->len == pos || mLastSubstringKey->index == nIndex)
387 == CachedGlyphsKey(outputDevice, text, mLastSubstringKey->index,
388 mLastSubstringKey->len, nLogicWidth))
389 {
390 GetLayoutGlyphs(outputDevice, text, 0, text.getLength(), nLogicWidth,
391 layoutCache);
392 itWhole = mCachedGlyphs.find(keyWhole);
393 }
394 else
395 mLastSubstringKey.reset();
396 }
397 if (!mLastSubstringKey.has_value())
398 {
399 mLastSubstringKey = key;
400 resetLastSubstringKey = false;
401 }
402 }
403 if (itWhole != mCachedGlyphs.end() && itWhole->second.IsValid())
404 {
405 mLastSubstringKey.reset();
407 = makeGlyphsSubset(itWhole->second, outputDevice, text, nIndex, nLen);
409 {
410 mLastTemporaryKey = key;
411#ifdef DBG_UTIL
412 std::shared_ptr<const vcl::text::TextLayoutCache> tmpLayoutCache;
413 if (layoutCache == nullptr)
414 {
416 layoutCache = tmpLayoutCache.get();
417 }
418 // Check if the subset result really matches what we would get normally,
419 // to make sure corner cases are handled well (see SalLayoutGlyphsImpl::cloneCharRange()).
420 std::unique_ptr<SalLayout> layout
421 = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), nLogicWidth, {}, {},
422 SalLayoutFlags::GlyphItemsOnly, layoutCache);
423 assert(layout);
424 checkGlyphsEqual(mLastTemporaryGlyphs, layout->GetGlyphs());
425#endif
426 return &mLastTemporaryGlyphs;
427 }
428 }
429 }
430 if (resetLastSubstringKey)
431 {
432 // Writer does non-breaking space differently (not as part of the string), so in that
433 // case ignore that call and still allow finding two adjacent substrings that have
434 // the non-breaking space between them.
435 if (nLen != 1 || text[nIndex] != nbSpace)
436 mLastSubstringKey.reset();
437 }
438
439 std::shared_ptr<const vcl::text::TextLayoutCache> tmpLayoutCache;
440 if (layoutCache == nullptr)
441 {
443 layoutCache = tmpLayoutCache.get();
444 }
445 std::unique_ptr<SalLayout> layout
446 = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), nLogicWidth, {}, {},
447 SalLayoutFlags::GlyphItemsOnly, layoutCache);
448 if (layout)
449 {
450 SalLayoutGlyphs glyphs = layout->GetGlyphs();
451 if (glyphs.IsValid())
452 {
453 // TODO: Fallbacks do not work reliably (fallback font not included in the key),
454 // so do not cache (but still return once, using the temporary without a key set).
455 if (!mbCacheGlyphsWhenDoingFallbackFonts && glyphs.Impl(1) != nullptr)
456 {
457 mLastTemporaryGlyphs = std::move(glyphs);
458 mLastTemporaryKey.reset();
459 return &mLastTemporaryGlyphs;
460 }
461 mCachedGlyphs.insert(std::make_pair(key, std::move(glyphs)));
462 assert(mCachedGlyphs.find(key)
463 == mCachedGlyphs.begin()); // newly inserted item is first
464 return &mCachedGlyphs.begin()->second;
465 }
466 }
467 // Failure, cache it too as invalid glyphs.
468 mCachedGlyphs.insert(std::make_pair(key, SalLayoutGlyphs()));
469 return nullptr;
470}
471
473{
475 if (!bOK)
476 clear();
477}
478
480 const VclPtr<const OutputDevice>& outputDevice, OUString t, sal_Int32 i, sal_Int32 l,
481 tools::Long w)
482 : text(std::move(t))
483 , index(i)
484 , len(l)
485 , logicWidth(w)
486 // we also need to save things used in OutputDevice::ImplPrepareLayoutArgs(), in case they
487 // change in the output device, plus mapMode affects the sizes.
488 , fontMetric(outputDevice->GetFontMetric())
489 // TODO It would be possible to get a better hit ratio if mapMode wasn't part of the key
490 // and results that differ only in mapmode would have coordinates adjusted based on that.
491 // That would occasionally lead to rounding errors (at least differences that would
492 // make checkGlyphsEqual() fail).
493 , mapMode(outputDevice->GetMapMode())
494 , rtl(outputDevice->IsRTLEnabled())
495 , layoutMode(outputDevice->GetLayoutMode())
496 , digitLanguage(outputDevice->GetDigitLanguage())
497{
498 const LogicalFontInstance* fi = outputDevice->GetFontInstance();
500
505
506 hashValue = 0;
511 o3tl::hash_combine(hashValue, outputDevice.get());
512 // Need to use IgnoreColor, because sometimes the color changes, but it's irrelevant
513 // for text layout (and also obsolete in vcl::Font).
515 // For some reason font scale may differ even if vcl::Font is the same,
516 // so explicitly check it too.
526}
527
529{
530 return hashValue == other.hashValue && index == other.index && len == other.len
531 && logicWidth == other.logicWidth && mapMode == other.mapMode && rtl == other.rtl
532 && disabledLigatures == other.disabledLigatures
533 && artificialItalic == other.artificialItalic && artificialBold == other.artificialBold
534 && layoutMode == other.layoutMode && digitLanguage == other.digitLanguage
535 && fontScaleX == other.fontScaleX && fontScaleY == other.fontScaleY
536 && fontMetric.EqualIgnoreColor(other.fontMetric)
538 // Slower things last in the comparison.
539}
540
542{
543 size_t cost = 0;
544 for (int level = 0;; ++level)
545 {
546 const SalLayoutGlyphsImpl* impl = glyphs.Impl(level);
547 if (impl == nullptr)
548 break;
549 // Count size in bytes, both the SalLayoutGlyphsImpl instance and contained GlyphItem's.
550 cost += sizeof(*impl);
551 cost += impl->size() * sizeof(impl->front());
552 }
553 return cost;
554}
555
556/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
XPropertyListType t
FontPitch GetPitch() const
size_t GetHashValueIgnoreColor() const
Definition: fontmetric.cxx:126
int charPos() const
void GetScale(double *nXScale, double *nYScale) const
bool NeedsArtificialItalic() const
const vcl::font::FontSelectPattern & GetFontSelectPattern() const
size_t GetHashValue() const
Definition: mapmod.cxx:135
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:170
LogicalFontInstance const * GetFontInstance() const
SAL_DLLPRIVATE SalLayoutFlags GetBiDiLayoutFlags(std::u16string_view rStr, const sal_Int32 nMinIndex, const sal_Int32 nEndIndex) const
Definition: text.cxx:1221
vcl::text::ComplexTextLayoutFlags GetLayoutMode() const
Definition: outdev.hxx:490
std::unique_ptr< SalLayout > ImplLayout(const OUString &, sal_Int32 nIndex, sal_Int32 nLen, const Point &rLogicPos=Point(0, 0), tools::Long nLogicWidth=0, KernArraySpan aKernArray=KernArraySpan(), o3tl::span< const sal_Bool > pKashidaArray={}, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::text::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1302
A cache for SalLayoutGlyphs objects.
std::optional< CachedGlyphsKey > mLastTemporaryKey
void SetCacheGlyphsWhenDoingFallbackFonts(bool bOK)
Normally, we cannot cache glyphs when doing font fallback, because the font fallbacks can cache durin...
SalLayoutGlyphs mLastTemporaryGlyphs
static SalLayoutGlyphsCache * self()
std::optional< CachedGlyphsKey > mLastSubstringKey
const SalLayoutGlyphs * GetLayoutGlyphs(VclPtr< const OutputDevice > outputDevice, const OUString &text, const vcl::text::TextLayoutCache *layoutCache=nullptr)
SalLayoutGlyphsImpl(LogicalFontInstance &rFontInstance)
void SetFlags(SalLayoutFlags flags)
SalLayoutFlags GetFlags() const
rtl::Reference< LogicalFontInstance > m_rFontInstance
SalLayoutGlyphsImpl * clone() const
bool IsValid() const
bool isSafeToBreak(const_iterator pos, bool rtl) const
bool isLayoutEquivalent(const SalLayoutGlyphsImpl *other) const
SalLayoutGlyphsImpl * cloneCharRange(sal_Int32 index, sal_Int32 length) const
const rtl::Reference< LogicalFontInstance > & GetFont() const
SalLayoutGlyphsImpl * Impl(unsigned int nLevel) const
void AppendImpl(SalLayoutGlyphsImpl *pImpl)
SalLayoutGlyphs & operator=(const SalLayoutGlyphs &)=delete
std::unique_ptr< SalLayoutGlyphsImpl > m_pImpl
Definition: glyphitem.hxx:33
std::unique_ptr< std::vector< std::unique_ptr< SalLayoutGlyphsImpl > > > m_pExtraImpls
Definition: glyphitem.hxx:37
bool IsValid() const
reference_type * get() const
Get the body.
Definition: vclptr.hxx:143
void insert(key_value_pair_t &rPair)
list_const_iterator_t end() const
list_const_iterator_t begin() const
list_const_iterator_t find(const Key &key)
static bool IsFuzzing()
const OUString & GetFamilyName() const
Definition: font/font.cxx:904
static std::shared_ptr< const vcl::text::TextLayoutCache > Create(OUString const &)
basegfx::B2DPoint DevicePoint
PITCH_FIXED
static SalLayoutGlyphs makeGlyphsSubset(const SalLayoutGlyphs &source, const OutputDevice *outputDevice, const OUString &text, sal_Int32 index, sal_Int32 len)
static UBiDiDirection getBiDiDirection(const OUString &text, sal_Int32 index, sal_Int32 len)
static void checkGlyphsEqual(const SalLayoutGlyphs &g1, const SalLayoutGlyphs &g2)
sal_Int32 nIndex
def text(shape, orig_st)
size
void copy(const fs::path &src, const fs::path &dest)
int i
index
std::enable_if_t<(sizeof(N)==4)> hash_combine(N &nSeed, T const *pValue, size_t nCount)
enumrange< T >::Iterator begin(enumrange< T >)
end
long Long
sal_Int32 w
bool operator==(const CachedGlyphsKey &other) const
vcl::text::ComplexTextLayoutFlags layoutMode
CachedGlyphsKey(const VclPtr< const OutputDevice > &dev, OUString t, sal_Int32 i, sal_Int32 l, tools::Long w)
size_t operator()(const SalLayoutGlyphs &) const
UNDERLYING_TYPE get() const
sal_uInt16 sal_Unicode
size_t pos