36#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
38#include <unicode/uchar.h>
40#include <hb-graphite2.h>
44#if !HB_VERSION_ATLEAST(1, 1, 0)
56 static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
64 , mpVertGlyphs(nullptr)
65 , mbFuzzing(
utl::ConfigManager::IsFuzzing())
77 if (!sLanguage.isEmpty())
82 hb_feature_t aFeature { rFeat.m_nTag, rFeat.m_nValue, rFeat.m_nStart, rFeat.m_nEnd };
94 hb_direction_t maDirection;
100#if U_ICU_VERSION_MAJOR_NUM >= 63
101 enum class VerticalOrientation {
103 Rotated = U_VO_ROTATED,
104 TransformedUpright = U_VO_TRANSFORMED_UPRIGHT,
105 TransformedRotated = U_VO_TRANSFORMED_ROTATED
111 enum class VerticalOrientation {
114 TransformedUpright = 2,
115 TransformedRotated = 3
123 if ((cCh == 0xff1a || cCh == 0xff1b
124 || cCh == 0x2ca || cCh == 0x2cb || cCh == 0x2c7 || cCh == 0x2d9)
126 return VerticalOrientation::TransformedUpright;
128#if U_ICU_VERSION_MAJOR_NUM >= 63
129 int32_t nRet = u_getIntPropertyValue(cCh, UCHAR_VERTICAL_ORIENTATION);
147 SAL_WARN(
"vcl.gdi",
"Getting VerticalOrientation for codepoint outside Unicode range");
151 return VerticalOrientation(nRet);
179 int nGraphemeEndPos =
181 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
183 rArgs.
mrStr.iterateCodePoints(&nCharPos);
184 int nGraphemeStartPos =
185 mxBreak->previousCharacters(rArgs.
mrStr, nCharPos, aLocale,
186 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
189 nGraphemeStartPos = std::max(rArgs.
mnMinCharPos, nGraphemeStartPos);
190 nGraphemeEndPos = std::min(rArgs.
mnEndCharPos, nGraphemeEndPos);
192 rArgs.
AddFallbackRun(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
223 hb_codepoint_t nGlyphIndex = 0;
225 if (!hb_font_get_glyph(pHbFont, aChar, aVariationSelector, &nGlyphIndex))
230 hb_face_t* pHbFace = hb_font_get_face(pHbFont);
234 hb_set_t* pLookups = hb_set_create();
235 hb_tag_t
const pFeatures[] = { HB_TAG(
'v',
'e',
'r',
't'), HB_TAG_NONE };
236 hb_ot_layout_collect_lookups(pHbFace, HB_OT_TAG_GSUB,
nullptr,
nullptr, pFeatures, pLookups);
237 if (!hb_set_is_empty(pLookups))
241 hb_codepoint_t nIdx = HB_SET_VALUE_INVALID;
242 while (hb_set_next(pLookups, &nIdx))
244 hb_set_t* pGlyphs = hb_set_create();
245 hb_ot_layout_lookup_collect_glyphs(pHbFace, HB_OT_TAG_GSUB, nIdx,
285 std::optional<vcl::text::TextLayoutCache> oNewScriptRun;
294 pTextLayout = &*oNewScriptRun;
304 hb_font_extents_t extents;
305 if (hb_font_get_h_extents(pHbFont, &extents))
306 nBaseOffset = ( extents.ascender + extents.descender ) / 2;
309 hb_buffer_t* pHbBuffer = hb_buffer_create();
310 hb_buffer_pre_allocate(pHbBuffer, nGlyphCapacity);
311#if !HB_VERSION_ATLEAST(1, 1, 0)
313 hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
320 maFeatures.push_back({ HB_TAG(
'k',
'e',
'r',
'n'), 0, 0,
static_cast<unsigned int>(-1) });
326 maFeatures.push_back({ HB_TAG(
'l',
'i',
'g',
'a'), 0, 0,
static_cast<unsigned int>(-1) });
338 int nBidiMinRunPos, nBidiEndRunPos;
340 if (!rArgs.
GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
344 std::vector<SubRun> aSubRuns;
345 int nCurrentPos = nBidiMinRunPos;
347 for (; k < pTextLayout->
runs.size(); ++k)
350 if (rRun.
nStart <= nCurrentPos && nCurrentPos < rRun.
nEnd)
358 hb_script_t aScript = hb_icu_script_to_script(pTextLayout->
runs[k].nCode);
359 aSubRuns.push_back({ nBidiMinRunPos, nBidiEndRunPos, aScript, bRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR });
363 while (nCurrentPos < nBidiEndRunPos && k < pTextLayout->runs.size())
365 int32_t nMinRunPos = nCurrentPos;
366 int32_t nEndRunPos = std::min(pTextLayout->
runs[k].nEnd, nBidiEndRunPos);
367 hb_direction_t
aDirection = bRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
368 hb_script_t aScript = hb_icu_script_to_script(pTextLayout->
runs[k].nCode);
373 sal_Int32 nIdx = nMinRunPos;
374 while (nIdx < nEndRunPos)
376 sal_Int32 nPrevIdx = nIdx;
378 VerticalOrientation aVo = GetVerticalOrientation(aChar, rArgs.
maLanguageTag);
381 if (nIdx < nEndRunPos)
383 sal_Int32 nNextIdx = nIdx;
384 sal_UCS4 aNextChar = rArgs.
mrStr.iterateCodePoints(&nNextIdx);
385 if (u_hasBinaryProperty(aNextChar, UCHAR_VARIATION_SELECTOR))
388 aVariationSelector = aNextChar;
399 if (aVo == VerticalOrientation::Upright ||
400 aVo == VerticalOrientation::TransformedUpright ||
401 (aVo == VerticalOrientation::TransformedRotated &&
408 aDirection = bRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
411 if (aSubRuns.empty() || aSubRuns.back().maDirection !=
aDirection)
412 aSubRuns.push_back({ nPrevIdx, nIdx, aScript,
aDirection });
414 aSubRuns.back().mnEnd = nIdx;
419 aSubRuns.push_back({ nMinRunPos, nEndRunPos, aScript,
aDirection });
422 nCurrentPos = nEndRunPos;
430 std::reverse(aSubRuns.begin(), aSubRuns.end());
432 for (
const auto& aSubRun : aSubRuns)
434 hb_buffer_clear_contents(pHbBuffer);
436 const int nMinRunPos = aSubRun.mnMin;
437 const int nEndRunPos = aSubRun.mnEnd;
438 const int nRunLen = nEndRunPos - nMinRunPos;
440 int nHbFlags = HB_BUFFER_FLAGS_DEFAULT;
442 nHbFlags |= HB_BUFFER_FLAG_BOT;
444 nHbFlags |= HB_BUFFER_FLAG_EOT;
446 hb_buffer_set_direction(pHbBuffer, aSubRun.maDirection);
447 hb_buffer_set_script(pHbBuffer, aSubRun.maScript);
450 hb_buffer_set_language(pHbBuffer, hb_language_from_string(
msLanguage.getStr(),
msLanguage.getLength()));
455 hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), sLanguage.getLength()));
457 hb_buffer_set_flags(pHbBuffer,
static_cast<hb_buffer_flags_t
>(nHbFlags));
459 pHbBuffer,
reinterpret_cast<uint16_t
const *
>(pStr),
nLength,
460 nMinRunPos, nRunLen);
461 hb_buffer_set_cluster_level(pHbBuffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
467 const char*
const pHbShapers[] = {
"graphite2",
"coretext_aat",
"ot",
"fallback",
nullptr };
472 int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
473 hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer,
nullptr);
474 hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer,
nullptr);
476 for (
int i = 0;
i < nRunGlyphCount; ++
i) {
477 int32_t nGlyphIndex = pHbGlyphInfos[
i].codepoint;
478 int32_t nCharPos = pHbGlyphInfos[
i].cluster;
479 int32_t nCharCount = 0;
480 bool bInCluster =
false;
481 bool bClusterStart =
false;
488 if (
i > 0 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i - 1].cluster)
498 int32_t nNextCharPos = nCharPos;
499 while (nNextCharPos == nCharPos && j < nRunGlyphCount)
500 nNextCharPos = pHbGlyphInfos[j++].cluster;
502 if (nNextCharPos == nCharPos)
503 nNextCharPos = nEndRunPos;
504 nCharCount = nNextCharPos - nCharPos;
505 if ((
i == 0 || pHbGlyphInfos[
i].cluster != pHbGlyphInfos[
i - 1].cluster) &&
506 (
i < nRunGlyphCount - 1 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i + 1].cluster))
507 bClusterStart =
true;
514 if (
i < nRunGlyphCount - 1 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i + 1].cluster)
524 int32_t nNextCharPos = nCharPos;
525 while (nNextCharPos == nCharPos && j >= 0)
526 nNextCharPos = pHbGlyphInfos[j--].cluster;
528 if (nNextCharPos == nCharPos)
529 nNextCharPos = nEndRunPos;
530 nCharCount = nNextCharPos - nCharPos;
531 if ((
i == nRunGlyphCount - 1 || pHbGlyphInfos[
i].cluster != pHbGlyphInfos[
i + 1].cluster) &&
532 (
i > 0 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i - 1].cluster))
533 bClusterStart =
true;
558 if (u_getIntPropertyValue(aChar, UCHAR_GENERAL_CATEGORY) == U_NON_SPACING_MARK)
561 if (u_isUWhiteSpace(aChar))
564 if (aSubRun.maScript == HB_SCRIPT_ARABIC &&
565 HB_DIRECTION_IS_BACKWARD(aSubRun.maDirection) &&
572#if HB_VERSION_ATLEAST(1, 5, 0)
573 if (hb_glyph_info_get_glyph_flags(&pHbGlyphInfos[
i]) & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
581 if (aSubRun.maDirection == HB_DIRECTION_TTB)
585 nAdvance = -pHbPositions[
i].y_advance;
586 nXOffset = -pHbPositions[
i].y_offset;
587 nYOffset = -pHbPositions[
i].x_offset - nBaseOffset;
589 if (
GetFont().NeedOffsetCorrection(pHbPositions[
i].y_offset))
597 nXOffset = -(aRect.
Top() / nXScale + ( pHbPositions[
i].y_advance
598 + ( aRect.
GetHeight() / nXScale ) ) / 2 );
604 nAdvance = pHbPositions[
i].x_advance;
605 nXOffset = pHbPositions[
i].x_offset;
606 nYOffset = -pHbPositions[
i].y_offset;
609 nAdvance = std::lround(nAdvance * nXScale);
610 nXOffset = std::lround(nXOffset * nXScale);
611 nYOffset = std::lround(nYOffset * nYScale);
614 const GlyphItem aGI(nCharPos, nCharCount, nGlyphIndex, aNewPos, nGlyphFlags,
615 nAdvance, nXOffset, nYOffset);
623 hb_buffer_destroy(pHbBuffer);
637 rCharWidths.resize(nCharCount, 0);
644 rCharWidths[
nIndex] += aGlyphItem.newWidth();
668 std::vector<DeviceCoordinate> aOldCharWidths;
669 std::unique_ptr<DC[]>
const pNewCharWidths(
new DC[nCharCount]);
675 for (
int i = 0;
i < nCharCount; ++
i)
678 pNewCharWidths[
i] = pDXArray[
i];
680 pNewCharWidths[
i] = pDXArray[
i] - pDXArray[
i - 1];
683 bool bKashidaJustify =
false;
685 hb_codepoint_t nKashidaIndex = 0;
690 if (hb_font_get_glyph(pHbFont, 0x0640, 0, &nKashidaIndex))
692 bKashidaJustify = nKashidaWidth != 0;
697 std::map<size_t, DeviceCoordinate> pKashidas;
711 nDiff += pNewCharWidths[nCharPos + j] - aOldCharWidths[nCharPos + j];
759 pKashidas[
i] = nDiff;
780 if (!bKashidaJustify || pKashidas.empty())
783 size_t nInserted = 0;
784 for (
auto const& pKashida : pKashidas)
786 auto pGlyphIter =
m_GlyphItems.begin() + nInserted + pKashida.first;
793 if (nTotalWidth > nKashidaWidth)
794 nCopies = nTotalWidth / nKashidaWidth;
805 nOverlap = nExcess / (nCopies - 1);
808 DevicePoint aPos(pGlyphIter->linearPos().getX() - nTotalWidth, 0);
809 int nCharPos = pGlyphIter->charPos();
813 GlyphItem aKashida(nCharPos, 0, nKashidaIndex, aPos, nFlags, nKashidaWidth, 0, 0);
815 aPos.
adjustX(nKashidaWidth - nOverlap);
826 if (pIter->charPos() == nCharPos)
837 if (pIter->glyphId() == 0)
843 for (
auto pPrev = pIter - 1; pPrev !=
m_GlyphItems.begin(); --pPrev)
845 if (pPrev->charPos() != nCharPos)
850 if (pPrev->charPos() == (nCharPos + 1))
static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t *, hb_codepoint_t, hb_codepoint_t *, void *)
static hb_unicode_funcs_t * getUnicodeFuncs()
static const uint8_t sVerticalOrientationValues[34][128]
#define kVerticalOrientationCharBits
static const uint8_t sVerticalOrientationPages[4][512]
static const uint8_t sVerticalOrientationPlanes[16]
#define kVerticalOrientationMaxPlane
FontPitch GetPitch() const
LogicalFontInstance & GetFont() const
SalLayoutGlyphsImpl m_GlyphItems
void ApplyAsianKerning(const OUString &rStr)
void Justify(DeviceCoordinate nNewWidth)
void SetNeedFallback(vcl::text::ImplLayoutArgs &, sal_Int32, bool)
SalLayoutGlyphs GetGlyphs() const final override
std::vector< hb_feature_t > maFeatures
GenericSalLayout(LogicalFontInstance &)
void ApplyDXArray(const DC *, SalLayoutFlags nLayoutFlags)
bool LayoutText(vcl::text::ImplLayoutArgs &, const SalLayoutGlyphsImpl *) final override
void DrawText(SalGraphics &) const final override
void ParseFeatures(std::u16string_view name)
void GetCharWidths(std::vector< DeviceCoordinate > &rCharWidths) const
~GenericSalLayout() override
void AdjustLayout(vcl::text::ImplLayoutArgs &) final override
bool HasVerticalAlternate(sal_UCS4 aChar, sal_UCS4 aNextChar)
bool IsKashidaPosValid(int nCharPos) const final override
css::uno::Reference< css::i18n::XBreakIterator > mxBreak
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
OUString getLanguage() const
const OUString & getBcp47(bool bResolveSystem=true) const
void GetScale(double *nXScale, double *nYScale) const
bool GetGlyphBoundRect(sal_GlyphId, tools::Rectangle &, bool) const
const vcl::font::FontSelectPattern & GetFontSelectPattern() const
int GetKashidaWidth() const
virtual void DrawTextLayout(const GenericSalLayout &)=0
void SetFlags(SalLayoutFlags flags)
SalLayoutFlags GetFlags() const
SalLayoutGlyphsImpl * clone() const
void AppendImpl(SalLayoutGlyphsImpl *pImpl)
virtual void AdjustLayout(vcl::text::ImplLayoutArgs &)
std::vector< FeatureSetting > const & getFeatures() const
OUString const & getLanguage() const
const double * mpAltNaturalDXArray
const DeviceCoordinate * mpDXArray
vcl::text::TextLayoutCache const * m_pTextLayoutCache
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
DeviceCoordinate mnLayoutWidth
void AddFallbackRun(int nMinRunPos, int nEndRunPos, bool bRTL)
LanguageTag maLanguageTag
std::vector< vcl::text::Run > runs
sal_Int32 DeviceCoordinate
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
constexpr T & temporary(T &&x)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
VCL_DLLPUBLIC css::uno::Reference< css::i18n::XBreakIterator > CreateBreakIterator()
css::drawing::Direction3D aDirection