@@ -116,4 +116,112 @@ describe('FontSubstitution', () => {
116116 expect ( string . runs [ 1 ] . attributes . font ) . toEqual ( [ SimplifiedChineseFont ] ) ;
117117 } ) ;
118118 } ) ;
119+
120+ describe ( 'Surrogate Pairs' , ( ) => {
121+ const EmojiFont = {
122+ name : 'EmojiFont' ,
123+ unitsPerEm : 1000 ,
124+ hasGlyphForCodePoint : ( codePoint ) =>
125+ codePoint === 0x1f600 || // 😀 Grinning Face
126+ codePoint === 0x1f60d , // 😍 Heart Eyes
127+ } ;
128+
129+ const RegularFont = {
130+ name : 'RegularFont' ,
131+ unitsPerEm : 1000 ,
132+ hasGlyphForCodePoint : ( codePoint ) => codePoint < 0xffff ,
133+ } ;
134+
135+ test ( 'should handle surrogate pairs in text' , ( ) => {
136+ const run = {
137+ start : 0 ,
138+ end : 3 , // A + surrogate pair (2 JS chars)
139+ attributes : {
140+ font : [ RegularFont , EmojiFont ] ,
141+ fontSize : 12 ,
142+ } ,
143+ } as any ;
144+
145+ // A + 😀 (Grinning Face emoji, surrogate pair)
146+ const string = instance ( { string : 'A😀' , runs : [ run ] } ) ;
147+
148+ expect ( string ) . toHaveProperty ( 'string' , 'A😀' ) ;
149+ expect ( string . runs ) . toHaveLength ( 2 ) ;
150+
151+ // First run is the letter "A" with RegularFont
152+ expect ( string . runs [ 0 ] ) . toHaveProperty ( 'start' , 0 ) ;
153+ expect ( string . runs [ 0 ] ) . toHaveProperty ( 'end' , 1 ) ;
154+ expect ( string . runs [ 0 ] . attributes . font ) . toEqual ( [ RegularFont ] ) ;
155+
156+ // Second run is the emoji "😀" with EmojiFont
157+ expect ( string . runs [ 1 ] ) . toHaveProperty ( 'start' , 1 ) ;
158+ expect ( string . runs [ 1 ] ) . toHaveProperty ( 'end' , 3 ) ; // Surrogate pair takes 2 positions
159+ expect ( string . runs [ 1 ] . attributes . font ) . toEqual ( [ EmojiFont ] ) ;
160+ } ) ;
161+
162+ test ( 'should handle multiple surrogate pairs in text' , ( ) => {
163+ const run = {
164+ start : 0 ,
165+ end : 5 , // A + surrogate pair (2) + B (1) + surrogate pair (2) = 5
166+ attributes : {
167+ font : [ RegularFont , EmojiFont ] ,
168+ fontSize : 12 ,
169+ } ,
170+ } as any ;
171+
172+ // A + 😀 (Grinning Face) + 😍 (Heart Eyes)
173+ const string = instance ( { string : 'A😀😍' , runs : [ run ] } ) ;
174+
175+ expect ( string ) . toHaveProperty ( 'string' , 'A😀😍' ) ;
176+ expect ( string . runs ) . toHaveLength ( 2 ) ;
177+
178+ // First run is the letter "A" with RegularFont
179+ expect ( string . runs [ 0 ] ) . toHaveProperty ( 'start' , 0 ) ;
180+ expect ( string . runs [ 0 ] ) . toHaveProperty ( 'end' , 1 ) ;
181+ expect ( string . runs [ 0 ] . attributes . font ) . toEqual ( [ RegularFont ] ) ;
182+
183+ // Second run is both emojis with EmojiFont
184+ expect ( string . runs [ 1 ] ) . toHaveProperty ( 'start' , 1 ) ;
185+ expect ( string . runs [ 1 ] ) . toHaveProperty ( 'end' , 5 ) ; // Two surrogate pairs
186+ expect ( string . runs [ 1 ] . attributes . font ) . toEqual ( [ EmojiFont ] ) ;
187+ } ) ;
188+
189+ test ( 'should handle surrogate pairs interspersed with regular text' , ( ) => {
190+ const run = {
191+ start : 0 ,
192+ end : 7 , // A + surrogate pair (2) + B + surrogate pair (2) + C = 7
193+ attributes : {
194+ font : [ RegularFont , EmojiFont ] ,
195+ fontSize : 12 ,
196+ } ,
197+ } as any ;
198+
199+ // A + 😀 (Grinning Face) + B + 😍 (Heart Eyes) + C
200+ const string = instance ( { string : 'A😀B😍C' , runs : [ run ] } ) ;
201+
202+ expect ( string ) . toHaveProperty ( 'string' , 'A😀B😍C' ) ;
203+ expect ( string . runs ) . toHaveLength ( 5 ) ;
204+
205+ // Alternating fonts for regular chars and emojis
206+ expect ( string . runs [ 0 ] ) . toHaveProperty ( 'start' , 0 ) ;
207+ expect ( string . runs [ 0 ] ) . toHaveProperty ( 'end' , 1 ) ;
208+ expect ( string . runs [ 0 ] . attributes . font ) . toEqual ( [ RegularFont ] ) ;
209+
210+ expect ( string . runs [ 1 ] ) . toHaveProperty ( 'start' , 1 ) ;
211+ expect ( string . runs [ 1 ] ) . toHaveProperty ( 'end' , 3 ) ;
212+ expect ( string . runs [ 1 ] . attributes . font ) . toEqual ( [ EmojiFont ] ) ;
213+
214+ expect ( string . runs [ 2 ] ) . toHaveProperty ( 'start' , 3 ) ;
215+ expect ( string . runs [ 2 ] ) . toHaveProperty ( 'end' , 4 ) ;
216+ expect ( string . runs [ 2 ] . attributes . font ) . toEqual ( [ RegularFont ] ) ;
217+
218+ expect ( string . runs [ 3 ] ) . toHaveProperty ( 'start' , 4 ) ;
219+ expect ( string . runs [ 3 ] ) . toHaveProperty ( 'end' , 6 ) ;
220+ expect ( string . runs [ 3 ] . attributes . font ) . toEqual ( [ EmojiFont ] ) ;
221+
222+ expect ( string . runs [ 4 ] ) . toHaveProperty ( 'start' , 6 ) ;
223+ expect ( string . runs [ 4 ] ) . toHaveProperty ( 'end' , 7 ) ;
224+ expect ( string . runs [ 4 ] . attributes . font ) . toEqual ( [ RegularFont ] ) ;
225+ } ) ;
226+ } ) ;
119227} ) ;
0 commit comments