1cb93a386Sopenharmony_cidescribe('Font Behavior', () => { 2cb93a386Sopenharmony_ci let container; 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_ci let notoSerifFontBuffer = null; 5cb93a386Sopenharmony_ci // This font is known to support kerning 6cb93a386Sopenharmony_ci const notoSerifFontLoaded = fetch('/assets/NotoSerif-Regular.ttf').then( 7cb93a386Sopenharmony_ci (response) => response.arrayBuffer()).then( 8cb93a386Sopenharmony_ci (buffer) => { 9cb93a386Sopenharmony_ci notoSerifFontBuffer = buffer; 10cb93a386Sopenharmony_ci }); 11cb93a386Sopenharmony_ci 12cb93a386Sopenharmony_ci let bungeeFontBuffer = null; 13cb93a386Sopenharmony_ci // This font has tofu for incorrect null terminators 14cb93a386Sopenharmony_ci // see https://bugs.chromium.org/p/skia/issues/detail?id=9314 15cb93a386Sopenharmony_ci const bungeeFontLoaded = fetch('/assets/Bungee-Regular.ttf').then( 16cb93a386Sopenharmony_ci (response) => response.arrayBuffer()).then( 17cb93a386Sopenharmony_ci (buffer) => { 18cb93a386Sopenharmony_ci bungeeFontBuffer = buffer; 19cb93a386Sopenharmony_ci }); 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci beforeEach(async () => { 22cb93a386Sopenharmony_ci await LoadCanvasKit; 23cb93a386Sopenharmony_ci await notoSerifFontLoaded; 24cb93a386Sopenharmony_ci await bungeeFontLoaded; 25cb93a386Sopenharmony_ci container = document.createElement('div'); 26cb93a386Sopenharmony_ci container.innerHTML = ` 27cb93a386Sopenharmony_ci <canvas width=600 height=600 id=test></canvas> 28cb93a386Sopenharmony_ci <canvas width=600 height=600 id=report></canvas>`; 29cb93a386Sopenharmony_ci document.body.appendChild(container); 30cb93a386Sopenharmony_ci }); 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci afterEach(() => { 33cb93a386Sopenharmony_ci document.body.removeChild(container); 34cb93a386Sopenharmony_ci }); 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci gm('monospace_text_on_path', (canvas) => { 37cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 38cb93a386Sopenharmony_ci paint.setAntiAlias(true); 39cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Stroke); 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci const font = new CanvasKit.Font(null, 24); 42cb93a386Sopenharmony_ci const fontPaint = new CanvasKit.Paint(); 43cb93a386Sopenharmony_ci fontPaint.setAntiAlias(true); 44cb93a386Sopenharmony_ci fontPaint.setStyle(CanvasKit.PaintStyle.Fill); 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci const arc = new CanvasKit.Path(); 48cb93a386Sopenharmony_ci arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); 49cb93a386Sopenharmony_ci arc.lineTo(210, 140); 50cb93a386Sopenharmony_ci arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci // Only 1 dot should show up in the image, because we run out of path. 53cb93a386Sopenharmony_ci const str = 'This téxt should follow the curve across contours...'; 54cb93a386Sopenharmony_ci const textBlob = CanvasKit.TextBlob.MakeOnPath(str, arc, font); 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci canvas.drawPath(arc, paint); 57cb93a386Sopenharmony_ci canvas.drawTextBlob(textBlob, 0, 0, fontPaint); 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_ci textBlob.delete(); 60cb93a386Sopenharmony_ci arc.delete(); 61cb93a386Sopenharmony_ci paint.delete(); 62cb93a386Sopenharmony_ci font.delete(); 63cb93a386Sopenharmony_ci fontPaint.delete(); 64cb93a386Sopenharmony_ci }); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci gm('serif_text_on_path', (canvas) => { 67cb93a386Sopenharmony_ci const notoSerif = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer); 68cb93a386Sopenharmony_ci 69cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 70cb93a386Sopenharmony_ci paint.setAntiAlias(true); 71cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Stroke); 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci const font = new CanvasKit.Font(notoSerif, 24); 74cb93a386Sopenharmony_ci const fontPaint = new CanvasKit.Paint(); 75cb93a386Sopenharmony_ci fontPaint.setAntiAlias(true); 76cb93a386Sopenharmony_ci fontPaint.setStyle(CanvasKit.PaintStyle.Fill); 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ci const arc = new CanvasKit.Path(); 79cb93a386Sopenharmony_ci arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); 80cb93a386Sopenharmony_ci arc.lineTo(210, 140); 81cb93a386Sopenharmony_ci arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci const str = 'This téxt should follow the curve across contours...'; 84cb93a386Sopenharmony_ci const textBlob = CanvasKit.TextBlob.MakeOnPath(str, arc, font, 60.5); 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ci canvas.drawPath(arc, paint); 87cb93a386Sopenharmony_ci canvas.drawTextBlob(textBlob, 0, 0, fontPaint); 88cb93a386Sopenharmony_ci 89cb93a386Sopenharmony_ci textBlob.delete(); 90cb93a386Sopenharmony_ci arc.delete(); 91cb93a386Sopenharmony_ci paint.delete(); 92cb93a386Sopenharmony_ci notoSerif.delete(); 93cb93a386Sopenharmony_ci font.delete(); 94cb93a386Sopenharmony_ci fontPaint.delete(); 95cb93a386Sopenharmony_ci }); 96cb93a386Sopenharmony_ci 97cb93a386Sopenharmony_ci // https://bugs.chromium.org/p/skia/issues/detail?id=9314 98cb93a386Sopenharmony_ci gm('nullterminators_skbug_9314', (canvas) => { 99cb93a386Sopenharmony_ci const bungee = CanvasKit.Typeface.MakeFreeTypeFaceFromData(bungeeFontBuffer); 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci // yellow, to make sure tofu is plainly visible 102cb93a386Sopenharmony_ci canvas.clear(CanvasKit.Color(255, 255, 0, 1)); 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ci const font = new CanvasKit.Font(bungee, 24); 105cb93a386Sopenharmony_ci const fontPaint = new CanvasKit.Paint(); 106cb93a386Sopenharmony_ci fontPaint.setAntiAlias(true); 107cb93a386Sopenharmony_ci fontPaint.setStyle(CanvasKit.PaintStyle.Fill); 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci 110cb93a386Sopenharmony_ci const str = 'This is téxt'; 111cb93a386Sopenharmony_ci const textBlob = CanvasKit.TextBlob.MakeFromText(str + ' text blob', font); 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci canvas.drawTextBlob(textBlob, 10, 50, fontPaint); 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci canvas.drawText(str + ' normal', 10, 100, fontPaint, font); 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci canvas.drawText('null terminator ->\u0000<- on purpose', 10, 150, fontPaint, font); 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci textBlob.delete(); 120cb93a386Sopenharmony_ci bungee.delete(); 121cb93a386Sopenharmony_ci font.delete(); 122cb93a386Sopenharmony_ci fontPaint.delete(); 123cb93a386Sopenharmony_ci }); 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci gm('textblobs_with_glyphs', (canvas) => { 126cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 127cb93a386Sopenharmony_ci const notoSerif = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer); 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci const font = new CanvasKit.Font(notoSerif, 24); 130cb93a386Sopenharmony_ci const bluePaint = new CanvasKit.Paint(); 131cb93a386Sopenharmony_ci bluePaint.setColor(CanvasKit.parseColorString('#04083f')); // arbitrary deep blue 132cb93a386Sopenharmony_ci bluePaint.setAntiAlias(true); 133cb93a386Sopenharmony_ci bluePaint.setStyle(CanvasKit.PaintStyle.Fill); 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci const redPaint = new CanvasKit.Paint(); 136cb93a386Sopenharmony_ci redPaint.setColor(CanvasKit.parseColorString('#770b1e')); // arbitrary deep red 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_ci const ids = notoSerif.getGlyphIDs('AEGIS ægis'); 139cb93a386Sopenharmony_ci expect(ids.length).toEqual(10); // one glyph id per glyph 140cb93a386Sopenharmony_ci expect(ids[0]).toEqual(36); // spot check this, should be consistent as long as the font is. 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci const bounds = font.getGlyphBounds(ids, bluePaint); 143cb93a386Sopenharmony_ci expect(bounds.length).toEqual(40); // 4 measurements per glyph 144cb93a386Sopenharmony_ci expect(bounds[0]).toEqual(0); // again, spot check the measurements for the first glyph. 145cb93a386Sopenharmony_ci expect(bounds[1]).toEqual(-17); 146cb93a386Sopenharmony_ci expect(bounds[2]).toEqual(17); 147cb93a386Sopenharmony_ci expect(bounds[3]).toEqual(0); 148cb93a386Sopenharmony_ci 149cb93a386Sopenharmony_ci const widths = font.getGlyphWidths(ids, bluePaint); 150cb93a386Sopenharmony_ci expect(widths.length).toEqual(10); // 1 width per glyph 151cb93a386Sopenharmony_ci expect(widths[0]).toEqual(17); 152cb93a386Sopenharmony_ci 153cb93a386Sopenharmony_ci const topBlob = CanvasKit.TextBlob.MakeFromGlyphs(ids, font); 154cb93a386Sopenharmony_ci canvas.drawTextBlob(topBlob, 5, 30, bluePaint); 155cb93a386Sopenharmony_ci canvas.drawTextBlob(topBlob, 5, 60, redPaint); 156cb93a386Sopenharmony_ci topBlob.delete(); 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci const mIDs = CanvasKit.MallocGlyphIDs(ids.length); 159cb93a386Sopenharmony_ci const mArr = mIDs.toTypedArray(); 160cb93a386Sopenharmony_ci mArr.set(ids); 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci const mXforms = CanvasKit.Malloc(Float32Array, ids.length * 4); 163cb93a386Sopenharmony_ci const mXformsArr = mXforms.toTypedArray(); 164cb93a386Sopenharmony_ci // Draw each glyph rotated slightly and slightly lower than the glyph before it. 165cb93a386Sopenharmony_ci let currX = 0; 166cb93a386Sopenharmony_ci for (let i = 0; i < ids.length; i++) { 167cb93a386Sopenharmony_ci mXformsArr[i * 4] = Math.cos(-Math.PI / 16); // scos 168cb93a386Sopenharmony_ci mXformsArr[i * 4 + 1] = Math.sin(-Math.PI / 16); // ssin 169cb93a386Sopenharmony_ci mXformsArr[i * 4 + 2] = currX; // tx 170cb93a386Sopenharmony_ci mXformsArr[i * 4 + 3] = i*2; // ty 171cb93a386Sopenharmony_ci currX += widths[i]; 172cb93a386Sopenharmony_ci } 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_ci const bottomBlob = CanvasKit.TextBlob.MakeFromRSXformGlyphs(mIDs, mXforms, font); 175cb93a386Sopenharmony_ci canvas.drawTextBlob(bottomBlob, 5, 110, bluePaint); 176cb93a386Sopenharmony_ci canvas.drawTextBlob(bottomBlob, 5, 140, redPaint); 177cb93a386Sopenharmony_ci bottomBlob.delete(); 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_ci CanvasKit.Free(mIDs); 180cb93a386Sopenharmony_ci CanvasKit.Free(mXforms); 181cb93a386Sopenharmony_ci bluePaint.delete(); 182cb93a386Sopenharmony_ci redPaint.delete(); 183cb93a386Sopenharmony_ci notoSerif.delete(); 184cb93a386Sopenharmony_ci font.delete(); 185cb93a386Sopenharmony_ci }); 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci it('can make a font mgr with passed in fonts', () => { 188cb93a386Sopenharmony_ci // CanvasKit.FontMgr.FromData([bungeeFontBuffer, notoSerifFontBuffer]) also works 189cb93a386Sopenharmony_ci const fontMgr = CanvasKit.FontMgr.FromData(bungeeFontBuffer, notoSerifFontBuffer); 190cb93a386Sopenharmony_ci expect(fontMgr).toBeTruthy(); 191cb93a386Sopenharmony_ci expect(fontMgr.countFamilies()).toBe(2); 192cb93a386Sopenharmony_ci // in debug mode, let's list them. 193cb93a386Sopenharmony_ci if (fontMgr.dumpFamilies) { 194cb93a386Sopenharmony_ci fontMgr.dumpFamilies(); 195cb93a386Sopenharmony_ci } 196cb93a386Sopenharmony_ci fontMgr.delete(); 197cb93a386Sopenharmony_ci }); 198cb93a386Sopenharmony_ci 199cb93a386Sopenharmony_ci it('can make a font provider with passed in fonts and aliases', () => { 200cb93a386Sopenharmony_ci const fontProvider = CanvasKit.TypefaceFontProvider.Make(); 201cb93a386Sopenharmony_ci fontProvider.registerFont(bungeeFontBuffer, "My Bungee Alias"); 202cb93a386Sopenharmony_ci fontProvider.registerFont(notoSerifFontBuffer, "My Noto Serif Alias"); 203cb93a386Sopenharmony_ci expect(fontProvider).toBeTruthy(); 204cb93a386Sopenharmony_ci expect(fontProvider.countFamilies()).toBe(2); 205cb93a386Sopenharmony_ci // in debug mode, let's list them. 206cb93a386Sopenharmony_ci if (fontProvider.dumpFamilies) { 207cb93a386Sopenharmony_ci fontProvider.dumpFamilies(); 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci fontProvider.delete(); 210cb93a386Sopenharmony_ci }); 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_ci gm('various_font_formats', (canvas, fetchedByteBuffers) => { 213cb93a386Sopenharmony_ci const fontPaint = new CanvasKit.Paint(); 214cb93a386Sopenharmony_ci fontPaint.setAntiAlias(true); 215cb93a386Sopenharmony_ci fontPaint.setStyle(CanvasKit.PaintStyle.Fill); 216cb93a386Sopenharmony_ci const inputs = [{ 217cb93a386Sopenharmony_ci type: '.ttf font', 218cb93a386Sopenharmony_ci buffer: bungeeFontBuffer, 219cb93a386Sopenharmony_ci y: 60, 220cb93a386Sopenharmony_ci },{ 221cb93a386Sopenharmony_ci type: '.otf font', 222cb93a386Sopenharmony_ci buffer: fetchedByteBuffers[0], 223cb93a386Sopenharmony_ci y: 90, 224cb93a386Sopenharmony_ci },{ 225cb93a386Sopenharmony_ci type: '.woff font', 226cb93a386Sopenharmony_ci buffer: fetchedByteBuffers[1], 227cb93a386Sopenharmony_ci y: 120, 228cb93a386Sopenharmony_ci },{ 229cb93a386Sopenharmony_ci type: '.woff2 font', 230cb93a386Sopenharmony_ci buffer: fetchedByteBuffers[2], 231cb93a386Sopenharmony_ci y: 150, 232cb93a386Sopenharmony_ci }]; 233cb93a386Sopenharmony_ci 234cb93a386Sopenharmony_ci const defaultFont = new CanvasKit.Font(null, 24); 235cb93a386Sopenharmony_ci canvas.drawText(`The following should be ${inputs.length + 1} lines of text:`, 5, 30, fontPaint, defaultFont); 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ci for (const fontType of inputs) { 238cb93a386Sopenharmony_ci // smoke test that the font bytes loaded. 239cb93a386Sopenharmony_ci expect(fontType.buffer).toBeTruthy(fontType.type + ' did not load'); 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci const typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(fontType.buffer); 242cb93a386Sopenharmony_ci const font = new CanvasKit.Font(typeface, 24); 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci if (font && typeface) { 245cb93a386Sopenharmony_ci canvas.drawText(fontType.type + ' loaded', 5, fontType.y, fontPaint, font); 246cb93a386Sopenharmony_ci } else { 247cb93a386Sopenharmony_ci canvas.drawText(fontType.type + ' *not* loaded', 5, fontType.y, fontPaint, defaultFont); 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci font && font.delete(); 250cb93a386Sopenharmony_ci typeface && typeface.delete(); 251cb93a386Sopenharmony_ci } 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci // The only ttc font I could find was 14 MB big, so I'm using the smaller test font, 254cb93a386Sopenharmony_ci // which doesn't have very many glyphs in it, so we just check that we got a non-zero 255cb93a386Sopenharmony_ci // typeface for it. I was able to load NotoSansCJK-Regular.ttc just fine in a 256cb93a386Sopenharmony_ci // manual test. 257cb93a386Sopenharmony_ci const typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(fetchedByteBuffers[3]); 258cb93a386Sopenharmony_ci expect(typeface).toBeTruthy('.ttc font'); 259cb93a386Sopenharmony_ci if (typeface) { 260cb93a386Sopenharmony_ci canvas.drawText('.ttc loaded', 5, 180, fontPaint, defaultFont); 261cb93a386Sopenharmony_ci typeface.delete(); 262cb93a386Sopenharmony_ci } else { 263cb93a386Sopenharmony_ci canvas.drawText('.ttc *not* loaded', 5, 180, fontPaint, defaultFont); 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci defaultFont.delete(); 267cb93a386Sopenharmony_ci fontPaint.delete(); 268cb93a386Sopenharmony_ci }, '/assets/Roboto-Regular.otf', '/assets/Roboto-Regular.woff', '/assets/Roboto-Regular.woff2', '/assets/test.ttc'); 269cb93a386Sopenharmony_ci 270cb93a386Sopenharmony_ci it('can measure text very precisely with proper settings', () => { 271cb93a386Sopenharmony_ci const typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer); 272cb93a386Sopenharmony_ci const fontSizes = [257, 100, 11]; 273cb93a386Sopenharmony_ci // The point of these values is to let us know 1) we can measure to sub-pixel levels 274cb93a386Sopenharmony_ci // and 2) that measurements don't drastically change. If these change a little bit, 275cb93a386Sopenharmony_ci // just update them with the new values. For super-accurate readings, one could 276cb93a386Sopenharmony_ci // run a C++ snippet of code and compare the values, but that is likely unnecessary 277cb93a386Sopenharmony_ci // unless we suspect a bug with the bindings. 278cb93a386Sopenharmony_ci const expectedSizes = [241.06299, 93.79883, 10.31787]; 279cb93a386Sopenharmony_ci for (const idx in fontSizes) { 280cb93a386Sopenharmony_ci const font = new CanvasKit.Font(typeface, fontSizes[idx]); 281cb93a386Sopenharmony_ci font.setHinting(CanvasKit.FontHinting.None); 282cb93a386Sopenharmony_ci font.setLinearMetrics(true); 283cb93a386Sopenharmony_ci font.setSubpixel(true); 284cb93a386Sopenharmony_ci 285cb93a386Sopenharmony_ci const ids = font.getGlyphIDs('M'); 286cb93a386Sopenharmony_ci const widths = font.getGlyphWidths(ids); 287cb93a386Sopenharmony_ci expect(widths[0]).toBeCloseTo(expectedSizes[idx], 5); 288cb93a386Sopenharmony_ci font.delete(); 289cb93a386Sopenharmony_ci } 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ci typeface.delete(); 292cb93a386Sopenharmony_ci }); 293cb93a386Sopenharmony_ci 294cb93a386Sopenharmony_ci gm('font_edging', (canvas) => { 295cb93a386Sopenharmony_ci // Draw a small font scaled up to see the aliasing artifacts. 296cb93a386Sopenharmony_ci canvas.scale(8, 8); 297cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 298cb93a386Sopenharmony_ci const notoSerif = CanvasKit.Typeface.MakeFreeTypeFaceFromData(notoSerifFontBuffer); 299cb93a386Sopenharmony_ci 300cb93a386Sopenharmony_ci const textPaint = new CanvasKit.Paint(); 301cb93a386Sopenharmony_ci const annotationFont = new CanvasKit.Font(notoSerif, 6); 302cb93a386Sopenharmony_ci 303cb93a386Sopenharmony_ci canvas.drawText('Default', 5, 5, textPaint, annotationFont); 304cb93a386Sopenharmony_ci canvas.drawText('Alias', 5, 25, textPaint, annotationFont); 305cb93a386Sopenharmony_ci canvas.drawText('AntiAlias', 5, 45, textPaint, annotationFont); 306cb93a386Sopenharmony_ci canvas.drawText('Subpixel', 5, 65, textPaint, annotationFont); 307cb93a386Sopenharmony_ci 308cb93a386Sopenharmony_ci const testFont = new CanvasKit.Font(notoSerif, 20); 309cb93a386Sopenharmony_ci 310cb93a386Sopenharmony_ci canvas.drawText('SEA', 35, 15, textPaint, testFont); 311cb93a386Sopenharmony_ci testFont.setEdging(CanvasKit.FontEdging.Alias); 312cb93a386Sopenharmony_ci canvas.drawText('SEA', 35, 35, textPaint, testFont); 313cb93a386Sopenharmony_ci testFont.setEdging(CanvasKit.FontEdging.AntiAlias); 314cb93a386Sopenharmony_ci canvas.drawText('SEA', 35, 55, textPaint, testFont); 315cb93a386Sopenharmony_ci testFont.setEdging(CanvasKit.FontEdging.SubpixelAntiAlias); 316cb93a386Sopenharmony_ci canvas.drawText('SEA', 35, 75, textPaint, testFont); 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ci textPaint.delete(); 319cb93a386Sopenharmony_ci annotationFont.delete(); 320cb93a386Sopenharmony_ci testFont.delete(); 321cb93a386Sopenharmony_ci notoSerif.delete(); 322cb93a386Sopenharmony_ci }); 323cb93a386Sopenharmony_ci 324cb93a386Sopenharmony_ci it('can get the intercepts of glyphs', () => { 325cb93a386Sopenharmony_ci const font = new CanvasKit.Font(null, 100); 326cb93a386Sopenharmony_ci const ids = font.getGlyphIDs('I'); 327cb93a386Sopenharmony_ci expect(ids.length).toEqual(1); 328cb93a386Sopenharmony_ci 329cb93a386Sopenharmony_ci // aim for the middle of the I at 100 point, expecting a hit 330cb93a386Sopenharmony_ci let sects = font.getGlyphIntercepts(ids, [0, 0], -60, -40); 331cb93a386Sopenharmony_ci expect(sects.length).toEqual(2, "expected one pair of intercepts"); 332cb93a386Sopenharmony_ci expect(sects[0]).toBeCloseTo(25.39063, 5); 333cb93a386Sopenharmony_ci expect(sects[1]).toBeCloseTo(34.52148, 5); 334cb93a386Sopenharmony_ci 335cb93a386Sopenharmony_ci // aim below the baseline where we expect no intercepts 336cb93a386Sopenharmony_ci sects = font.getGlyphIntercepts(ids, [0, 0], 20, 30); 337cb93a386Sopenharmony_ci expect(sects.length).toEqual(0, "expected no intercepts"); 338cb93a386Sopenharmony_ci font.delete(); 339cb93a386Sopenharmony_ci }); 340cb93a386Sopenharmony_ci 341cb93a386Sopenharmony_ci it('can use mallocd and normal arrays', () => { 342cb93a386Sopenharmony_ci const font = new CanvasKit.Font(null, 100); 343cb93a386Sopenharmony_ci const ids = font.getGlyphIDs('I'); 344cb93a386Sopenharmony_ci expect(ids.length).toEqual(1); 345cb93a386Sopenharmony_ci const glyphID = ids[0]; 346cb93a386Sopenharmony_ci 347cb93a386Sopenharmony_ci // aim for the middle of the I at 100 point, expecting a hit 348cb93a386Sopenharmony_ci const sects = font.getGlyphIntercepts(Array.of(glyphID), Float32Array.of(0, 0), -60, -40); 349cb93a386Sopenharmony_ci expect(sects.length).toEqual(2); 350cb93a386Sopenharmony_ci expect(sects[0]).toBeLessThan(sects[1]); 351cb93a386Sopenharmony_ci // these values were recorded from the first time it was run 352cb93a386Sopenharmony_ci expect(sects[0]).toBeCloseTo(25.39063, 5); 353cb93a386Sopenharmony_ci expect(sects[1]).toBeCloseTo(34.52148, 5); 354cb93a386Sopenharmony_ci 355cb93a386Sopenharmony_ci const free_list = []; // will free CanvasKit.Malloc objects at the end 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_ci // Want to exercise 4 different ways we can receive an array: 358cb93a386Sopenharmony_ci // 1. normal array 359cb93a386Sopenharmony_ci // 2. typed-array 360cb93a386Sopenharmony_ci // 3. CanvasKit.Malloc typeed-array 361cb93a386Sopenharmony_ci // 4. CavnasKit.Malloc (raw) 362cb93a386Sopenharmony_ci 363cb93a386Sopenharmony_ci const id_makers = [ 364cb93a386Sopenharmony_ci (id) => [ id ], 365cb93a386Sopenharmony_ci (id) => new Uint16Array([ id ]), 366cb93a386Sopenharmony_ci (id) => { 367cb93a386Sopenharmony_ci const a = CanvasKit.Malloc(Uint16Array, 1); 368cb93a386Sopenharmony_ci free_list.push(a); 369cb93a386Sopenharmony_ci const ta = a.toTypedArray(); 370cb93a386Sopenharmony_ci ta[0] = id; 371cb93a386Sopenharmony_ci return ta; // return typed-array 372cb93a386Sopenharmony_ci }, 373cb93a386Sopenharmony_ci (id) => { 374cb93a386Sopenharmony_ci const a = CanvasKit.Malloc(Uint16Array, 1); 375cb93a386Sopenharmony_ci free_list.push(a); 376cb93a386Sopenharmony_ci a.toTypedArray()[0] = id; 377cb93a386Sopenharmony_ci return a; // return raw obj 378cb93a386Sopenharmony_ci }, 379cb93a386Sopenharmony_ci ]; 380cb93a386Sopenharmony_ci const pos_makers = [ 381cb93a386Sopenharmony_ci (x, y) => [ x, y ], 382cb93a386Sopenharmony_ci (x, y) => new Float32Array([ x, y ]), 383cb93a386Sopenharmony_ci (x, y) => { 384cb93a386Sopenharmony_ci const a = CanvasKit.Malloc(Float32Array, 2); 385cb93a386Sopenharmony_ci free_list.push(a); 386cb93a386Sopenharmony_ci const ta = a.toTypedArray(); 387cb93a386Sopenharmony_ci ta[0] = x; 388cb93a386Sopenharmony_ci ta[1] = y; 389cb93a386Sopenharmony_ci return ta; // return typed-array 390cb93a386Sopenharmony_ci }, 391cb93a386Sopenharmony_ci (x, y) => { 392cb93a386Sopenharmony_ci const a = CanvasKit.Malloc(Float32Array, 2); 393cb93a386Sopenharmony_ci free_list.push(a); 394cb93a386Sopenharmony_ci const ta = a.toTypedArray(); 395cb93a386Sopenharmony_ci ta[0] = x; 396cb93a386Sopenharmony_ci ta[1] = y; 397cb93a386Sopenharmony_ci return a; // return raw obj 398cb93a386Sopenharmony_ci }, 399cb93a386Sopenharmony_ci ]; 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci for (const idm of id_makers) { 402cb93a386Sopenharmony_ci for (const posm of pos_makers) { 403cb93a386Sopenharmony_ci const s = font.getGlyphIntercepts(idm(glyphID), posm(0, 0), -60, -40); 404cb93a386Sopenharmony_ci expect(s.length).toEqual(sects.length); 405cb93a386Sopenharmony_ci for (let i = 0; i < s.length; ++i) { 406cb93a386Sopenharmony_ci expect(s[i]).toEqual(sects[i]); 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci } 409cb93a386Sopenharmony_ci 410cb93a386Sopenharmony_ci } 411cb93a386Sopenharmony_ci 412cb93a386Sopenharmony_ci free_list.forEach(obj => CanvasKit.Free(obj)); 413cb93a386Sopenharmony_ci font.delete(); 414cb93a386Sopenharmony_ci }); 415cb93a386Sopenharmony_ci 416cb93a386Sopenharmony_ci}); 417