Skip to content

Commit 572f18c

Browse files
committed
LT-22324: finish OpenType locale review fixes
1 parent add09b5 commit 572f18c

8 files changed

Lines changed: 723 additions & 80 deletions

File tree

Src/FwCoreDlgs/FwCoreDlgControls/FontFeaturesButton.cs

Lines changed: 213 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -921,29 +921,29 @@ public string GetFeatureValueLabel(int featureId, int valueId, int languageId)
921921

922922
private sealed class OpenTypeFontFeatureProvider : IFontFeatureProvider
923923
{
924-
private static readonly Dictionary<string, string> s_featureLabels = new Dictionary<string, string>
925-
{
926-
{ "aalt", "Access All Alternates" },
927-
{ "c2sc", "Small Capitals From Capitals" },
928-
{ "calt", "Contextual Alternates" },
929-
{ "case", "Case-Sensitive Forms" },
930-
{ "ccmp", "Glyph Composition/Decomposition" },
931-
{ "clig", "Contextual Ligatures" },
932-
{ "dlig", "Discretionary Ligatures" },
933-
{ "frac", "Fractions" },
934-
{ "kern", "Kerning" },
935-
{ "liga", "Standard Ligatures" },
936-
{ "lnum", "Lining Figures" },
937-
{ "onum", "Oldstyle Figures" },
938-
{ "pnum", "Proportional Figures" },
939-
{ "salt", "Stylistic Alternates" },
940-
{ "smcp", "Small Capitals" },
941-
{ "ss01", "Stylistic Set 1" },
942-
{ "ss02", "Stylistic Set 2" },
943-
{ "ss03", "Stylistic Set 3" },
944-
{ "ss04", "Stylistic Set 4" },
945-
{ "ss05", "Stylistic Set 5" },
946-
{ "tnum", "Tabular Figures" },
924+
private static readonly Dictionary<string, string> s_featureLabelResourceIds = new Dictionary<string, string>
925+
{
926+
{ "aalt", "kstidOpenTypeFeature_aalt" },
927+
{ "c2sc", "kstidOpenTypeFeature_c2sc" },
928+
{ "calt", "kstidOpenTypeFeature_calt" },
929+
{ "case", "kstidOpenTypeFeature_case" },
930+
{ "ccmp", "kstidOpenTypeFeature_ccmp" },
931+
{ "clig", "kstidOpenTypeFeature_clig" },
932+
{ "dlig", "kstidOpenTypeFeature_dlig" },
933+
{ "frac", "kstidOpenTypeFeature_frac" },
934+
{ "kern", "kstidOpenTypeFeature_kern" },
935+
{ "liga", "kstidOpenTypeFeature_liga" },
936+
{ "lnum", "kstidOpenTypeFeature_lnum" },
937+
{ "onum", "kstidOpenTypeFeature_onum" },
938+
{ "pnum", "kstidOpenTypeFeature_pnum" },
939+
{ "salt", "kstidOpenTypeFeature_salt" },
940+
{ "smcp", "kstidOpenTypeFeature_smcp" },
941+
{ "ss01", "kstidOpenTypeFeature_ss01" },
942+
{ "ss02", "kstidOpenTypeFeature_ss02" },
943+
{ "ss03", "kstidOpenTypeFeature_ss03" },
944+
{ "ss04", "kstidOpenTypeFeature_ss04" },
945+
{ "ss05", "kstidOpenTypeFeature_ss05" },
946+
{ "tnum", "kstidOpenTypeFeature_tnum" },
947947
};
948948

949949
private readonly int[] m_featureIds;
@@ -977,8 +977,14 @@ public string GetFeatureTag(int featureId)
977977
public string GetFeatureLabel(int featureId, int languageId)
978978
{
979979
var tag = GetFeatureTag(featureId);
980-
string label;
981-
return s_featureLabels.TryGetValue(tag, out label) ? label : tag;
980+
string resourceId;
981+
if (s_featureLabelResourceIds.TryGetValue(tag, out resourceId))
982+
{
983+
var label = FwCoreDlgControls.ResourceManager.GetString(resourceId, CultureInfo.CurrentUICulture);
984+
if (!string.IsNullOrEmpty(label))
985+
return label;
986+
}
987+
return tag;
982988
}
983989

984990
public int[] GetFeatureValues(int featureId, int maxValues, out int valueCount, out int defaultValue)
@@ -990,7 +996,9 @@ public int[] GetFeatureValues(int featureId, int maxValues, out int valueCount,
990996

991997
public string GetFeatureValueLabel(int featureId, int valueId, int languageId)
992998
{
993-
return valueId == 0 ? "Off" : "On";
999+
return valueId == 0
1000+
? FwCoreDlgControls.ResourceManager.GetString("kstidOpenTypeFeatureValueOff", CultureInfo.CurrentUICulture)
1001+
: FwCoreDlgControls.ResourceManager.GetString("kstidOpenTypeFeatureValueOn", CultureInfo.CurrentUICulture);
9941002
}
9951003
}
9961004

@@ -1009,24 +1017,88 @@ internal static string ConvertRendererNeutralFeatureStringToIds(string fontFeatu
10091017
setting.Value)));
10101018
}
10111019

