Skip to content

Commit fa3244c

Browse files
committed
Enhance font handling and dynamic spacing for Arabic text rendering
- Introduced functions to measure font line height and apply dynamic spacing adjustments based on font metrics. - Improved text measurement and wrapping logic in the Reshaper module to ensure proper display of Arabic text. - Updated the ExpandUnitInfo function to incorporate dynamic font spacing, enhancing overall text layout consistency. - Implemented caching for line height measurements to optimize performance during text processing.
1 parent b2f2754 commit fa3244c

2 files changed

Lines changed: 130 additions & 8 deletions

File tree

common/Text.lua

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,92 @@ function Text.WOW_ZmienKody(message, target)
551551
end
552552

553553
-- Expand unit info and shape Arabic text for display in a FontString-like region.
554+
local textMeasureFrame
555+
local textMeasureFontString
556+
557+
local function normalizeFontPath(fontPath)
558+
if type(fontPath) ~= "string" or fontPath == "" then return nil end
559+
return (fontPath:gsub("/", "\\"):lower())
560+
end
561+
562+
local function isFont1(fontPath)
563+
return normalizeFontPath(fontPath) == normalizeFontPath(rawget(_G, "WOWTR_Font1"))
564+
end
565+
566+
local function ensureTextMeasureFontString()
567+
if textMeasureFontString then return textMeasureFontString end
568+
textMeasureFrame = CreateFrame("Frame", nil, UIParent)
569+
textMeasureFrame:SetSize(2048, 256)
570+
textMeasureFrame:Hide()
571+
textMeasureFontString = textMeasureFrame:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
572+
textMeasureFontString:SetPoint("TOPLEFT", textMeasureFrame, "TOPLEFT", 0, 0)
573+
textMeasureFontString:SetJustifyH("LEFT")
574+
return textMeasureFontString
575+
end
576+
577+
local function measureFontLineHeight(fontPath, fontSize, fontFlags)
578+
if (not fontPath) or (not fontSize) then return nil end
579+
local fs = ensureTextMeasureFontString()
580+
fs:SetWidth(2048)
581+
if fs.SetSpacing then
582+
pcall(fs.SetSpacing, fs, 0)
583+
end
584+
pcall(fs.SetFont, fs, fontPath, fontSize, fontFlags or "")
585+
fs:SetText("Hg")
586+
local measuredHeight = fs:GetHeight()
587+
if measuredHeight and measuredHeight > 0 then
588+
return measuredHeight
589+
end
590+
return nil
591+
end
592+
593+
local function resolveSpacingTarget(obj)
594+
if not obj then return nil end
595+
if obj.SetSpacing and obj.GetSpacing then
596+
return obj
597+
end
598+
if obj.Text and obj.Text.SetSpacing and obj.Text.GetSpacing then
599+
return obj.Text
600+
end
601+
if obj.GetRegions then
602+
local regions = { obj:GetRegions() }
603+
for _, region in ipairs(regions) do
604+
if region and region.GetObjectType and region:GetObjectType() == "FontString" and region.SetSpacing and region.GetSpacing then
605+
return region
606+
end
607+
end
608+
end
609+
return nil
610+
end
611+
612+
local function applyDynamicFontSpacing(obj, sourceFont, targetFont, fontSize, fontFlags)
613+
local spacingTarget = resolveSpacingTarget(obj)
614+
if not spacingTarget then return end
615+
616+
if spacingTarget.WoWAR_DefaultSpacing == nil then
617+
local ok, currentSpacing = pcall(spacingTarget.GetSpacing, spacingTarget)
618+
spacingTarget.WoWAR_DefaultSpacing = ok and currentSpacing or 0
619+
end
620+
621+
local defaultSpacing = spacingTarget.WoWAR_DefaultSpacing or 0
622+
local desiredSpacing = defaultSpacing
623+
624+
if isFont1(targetFont) then
625+
local spacingFromMetrics = 0
626+
local sourceLineHeight = measureFontLineHeight(sourceFont or targetFont, fontSize, fontFlags)
627+
local targetLineHeight = measureFontLineHeight(targetFont, fontSize, fontFlags)
628+
if sourceLineHeight and targetLineHeight and (targetLineHeight + 0.5 < sourceLineHeight) then
629+
spacingFromMetrics = math.floor((sourceLineHeight - targetLineHeight) + 0.5)
630+
end
631+
desiredSpacing = math.max(defaultSpacing, spacingFromMetrics + 2)
632+
end
633+
634+
local ok, currentSpacing = pcall(spacingTarget.GetSpacing, spacingTarget)
635+
if (not ok) or (not currentSpacing) or (math.abs(currentSpacing - desiredSpacing) > 0.1) then
636+
pcall(spacingTarget.SetSpacing, spacingTarget, desiredSpacing)
637+
end
638+
end
639+
554640
function Text.ExpandUnitInfo(msg, OnObjectives, AR_obj, AR_font, AR_corr, AR_RIGHT)
555641
if (msg == nil) then msg = "" end
556642
msg = Text.WOW_ZmienKody(msg)
@@ -559,18 +645,19 @@ function Text.ExpandUnitInfo(msg, OnObjectives, AR_obj, AR_font, AR_corr, AR_RIG
559645
msg = FixCurlyColorSpansForRTL(msg)
560646
local _font = WOWTR_Font2
561647
local AR_size = 13
648+
local _flags = ""
562649
if AR_obj.GetFont then
563-
local ok, f, s = pcall(AR_obj.GetFont, AR_obj, "P")
564-
if ok and f then _font = f; AR_size = s or AR_size else
565-
ok, f, s = pcall(AR_obj.GetFont, AR_obj)
566-
if ok and f then _font = f; AR_size = s or AR_size end
650+
local ok, f, s, fl = pcall(AR_obj.GetFont, AR_obj, "P")
651+
if ok and f then _font = f; AR_size = s or AR_size; _flags = fl or _flags else
652+
ok, f, s, fl = pcall(AR_obj.GetFont, AR_obj)
653+
if ok and f then _font = f; AR_size = s or AR_size; _flags = fl or _flags end
567654
end
568655
elseif AR_obj.GetRegions then
569656
local regions = { AR_obj:GetRegions() }
570657
for _, v in pairs(regions) do
571658
if (v.GetObjectType and v:GetObjectType() == "FontString" and v.GetFont) then
572-
local ok, f, s = pcall(v.GetFont, v)
573-
if ok and f then _font = f; AR_size = s or AR_size; break end
659+
local ok, f, s, fl = pcall(v.GetFont, v)
660+
if ok and f then _font = f; AR_size = s or AR_size; _flags = fl or _flags; break end
574661
end
575662
end
576663
end
@@ -614,6 +701,8 @@ function Text.ExpandUnitInfo(msg, OnObjectives, AR_obj, AR_font, AR_corr, AR_RIG
614701
msg = AS_ReverseAndPrepareLineText(msg, AR_obj:GetWidth() + _corr, AR_font or _font, AR_size)
615702
end
616703

704+
applyDynamicFontSpacing(AR_obj, _font, AR_font or _font, AR_size, _flags)
705+
617706
msg = Text.RestoreWoWSpecialCodes(msg, specialCodes)
618707
msg = (prefix or "") .. msg
619708
end

common/Text/Reshaper.lua

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,37 @@ function AS_CreateTestLine()
10711071
AS_TestLine:Hide(); -- the frame is invisible in the game
10721072
end
10731073

1074+
local AS_LineHeightCache = {}
1075+
1076+
local function AS_GetWrapLineHeight(Afont, AfontSize)
1077+
if (not Afont) or (not AfontSize) then
1078+
return (AfontSize or 13) * 1.5
1079+
end
1080+
if (AS_TestLine == nil) then
1081+
AS_CreateTestLine();
1082+
end
1083+
1084+
local cacheKey = tostring(Afont) .. "|" .. tostring(AfontSize)
1085+
if AS_LineHeightCache[cacheKey] then
1086+
return AS_LineHeightCache[cacheKey]
1087+
end
1088+
1089+
AS_TestLine.text:SetWidth(2048);
1090+
if AS_TestLine.text.SetSpacing then
1091+
pcall(AS_TestLine.text.SetSpacing, AS_TestLine.text, 0);
1092+
end
1093+
AS_TestLine.text:SetFont(Afont, AfontSize);
1094+
AS_TestLine.text:SetText("Hg");
1095+
1096+
local measuredHeight = AS_TestLine.text:GetHeight();
1097+
if (not measuredHeight) or (measuredHeight <= 0) then
1098+
measuredHeight = AfontSize * 1.5;
1099+
end
1100+
1101+
AS_LineHeightCache[cacheKey] = measuredHeight
1102+
return measuredHeight
1103+
end
1104+
10741105
-------------------------------------------------------------------------------------------------------
10751106
-- This function prepares Arabic text to be displayed in a specific window width;
10761107
-- Parameters: Atext=arabic UTF8 text, Awidth=frame width to the text, AfontSize=size of current font
@@ -1089,6 +1120,7 @@ function AS_ReverseAndPrepareLineText(Atext, Awidth, Afont, AfontSize)
10891120
if (AS_TestLine == nil) then
10901121
AS_CreateTestLine();
10911122
end
1123+
local wrapLineHeight = AS_GetWrapLineHeight(Afont, AfontSize)
10921124
Atext = string.gsub(Atext, " #", "#");
10931125
Atext = string.gsub(Atext, "# ", "#");
10941126

@@ -1128,7 +1160,7 @@ function AS_ReverseAndPrepareLineText(Atext, Awidth, Afont, AfontSize)
11281160
AS_TestLine.text:SetWidth(Awidth);
11291161
AS_TestLine.text:SetFont(Afont, AfontSize);
11301162
AS_TestLine.text:SetText(AS_UTF8reverse(newstr));
1131-
if ((char1 == '#') or (AS_TestLine.text:GetHeight() > AfontSize * 1.5)) then
1163+
if ((char1 == '#') or (AS_TestLine.text:GetHeight() > wrapLineHeight + 1)) then
11321164
newstr = string.sub(newstr, 1, strlen(newstr) - last_space);
11331165
newstr = string.gsub(newstr, "#", "");
11341166
-- *** MODIFICATION: Remove call to AS_AddSpaces ***
@@ -1165,6 +1197,7 @@ function AS_ReverseAndPrepareLineText_RIGHT(Atext, Awidth, Afont, AfontSize)
11651197
if (AS_TestLine == nil) then -- a separate frame for displaying the translation of texts and determining the length
11661198
AS_CreateTestLine();
11671199
end
1200+
local wrapLineHeight = AS_GetWrapLineHeight(Afont, AfontSize)
11681201
Atext = string.gsub(Atext, " #", "#");
11691202
Atext = string.gsub(Atext, "# ", "#");
11701203
local bytes = strlen(Atext);
@@ -1201,7 +1234,7 @@ function AS_ReverseAndPrepareLineText_RIGHT(Atext, Awidth, Afont, AfontSize)
12011234
AS_TestLine.text:SetWidth(Awidth); -- set the frame width to the text
12021235
AS_TestLine.text:SetFont(Afont, AfontSize);
12031236
AS_TestLine.text:SetText(AS_UTF8reverse(newstr));
1204-
if ((char1 == '#') or (AS_TestLine.text:GetHeight() > AfontSize * 1.5)) then -- text no longer fits in one line
1237+
if ((char1 == '#') or (AS_TestLine.text:GetHeight() > wrapLineHeight + 1)) then -- text no longer fits in one line
12051238
newstr = string.sub(newstr, 1, strlen(newstr) - last_space); -- text up to the last space
12061239
newstr = string.gsub(newstr, "#", "");
12071240
retstr = retstr .. AS_AddSpaces(AS_UTF8reverse(newstr), Awidth, Afont, AfontSize) .. "\n";

0 commit comments

Comments
 (0)