24 #include <hb-graphite2.h>
35 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
37 #include <unicode/uchar.h>
41 #if !HB_VERSION_ATLEAST(1, 1, 0)
53 static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
60 : mpVertGlyphs(nullptr)
61 , mbFuzzing(
utl::ConfigManager::IsFuzzing())
74 if (!sLanguage.isEmpty())
79 hb_feature_t aFeature { rFeat.m_nTag, rFeat.m_nValue, rFeat.m_nStart, rFeat.m_nEnd };
91 hb_direction_t maDirection;
104 Run(int32_t nStart_, int32_t nEnd_, UScriptCode nCode_)
120 reinterpret_cast<const UChar *>(pStr),
122 while (aScriptRun.
next())
132 #if U_ICU_VERSION_MAJOR_NUM >= 63
135 Rotated = U_VO_ROTATED,
136 TransformedUpright = U_VO_TRANSFORMED_UPRIGHT,
137 TransformedRotated = U_VO_TRANSFORMED_ROTATED
146 TransformedUpright = 2,
147 TransformedRotated = 3
155 if ((cCh == 0xff1a || cCh == 0xff1b
156 || cCh == 0x2ca || cCh == 0x2cb || cCh == 0x2c7 || cCh == 0x2d9)
158 return VerticalOrientation::TransformedUpright;
160 #if U_ICU_VERSION_MAJOR_NUM >= 63
161 int32_t nRet = u_getIntPropertyValue(cCh, UCHAR_VERTICAL_ORIENTATION);
179 SAL_WARN(
"vcl.gdi",
"Getting VerticalOrientation for codepoint outside Unicode range");
190 return std::make_shared<vcl::TextLayoutCache>(rString.getStr(), rString.getLength());
214 sal_Int32 nGraphemeEndPos =
216 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
218 rArgs.
mrStr.iterateCodePoints(&nCharPos);
219 sal_Int32 nGraphemeStartPos =
220 mxBreak->previousCharacters(rArgs.
mrStr, nCharPos, aLocale,
221 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
223 rArgs.
NeedFallback(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
252 hb_codepoint_t nGlyphIndex = 0;
254 if (!hb_font_get_glyph(pHbFont, aChar, aVariationSelector, &nGlyphIndex))
259 hb_face_t* pHbFace = hb_font_get_face(pHbFont);
263 hb_set_t* pLookups = hb_set_create();
264 hb_tag_t
const pFeatures[] = { HB_TAG(
'v',
'e',
'r',
't'), HB_TAG_NONE };
265 hb_ot_layout_collect_lookups(pHbFace, HB_OT_TAG_GSUB,
nullptr,
nullptr, pFeatures, pLookups);
266 if (!hb_set_is_empty(pLookups))
270 hb_codepoint_t nIdx = HB_SET_VALUE_INVALID;
271 while (hb_set_next(pLookups, &nIdx))
273 hb_set_t* pGlyphs = hb_set_create();
274 hb_ot_layout_lookup_collect_glyphs(pHbFace, HB_OT_TAG_GSUB, nIdx,
311 std::unique_ptr<vcl::TextLayoutCache> pNewScriptRun;
320 pTextLayout = pNewScriptRun.get();
323 hb_buffer_t* pHbBuffer = hb_buffer_create();
324 hb_buffer_pre_allocate(pHbBuffer, nGlyphCapacity);
325 #if !HB_VERSION_ATLEAST(1, 1, 0)
327 hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
334 maFeatures.push_back({ HB_TAG(
'k',
'e',
'r',
'n'), 0, 0,
static_cast<unsigned int>(-1) });
343 Point aCurrPos(0, 0);
346 int nBidiMinRunPos, nBidiEndRunPos;
348 if (!rArgs.
GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
352 std::vector<SubRun> aSubRuns;
353 int nCurrentPos = nBidiMinRunPos;
355 for (; k < pTextLayout->
runs.size(); ++k)
357 vcl::Run
const& rRun(pTextLayout->
runs[k]);
358 if (rRun.nStart <= nCurrentPos && nCurrentPos < rRun.nEnd)
366 hb_script_t aScript = hb_icu_script_to_script(pTextLayout->
runs[k].nCode);
367 aSubRuns.push_back({ nBidiMinRunPos, nBidiEndRunPos, aScript, bRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR });
371 while (nCurrentPos < nBidiEndRunPos && k < pTextLayout->runs.size())
373 int32_t nMinRunPos = nCurrentPos;
374 int32_t nEndRunPos = std::min(pTextLayout->
runs[k].nEnd, nBidiEndRunPos);
375 hb_direction_t
aDirection = bRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
376 hb_script_t aScript = hb_icu_script_to_script(pTextLayout->
runs[k].nCode);
381 sal_Int32 nIdx = nMinRunPos;
382 while (nIdx < nEndRunPos)
384 sal_Int32 nPrevIdx = nIdx;
389 if (nIdx < nEndRunPos)
391 sal_Int32 nNextIdx = nIdx;
392 sal_UCS4 aNextChar = rArgs.
mrStr.iterateCodePoints(&nNextIdx);
393 if (u_hasBinaryProperty(aNextChar, UCHAR_VARIATION_SELECTOR))
396 aVariationSelector = aNextChar;
407 if (aVo == VerticalOrientation::Upright ||
408 aVo == VerticalOrientation::TransformedUpright ||
409 (aVo == VerticalOrientation::TransformedRotated &&
412 aDirection = HB_DIRECTION_TTB;
416 aDirection = bRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
419 if (aSubRuns.empty() || aSubRuns.back().maDirection != aDirection)
420 aSubRuns.push_back({ nPrevIdx, nIdx, aScript, aDirection });
422 aSubRuns.back().mnEnd = nIdx;
427 aSubRuns.push_back({ nMinRunPos, nEndRunPos, aScript, aDirection });
430 nCurrentPos = nEndRunPos;
438 std::reverse(aSubRuns.begin(), aSubRuns.end());
440 for (
const auto& aSubRun : aSubRuns)
442 hb_buffer_clear_contents(pHbBuffer);
444 const int nMinRunPos = aSubRun.mnMin;
445 const int nEndRunPos = aSubRun.mnEnd;
446 const int nRunLen = nEndRunPos - nMinRunPos;
449 if (sLanguage.isEmpty())
452 int nHbFlags = HB_BUFFER_FLAGS_DEFAULT;
454 nHbFlags |= HB_BUFFER_FLAG_BOT;
455 if (nEndRunPos == nLength)
456 nHbFlags |= HB_BUFFER_FLAG_EOT;
458 hb_buffer_set_direction(pHbBuffer, aSubRun.maDirection);
459 hb_buffer_set_script(pHbBuffer, aSubRun.maScript);
460 hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
461 hb_buffer_set_flags(pHbBuffer, static_cast<hb_buffer_flags_t>(nHbFlags));
463 pHbBuffer, reinterpret_cast<uint16_t const *>(pStr), nLength,
464 nMinRunPos, nRunLen);
465 hb_buffer_set_cluster_level(pHbBuffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
471 const char*
const pHbShapers[] = {
"graphite2",
"coretext_aat",
"ot",
"fallback",
nullptr };
476 int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
477 hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer,
nullptr);
478 hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer,
nullptr);
480 for (
int i = 0;
i < nRunGlyphCount; ++
i) {
481 int32_t nGlyphIndex = pHbGlyphInfos[
i].codepoint;
482 int32_t nCharPos = pHbGlyphInfos[
i].cluster;
483 int32_t nCharCount = 0;
484 bool bInCluster =
false;
485 bool bClusterStart =
false;
492 if (
i > 0 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i - 1].cluster)
502 int32_t nNextCharPos = nCharPos;
503 while (nNextCharPos == nCharPos && j < nRunGlyphCount)
504 nNextCharPos = pHbGlyphInfos[j++].cluster;
506 if (nNextCharPos == nCharPos)
507 nNextCharPos = nEndRunPos;
508 nCharCount = nNextCharPos - nCharPos;
509 if ((
i == 0 || pHbGlyphInfos[
i].cluster != pHbGlyphInfos[
i - 1].cluster) &&
510 (
i < nRunGlyphCount - 1 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i + 1].cluster))
511 bClusterStart =
true;
518 if (
i < nRunGlyphCount - 1 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i + 1].cluster)
528 int32_t nNextCharPos = nCharPos;
529 while (nNextCharPos == nCharPos && j >= 0)
530 nNextCharPos = pHbGlyphInfos[j--].cluster;
532 if (nNextCharPos == nCharPos)
533 nNextCharPos = nEndRunPos;
534 nCharCount = nNextCharPos - nCharPos;
535 if ((
i == nRunGlyphCount - 1 || pHbGlyphInfos[
i].cluster != pHbGlyphInfos[
i + 1].cluster) &&
536 (
i > 0 && pHbGlyphInfos[
i].cluster == pHbGlyphInfos[
i - 1].cluster))
537 bClusterStart =
true;
562 if (u_getIntPropertyValue(aChar, UCHAR_GENERAL_CATEGORY) == U_NON_SPACING_MARK)
565 if (u_isUWhiteSpace(aChar))
568 if (aSubRun.maScript == HB_SCRIPT_ARABIC &&
569 HB_DIRECTION_IS_BACKWARD(aSubRun.maDirection) &&
577 if (aSubRun.maDirection == HB_DIRECTION_TTB)
583 hb_font_add_glyph_origin_for_direction(pHbFont,
586 &pHbPositions[
i].x_offset ,
587 &pHbPositions[
i].y_offset );
588 nAdvance = -pHbPositions[
i].y_advance;
589 nXOffset = -pHbPositions[
i].y_offset;
590 nYOffset = -pHbPositions[
i].x_offset;
594 nAdvance = pHbPositions[
i].x_advance;
595 nXOffset = pHbPositions[
i].x_offset;
596 nYOffset = -pHbPositions[
i].y_offset;
599 nAdvance = std::lround(nAdvance * nXScale);
600 nXOffset = std::lround(nXOffset * nXScale);
601 nYOffset = std::lround(nYOffset * nYScale);
603 Point aNewPos(aCurrPos.X() + nXOffset, aCurrPos.Y() + nYOffset);
604 const GlyphItem aGI(nCharPos, nCharCount, nGlyphIndex, aNewPos, nGlyphFlags,
605 nAdvance, nXOffset, &
GetFont());
608 aCurrPos.AdjustX(nAdvance );
613 hb_buffer_destroy(pHbBuffer);
626 for (
int i = 0;
i < nCharCount; ++
i)
632 if (nIndex >= nCharCount)
634 pCharWidths[nIndex] += aGlyphItem.m_nNewWidth;
661 std::unique_ptr<DeviceCoordinate[]>
const pOldCharWidths(
new DeviceCoordinate[nCharCount]);
662 std::unique_ptr<DeviceCoordinate[]>
const pNewCharWidths(
new DeviceCoordinate[nCharCount]);
668 for (
int i = 0;
i < nCharCount; ++
i)
676 bool bKashidaJustify =
false;
678 hb_codepoint_t nKashidaIndex = 0;
683 if (hb_font_get_glyph(pHbFont, 0x0640, 0, &nKashidaIndex))
685 bKashidaJustify = nKashidaWidth != 0;
690 std::map<size_t, DeviceCoordinate> pKashidas;
704 nDiff += pNewCharWidths[nCharPos + j] - pOldCharWidths[nCharPos + j];
752 pKashidas[i] = nDiff;
773 if (!bKashidaJustify || pKashidas.empty())
776 size_t nInserted = 0;
777 for (
auto const& pKashida : pKashidas)
779 auto pGlyphIter =
m_GlyphItems.
Impl()->begin() + nInserted + pKashida.first;
786 if (nTotalWidth > nKashidaWidth)
787 nCopies = nTotalWidth / nKashidaWidth;
798 nOverlap = nExcess / (nCopies - 1);
801 Point aPos(pGlyphIter->m_aLinearPos.getX() - nTotalWidth, 0);
802 int nCharPos = pGlyphIter->charPos();
806 GlyphItem aKashida(nCharPos, 0, nKashidaIndex, aPos, nFlags, nKashidaWidth, 0, &
GetFont());
808 aPos.AdjustX(nKashidaWidth );
809 aPos.AdjustX( -nOverlap );
820 if (pIter->charPos() == nCharPos)
831 if (pIter->glyphId() == 0)
837 for (
auto pPrev = pIter - 1; pPrev !=
m_GlyphItems.
Impl()->begin(); --pPrev)
839 if (pPrev->charPos() != nCharPos)
844 if (pPrev->charPos() == (nCharPos + 1))
GenericSalLayout(LogicalFontInstance &)
OUString const & getLanguage() const
int32_t getScriptEnd() const
static hb_unicode_funcs_t * getUnicodeFuncs()
virtual void DrawTextLayout(const GenericSalLayout &)=0
static const uint8_t sVerticalOrientationValues[34][128]
bool LayoutText(ImplLayoutArgs &, const SalLayoutGlyphs *) final override
const OUString & getBcp47(bool bResolveSystem=true) const
void Justify(DeviceCoordinate nNewWidth)
const SalLayoutGlyphs * GetGlyphs() const final override
LogicalFontInstance & GetFont() const
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
void NeedFallback(int nMinRunPos, int nEndRunPos, bool bRTL)
void ApplyDXArray(const ImplLayoutArgs &)
bool HasVerticalAlternate(sal_UCS4 aChar, sal_UCS4 aNextChar)
OUString getLanguage() const
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
void GetScale(double *nXScale, double *nYScale)
~GenericSalLayout() override
UScriptCode getScriptCode() const
void SetNeedFallback(ImplLayoutArgs &, sal_Int32, bool)
virtual void AdjustLayout(ImplLayoutArgs &)
DeviceCoordinate mnLayoutWidth
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
void ApplyAsianKerning(const OUString &rStr)
static std::shared_ptr< vcl::TextLayoutCache > CreateTextLayoutCache(OUString const &)
void AdjustLayout(ImplLayoutArgs &) final override
css::drawing::Direction3D aDirection
SalLayoutGlyphsImpl * Impl() const
static const uint8_t sVerticalOrientationPages[4][512]
void GetCharWidths(DeviceCoordinate *pCharWidths) const
tools::Long DeviceCoordinate
static const uint8_t sVerticalOrientationPlanes[16]
std::vector< FeatureSetting > const & getFeatures() const
static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t *, hb_codepoint_t, hb_codepoint_t *, void *)
int32_t getScriptStart() const
vcl::TextLayoutCache const * m_pTextLayoutCache
bool GetNextRun(int *nMinRunPos, int *nEndRunPos, bool *bRTL)
LanguageTag maLanguageTag
constexpr T & temporary(T &&x)
TextLayoutCache(sal_Unicode const *pStr, sal_Int32 const nEnd)
const FontSelectPattern & GetFontSelectPattern() const
std::vector< hb_feature_t > maFeatures
#define kVerticalOrientationMaxPlane
#define SAL_INFO(area, stream)
void DrawText(SalGraphics &) const final override
css::uno::Reference< css::i18n::XBreakIterator > mxBreak
bool IsKashidaPosValid(int nCharPos) const final override
SalLayoutGlyphs m_GlyphItems
#define SAL_WARN(area, stream)
std::vector< vcl::Run > runs
VCL_DLLPUBLIC css::uno::Reference< css::i18n::XBreakIterator > CreateBreakIterator()
void ParseFeatures(const OUString &name)
#define kVerticalOrientationCharBits
const DeviceCoordinate * mpDXArray