1012-
private static class OpenTypeFontFeatureReader
1020+
internal static class OpenTypeFontFeatureReader
10131021
{
10141022
private const uint GdiError = 0xFFFFFFFF;
1023+
private const int MaxCacheEntries = 32;
1024+
private const int ObjFont = 6;
10151025
private static readonly uint[] s_layoutTables = { MakeTableTag("GSUB"), MakeTableTag("GPOS") };
1026+
private static readonly object s_cacheLock = new object();
1027+
private static readonly Dictionary<FontFeatureCacheKey, string[]> s_featureTagCache =
1028+
new Dictionary<FontFeatureCacheKey, string[]>();
1029+
private static readonly Queue<FontFeatureCacheKey> s_cacheOrder = new Queue<FontFeatureCacheKey>();
1030+
private static Func<IntPtr, uint, byte[]> s_tableReader = ReadTable;
10161031

10171032
[DllImport("gdi32.dll", SetLastError = true)]
10181033
private static extern uint GetFontData(IntPtr hdc, uint table, uint offset, byte[] buffer, uint length);
10191034

1035+
[DllImport("gdi32.dll")]
1036+
private static extern IntPtr GetCurrentObject(IntPtr hdc, int objectType);
1037+
1038+
[DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
1039+
private static extern int GetObject(IntPtr hObject, int size, ref LogFont logFont);
1040+
10201041
public static IReadOnlyList<string> GetFeatureTags(IntPtr hdc)
10211042
{
1043+
var cacheKey = FontFeatureCacheKey.FromHdc(hdc);
1044+
lock (s_cacheLock)
1045+
{
1046+
string[] cachedTags;
1047+
if (s_featureTagCache.TryGetValue(cacheKey, out cachedTags))
1048+
return cachedTags.ToArray();
1049+
}
1050+
10221051
var tags = new SortedSet<string>(StringComparer.Ordinal);
10231052
foreach (var table in s_layoutTables)
10241053
{
1025-
var tableData = ReadTable(hdc, table);
1054+
var tableData = s_tableReader(hdc, table);
10261055
if (tableData != null)
10271056
ReadFeatureList(tableData, tags);
10281057
}
1029-
return tags.ToArray();
1058+
var discoveredTags = tags.ToArray();
1059+
lock (s_cacheLock)
1060+
{
1061+
if (!s_featureTagCache.ContainsKey(cacheKey))
1062+
{
1063+
if (s_cacheOrder.Count >= MaxCacheEntries)
1064+
s_featureTagCache.Remove(s_cacheOrder.Dequeue());
1065+
s_featureTagCache[cacheKey] = discoveredTags;
1066+
s_cacheOrder.Enqueue(cacheKey);
1067+
}
1068+
}
1069+
return discoveredTags.ToArray();
1070+
}
1071+
1072+
internal static void ClearCacheForTests()
1073+
{
1074+
lock (s_cacheLock)
1075+
{
1076+
ClearCache();
1077+
}
1078+
}
1079+
1080+
internal static IDisposable UseTableReaderForTests(Func<IntPtr, uint, byte[]> tableReader)
1081+
{
1082+
lock (s_cacheLock)
1083+
{
1084+
var previousReader = s_tableReader;
1085+
s_tableReader = tableReader ?? ReadTable;
1086+
ClearCache();
1087+
return new DisposableAction(() =>
1088+
{
1089+
lock (s_cacheLock)
1090+
{
1091+
s_tableReader = previousReader;
1092+
ClearCache();
1093+
}
1094+
});
1095+
}
1096+
}
1097+
1098+
private static void ClearCache()
1099+
{
1100+
s_featureTagCache.Clear();
1101+
s_cacheOrder.Clear();
10301102
}
10311103

10321104
private static byte[] ReadTable(IntPtr hdc, uint table)
@@ -1072,6 +1144,118 @@ private static uint MakeTableTag(string tag)
10721144
{
10731145
return (uint)(tag[0] | tag[1] << 8 | tag[2] << 16 | tag[3] << 24);
10741146
}
1147+
1148+
[StructLayout(LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
1149+
private struct LogFont
1150+
{
1151+
public int Height;
1152+
public int Width;
1153+
public int Escapement;
1154+
public int Orientation;
1155+
public int Weight;
1156+
public byte Italic;
1157+
public byte Underline;
1158+
public byte StrikeOut;
1159+
public byte CharSet;
1160+
public byte OutPrecision;
1161+
public byte ClipPrecision;
1162+
public byte Quality;
1163+
public byte PitchAndFamily;
1164+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
1165+
public string FaceName;
1166+
}
1167+
1168+
private struct FontFeatureCacheKey : IEquatable<FontFeatureCacheKey>
1169+
{
1170+
private readonly string m_faceName;
1171+
private readonly int m_height;
1172+
private readonly int m_weight;
1173+
private readonly byte m_italic;
1174+
private readonly byte m_charSet;
1175+
private readonly byte m_pitchAndFamily;
1176+
private readonly IntPtr m_fallbackHdc;
1177+
1178+
private FontFeatureCacheKey(string faceName, int height, int weight, byte italic,
1179+
byte charSet, byte pitchAndFamily, IntPtr fallbackHdc)
1180+
{
1181+
m_faceName = faceName ?? string.Empty;
1182+
m_height = height;
1183+
m_weight = weight;
1184+
m_italic = italic;
1185+
m_charSet = charSet;
1186+
m_pitchAndFamily = pitchAndFamily;
1187+
m_fallbackHdc = fallbackHdc;
1188+
}
1189+
1190+
public static FontFeatureCacheKey FromHdc(IntPtr hdc)
1191+
{
1192+
if (hdc != IntPtr.Zero)
1193+
{
1194+
var hfont = GetCurrentObject(hdc, ObjFont);
1195+
if (hfont != IntPtr.Zero)
1196+
{
1197+
var logFont = new LogFont();
1198+
if (GetObject(hfont, Marshal.SizeOf(typeof(LogFont)), ref logFont) > 0)
1199+
{
1200+
return new FontFeatureCacheKey(logFont.FaceName, logFont.Height,
1201+
logFont.Weight, logFont.Italic, logFont.CharSet,
1202+
logFont.PitchAndFamily, IntPtr.Zero);
1203+
}
1204+
}
1205+
}
1206+
return new FontFeatureCacheKey(string.Empty, 0, 0, 0, 0, 0, hdc);
1207+
}
1208+
1209+
public bool Equals(FontFeatureCacheKey other)
1210+
{
1211+
return string.Equals(m_faceName, other.m_faceName, StringComparison.OrdinalIgnoreCase) &&
1212+
m_height == other.m_height &&
1213+
m_weight == other.m_weight &&
1214+
m_italic == other.m_italic &&
1215+
m_charSet == other.m_charSet &&
1216+
m_pitchAndFamily == other.m_pitchAndFamily &&
1217+
m_fallbackHdc == other.m_fallbackHdc;
1218+
}
1219+
1220+
public override bool Equals(object obj)
1221+
{
1222+
return obj is FontFeatureCacheKey && Equals((FontFeatureCacheKey)obj);
1223+
}
1224+
1225+
public override int GetHashCode()
1226+
{
1227+
unchecked
1228+
{
1229+
var hash = StringComparer.OrdinalIgnoreCase.GetHashCode(m_faceName);
1230+
hash = (hash * 397) ^ m_height;
1231+
hash = (hash * 397) ^ m_weight;
1232+
hash = (hash * 397) ^ m_italic;
1233+
hash = (hash * 397) ^ m_charSet;
1234+
hash = (hash * 397) ^ m_pitchAndFamily;
1235+
hash = (hash * 397) ^ m_fallbackHdc.GetHashCode();
1236+
return hash;
1237+
}
1238+
}
1239+
}
1240+
1241+
private sealed class DisposableAction : IDisposable
1242+
{
1243+
private Action m_disposeAction;
1244+
1245+
public DisposableAction(Action disposeAction)
1246+
{
1247+
m_disposeAction = disposeAction;
1248+
}
1249+
1250+
public void Dispose()
1251+
{
1252+
var disposeAction = m_disposeAction;
1253+
if (disposeAction == null)
1254+
return;
1255+
m_disposeAction = null;
1256+
disposeAction();
1257+
}
1258+
}
10751259
}
10761260
}
10771261
}

Src/FwCoreDlgs/FwCoreDlgControls/FwCoreDlgControls.resx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,75 @@
170170
<data name="kstidFeatureValue" xml:space="preserve">
171171
<value>Value #{0}</value>
172172
</data>
173+
<data name="kstidOpenTypeFeatureValueOff" xml:space="preserve">
174+
<value>Off</value>
175+
</data>
176+
<data name="kstidOpenTypeFeatureValueOn" xml:space="preserve">
177+
<value>On</value>
178+
</data>
179+
<data name="kstidOpenTypeFeature_aalt" xml:space="preserve">
180+
<value>Access All Alternates</value>
181+
</data>
182+
<data name="kstidOpenTypeFeature_c2sc" xml:space="preserve">
183+
<value>Small Capitals From Capitals</value>
184+
</data>
185+
<data name="kstidOpenTypeFeature_calt" xml:space="preserve">
186+
<value>Contextual Alternates</value>
187+
</data>
188+
<data name="kstidOpenTypeFeature_case" xml:space="preserve">
189+
<value>Case-Sensitive Forms</value>
190+
</data>
191+
<data name="kstidOpenTypeFeature_ccmp" xml:space="preserve">
192+
<value>Glyph Composition/Decomposition</value>
193+
</data>
194+
<data name="kstidOpenTypeFeature_clig" xml:space="preserve">
195+
<value>Contextual Ligatures</value>
196+
</data>
197+
<data name="kstidOpenTypeFeature_dlig" xml:space="preserve">
198+
<value>Discretionary Ligatures</value>
199+
</data>
200+
<data name="kstidOpenTypeFeature_frac" xml:space="preserve">
201+
<value>Fractions</value>
202+
</data>
203+
<data name="kstidOpenTypeFeature_kern" xml:space="preserve">
204+
<value>Kerning</value>
205+
</data>
206+
<data name="kstidOpenTypeFeature_liga" xml:space="preserve">
207+
<value>Standard Ligatures</value>
208+
</data>
209+
<data name="kstidOpenTypeFeature_lnum" xml:space="preserve">
210+
<value>Lining Figures</value>
211+
</data>
212+
<data name="kstidOpenTypeFeature_onum" xml:space="preserve">
213+
<value>Oldstyle Figures</value>
214+
</data>
215+
<data name="kstidOpenTypeFeature_pnum" xml:space="preserve">
216+
<value>Proportional Figures</value>
217+
</data>
218+
<data name="kstidOpenTypeFeature_salt" xml:space="preserve">
219+
<value>Stylistic Alternates</value>
220+
</data>
221+
<data name="kstidOpenTypeFeature_smcp" xml:space="preserve">
222+
<value>Small Capitals</value>
223+
</data>
224+
<data name="kstidOpenTypeFeature_ss01" xml:space="preserve">
225+
<value>Stylistic Set 1</value>
226+
</data>
227+
<data name="kstidOpenTypeFeature_ss02" xml:space="preserve">
228+
<value>Stylistic Set 2</value>
229+
</data>
230+
<data name="kstidOpenTypeFeature_ss03" xml:space="preserve">
231+
<value>Stylistic Set 3</value>
232+
</data>
233+
<data name="kstidOpenTypeFeature_ss04" xml:space="preserve">
234+
<value>Stylistic Set 4</value>
235+
</data>
236+
<data name="kstidOpenTypeFeature_ss05" xml:space="preserve">
237+
<value>Stylistic Set 5</value>
238+
</data>
239+
<data name="kstidOpenTypeFeature_tnum" xml:space="preserve">
240+
<value>Tabular Figures</value>
241+
</data>
173242
<data name="kstidBadMsr" xml:space="preserve">
174243
<value>"{0}" is not a valid measurement.</value>
175244
</data>

0 commit comments

Comments
 (0)