-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTestUnicode.VariationSequences.pas
More file actions
207 lines (172 loc) · 6.47 KB
/
TestUnicode.VariationSequences.pas
File metadata and controls
207 lines (172 loc) · 6.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
unit TestUnicode.VariationSequences;
interface
uses
System.Classes,
System.SysUtils,
Generics.Collections,
TestFramework,
PascalType.Types,
PascalType.Classes,
PascalType.FontFace,
PascalType.FontFace.SFNT,
PascalType.Tables.TrueType.cmap,
PascalType.Tables.TrueType.CharacterMaps;
type
TTestVariationSequences = class(TTestCase)
private
function CreateMockCmapStream: TMemoryStream;
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestUVSLookahead;
procedure TestCreateGlyphStringWithUVS;
end;
implementation
uses
PascalType.Unicode;
{ TTestVariationSequences }
procedure TTestVariationSequences.SetUp;
begin
inherited;
end;
procedure TTestVariationSequences.TearDown;
begin
inherited;
end;
function TTestVariationSequences.CreateMockCmapStream: TMemoryStream;
begin
Result := TMemoryStream.Create;
// cmap header
BigEndianValue.WriteWord(Result, 0); // version
BigEndianValue.WriteWord(Result, 2); // numTables
// Encoding Record 1 (Format 4, Windows Unicode BMP)
BigEndianValue.WriteWord(Result, 3); // platformID = Windows
BigEndianValue.WriteWord(Result, 1); // encodingID = Unicode BMP
BigEndianValue.WriteCardinal(Result, 20); // offset to format 4 (header(4) + 2 records(16))
// Encoding Record 2 (Format 14, Unicode Variation Sequences)
BigEndianValue.WriteWord(Result, 0); // platformID = Unicode
BigEndianValue.WriteWord(Result, 5); // encodingID = UVS
BigEndianValue.WriteCardinal(Result, 0); // offset - will fill later
//var Format4Pos := Result.Position;
// Format 4 data (minimal)
BigEndianValue.WriteWord(Result, 4); // format
BigEndianValue.WriteWord(Result, 32); // length
BigEndianValue.WriteWord(Result, 0); // language
BigEndianValue.WriteWord(Result, 4); // segCountX2
BigEndianValue.WriteWord(Result, 4); // searchRange
BigEndianValue.WriteWord(Result, 1); // entrySelector
BigEndianValue.WriteWord(Result, 0); // rangeShift
// EndCount
BigEndianValue.WriteWord(Result, $0041); // 'A'
BigEndianValue.WriteWord(Result, $FFFF); // end
BigEndianValue.WriteWord(Result, 0); // reservedPad
// StartCount
BigEndianValue.WriteWord(Result, $0041);
BigEndianValue.WriteWord(Result, $FFFF);
// IdDelta
BigEndianValue.WriteWord(Result, 100 - $0041); // Map 'A' to glyph 100
BigEndianValue.WriteWord(Result, 1);
// IdRangeOffset
BigEndianValue.WriteWord(Result, 0);
BigEndianValue.WriteWord(Result, 0);
var Format14Pos := Result.Position;
// Update Format 14 offset (record 2 offset starts at 16)
Result.Position := 16;
BigEndianValue.WriteCardinal(Result, Cardinal(Format14Pos));
Result.Position := Format14Pos;
// Format 14 data
BigEndianValue.WriteWord(Result, 14); // format
var F14LengthPos := Result.Position;
BigEndianValue.WriteCardinal(Result, 0); // length
BigEndianValue.WriteCardinal(Result, 1); // numVarSelectorRecords
// VariationSelector Record
BigEndianValue.WriteUInt24(Result, $FE00); // VS1
BigEndianValue.WriteCardinal(Result, 0); // defaultUVSOffset
BigEndianValue.WriteCardinal(Result, 0); // nonDefaultUVSOffset - will fill later
// Non-Default UVS table
var NonDefaultUVSPos := Result.Position;
Result.Position := Format14Pos + 2 + 4 + 4 + 3 + 4; // offset of nonDefaultUVSOffset
BigEndianValue.WriteCardinal(Result, Cardinal(NonDefaultUVSPos - Format14Pos));
Result.Position := NonDefaultUVSPos;
BigEndianValue.WriteCardinal(Result, 1); // numUVSMappings
BigEndianValue.WriteUInt24(Result, $0041); // Base 'A'
BigEndianValue.WriteWord(Result, 200); // Variant Glyph ID
var F14EndPos := Result.Position;
Result.Position := F14LengthPos;
BigEndianValue.WriteCardinal(Result, Cardinal(F14EndPos - Format14Pos));
Result.Position := 0;
end;
procedure TTestVariationSequences.TestUVSLookahead;
var
Stream: TMemoryStream;
Cmap: TPascalTypeCharacterMapTable;
begin
Stream := CreateMockCmapStream;
try
Cmap := TPascalTypeCharacterMapTable.Create(nil);
try
Cmap.LoadFromStream(Stream, Stream.Size);
// Test 1: Standard lookup
CheckEquals(100, Cmap.GetGlyphByCharacter($0041), 'Standard lookup for A failed');
// Test 2: UVS lookup match
var Index := 0;
CheckEquals(200, Cmap.GetGlyphByCharacter($0041, $FE00, Index), 'UVS lookup for A+VS1 failed');
CheckEquals(2, Index, 'Index not incremented correctly for UVS match');
// Test 3: UVS lookup no match (wrong selector)
Index := 0;
CheckEquals(100, Cmap.GetGlyphByCharacter($0041, $FE01, Index), 'UVS lookup for A+VS2 should return default');
CheckEquals(0, Index, 'Index should not be incremented for no UVS match');
finally
Cmap.Free;
end;
finally
Stream.Free;
end;
end;
procedure TTestVariationSequences.TestCreateGlyphStringWithUVS;
var
Stream: TMemoryStream;
FontFace: TPascalTypeFontFace;
GlyphString: TFontGlyphString;
CodePoints: TPascalTypeCodePoints;
begin
Stream := CreateMockCmapStream;
try
FontFace := TPascalTypeFontFace.Create;
try
// We need to mock a full font load or at least ttCmap
// TTFontFace expects head, hhea, maxp, name, post
// Let's just add cmap and hope for the best
var Cmap := TPascalTypeCharacterMapTable(FontFace.AddTable('cmap'));
Cmap.LoadFromStream(Stream, Stream.Size);
// We also need a space character for invisible glyph
// In CreateMockCmapStream, 'A' is $41. Let's add space $20 to format 4 manually?
// Or just assume GetGlyphByCodePoint(32) returns 0.
// Input: 'A' followed by VS1 ($FE00)
SetLength(CodePoints, 2);
CodePoints[0] := $0041;
CodePoints[1] := $FE00;
GlyphString := FontFace.CreateGlyphString(CodePoints);
try
CheckEquals(2, GlyphString.Count, 'Glyph count mismatch');
CheckEquals(200, GlyphString[0].GlyphID, 'Base glyph ID should be variant ID');
// Variation selector should be "hidden" (assigned to space or kept)
// Since we didn't define space in cmap, it might be 0 or standard.
// But our implementation sets NextGlyph.GlyphID to 0 (GlyphNotDef) if space lookup fails.
// Actually, it calls GetGlyphByCodePoint(32).
// Let's verify the selector is zeroed/hidden
CheckEquals(0, GlyphString[1].XAdvance, 'Variation selector should have 0 advance');
finally
GlyphString.Free;
end;
finally
FontFace.Free;
end;
finally
Stream.Free;
end;
end;
initialization
RegisterTest(TTestVariationSequences.Suite);
end.