xref: /aosp_15_r20/external/skia/modules/canvaskit/tests/paragraph_test.js (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Workerdescribe('Paragraph Behavior', function() {
2*c8dee2aaSAndroid Build Coastguard Worker    let container;
3*c8dee2aaSAndroid Build Coastguard Worker
4*c8dee2aaSAndroid Build Coastguard Worker    const assetLoadingPromises = [];
5*c8dee2aaSAndroid Build Coastguard Worker    let notoSerifFontBuffer = null;
6*c8dee2aaSAndroid Build Coastguard Worker    // This font is known to support kerning
7*c8dee2aaSAndroid Build Coastguard Worker    assetLoadingPromises.push(fetch('/assets/NotoSerif-Regular.ttf').then(
8*c8dee2aaSAndroid Build Coastguard Worker        (response) => response.arrayBuffer()).then(
9*c8dee2aaSAndroid Build Coastguard Worker        (buffer) => {
10*c8dee2aaSAndroid Build Coastguard Worker            notoSerifFontBuffer = buffer;
11*c8dee2aaSAndroid Build Coastguard Worker        }));
12*c8dee2aaSAndroid Build Coastguard Worker
13*c8dee2aaSAndroid Build Coastguard Worker    let notoSerifBoldItalicFontBuffer = null;
14*c8dee2aaSAndroid Build Coastguard Worker    assetLoadingPromises.push(fetch('/assets/NotoSerif-BoldItalic.ttf').then(
15*c8dee2aaSAndroid Build Coastguard Worker        (response) => response.arrayBuffer()).then(
16*c8dee2aaSAndroid Build Coastguard Worker        (buffer) => {
17*c8dee2aaSAndroid Build Coastguard Worker            notoSerifBoldItalicFontBuffer = buffer;
18*c8dee2aaSAndroid Build Coastguard Worker        }));
19*c8dee2aaSAndroid Build Coastguard Worker
20*c8dee2aaSAndroid Build Coastguard Worker    let emojiFontBuffer = null;
21*c8dee2aaSAndroid Build Coastguard Worker    assetLoadingPromises.push(fetch('/assets/NotoColorEmoji.ttf').then(
22*c8dee2aaSAndroid Build Coastguard Worker        (response) => response.arrayBuffer()).then(
23*c8dee2aaSAndroid Build Coastguard Worker        (buffer) => {
24*c8dee2aaSAndroid Build Coastguard Worker            emojiFontBuffer = buffer;
25*c8dee2aaSAndroid Build Coastguard Worker        }));
26*c8dee2aaSAndroid Build Coastguard Worker
27*c8dee2aaSAndroid Build Coastguard Worker    let robotoFontBuffer = null;
28*c8dee2aaSAndroid Build Coastguard Worker    assetLoadingPromises.push(fetch('/assets/Roboto-Regular.otf').then(
29*c8dee2aaSAndroid Build Coastguard Worker        (response) => response.arrayBuffer()).then(
30*c8dee2aaSAndroid Build Coastguard Worker        (buffer) => {
31*c8dee2aaSAndroid Build Coastguard Worker            robotoFontBuffer = buffer;
32*c8dee2aaSAndroid Build Coastguard Worker        }));
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker    let robotoVariableFontBuffer = null;
35*c8dee2aaSAndroid Build Coastguard Worker    assetLoadingPromises.push(fetch('/assets/RobotoSlab-VariableFont_wght.ttf').then(
36*c8dee2aaSAndroid Build Coastguard Worker        (response) => response.arrayBuffer()).then(
37*c8dee2aaSAndroid Build Coastguard Worker        (buffer) => {
38*c8dee2aaSAndroid Build Coastguard Worker            robotoVariableFontBuffer = buffer;
39*c8dee2aaSAndroid Build Coastguard Worker        }));
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker    beforeEach(async () => {
42*c8dee2aaSAndroid Build Coastguard Worker        await EverythingLoaded;
43*c8dee2aaSAndroid Build Coastguard Worker        await Promise.all(assetLoadingPromises);
44*c8dee2aaSAndroid Build Coastguard Worker        container = document.createElement('div');
45*c8dee2aaSAndroid Build Coastguard Worker        container.innerHTML = `
46*c8dee2aaSAndroid Build Coastguard Worker            <canvas width=600 height=600 id=test></canvas>
47*c8dee2aaSAndroid Build Coastguard Worker            <canvas width=600 height=600 id=report></canvas>`;
48*c8dee2aaSAndroid Build Coastguard Worker        document.body.appendChild(container);
49*c8dee2aaSAndroid Build Coastguard Worker    });
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker    afterEach(() => {
52*c8dee2aaSAndroid Build Coastguard Worker        document.body.removeChild(container);
53*c8dee2aaSAndroid Build Coastguard Worker    });
54*c8dee2aaSAndroid Build Coastguard Worker
55*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_basic', (canvas) => {
56*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
59*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
62*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
63*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
64*c8dee2aaSAndroid Build Coastguard Worker
65*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
68*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
69*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
70*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
71*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
72*c8dee2aaSAndroid Build Coastguard Worker            },
73*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
74*c8dee2aaSAndroid Build Coastguard Worker            maxLines: 8,
75*c8dee2aaSAndroid Build Coastguard Worker            ellipsis: '.._.',
76*c8dee2aaSAndroid Build Coastguard Worker        });
77*c8dee2aaSAndroid Build Coastguard Worker
78*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
79*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker        const blueText = new CanvasKit.TextStyle({
82*c8dee2aaSAndroid Build Coastguard Worker            backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
83*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.Color(48, 37, 199),
84*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Noto Serif'],
85*c8dee2aaSAndroid Build Coastguard Worker            decoration: CanvasKit.LineThroughDecoration,
86*c8dee2aaSAndroid Build Coastguard Worker            decorationThickness: 1.5, // multiplier based on font size
87*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 24,
88*c8dee2aaSAndroid Build Coastguard Worker        });
89*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(blueText);
90*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`)
91*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
92*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(` I'm done with the blue now. `)
93*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Now I hope we should stop before we get 8 lines tall. `);
94*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
95*c8dee2aaSAndroid Build Coastguard Worker
96*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
97*c8dee2aaSAndroid Build Coastguard Worker
98*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.didExceedMaxLines()).toBeTruthy();
99*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3);
100*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getHeight()).toEqual(240);
101*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3);
102*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getLongestLine()).toBeCloseTo(193.820, 3);
103*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1444.250, 3);
104*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getMaxWidth()).toEqual(200);
105*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3);
106*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getWordBoundary(8)).toEqual({
107*c8dee2aaSAndroid Build Coastguard Worker            start: 0,
108*c8dee2aaSAndroid Build Coastguard Worker            end: 14,
109*c8dee2aaSAndroid Build Coastguard Worker        });
110*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getWordBoundary(25)).toEqual({
111*c8dee2aaSAndroid Build Coastguard Worker            start: 25,
112*c8dee2aaSAndroid Build Coastguard Worker            end: 26,
113*c8dee2aaSAndroid Build Coastguard Worker        });
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker
116*c8dee2aaSAndroid Build Coastguard Worker        const lineMetrics = paragraph.getLineMetrics();
117*c8dee2aaSAndroid Build Coastguard Worker        expect(lineMetrics.length).toEqual(8); // 8 lines worth of metrics
118*c8dee2aaSAndroid Build Coastguard Worker        const flm = lineMetrics[0]; // First Line Metric
119*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.startIndex).toEqual(0);
120*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.endExcludingWhitespaces).toEqual(14)
121*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.endIndex).toEqual(14); // Including whitespaces but excluding newlines
122*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.endIncludingNewline).toEqual(15);
123*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.lineNumber).toEqual(0);
124*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.isHardBreak).toEqual(true);
125*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.ascent).toBeCloseTo(21.377, 3);
126*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.descent).toBeCloseTo(5.859, 3);
127*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.height).toBeCloseTo(27.000, 3);
128*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.width).toBeCloseTo(172.360, 3);
129*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.left).toBeCloseTo(13.818, 3);
130*c8dee2aaSAndroid Build Coastguard Worker        expect(flm.baseline).toBeCloseTo(21.141, 3);
131*c8dee2aaSAndroid Build Coastguard Worker
132*c8dee2aaSAndroid Build Coastguard Worker        const singleLineMetrics = paragraph.getLineMetricsAt(0);
133*c8dee2aaSAndroid Build Coastguard Worker        expect(singleLineMetrics.startIndex).toEqual(flm.startIndex);
134*c8dee2aaSAndroid Build Coastguard Worker        expect(singleLineMetrics.lineNumber).toEqual(flm.lineNumber);
135*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getLineMetricsAt(9)).toBeFalsy();
136*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getNumberOfLines()).toEqual(8);
137*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getLineNumberAt(9999)).toEqual(-1);
138*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getLineNumberAt(0)).toEqual(0);
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker        const glyphInfo = paragraph.getGlyphInfoAt(13);
141*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.graphemeClusterTextRange.start).toEqual(13);
142*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.graphemeClusterTextRange.end).toEqual(14);
143*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.dir).toEqual(CanvasKit.TextDirection.LTR);
144*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.isEllipsis).toEqual(false);
145*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.graphemeLayoutBounds[0]).toBeCloseTo(172.08, 3);
146*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.graphemeLayoutBounds[1]).toBeCloseTo(-0.24, 3);
147*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.graphemeLayoutBounds[2]).toBeCloseTo(186.18, 3);
148*c8dee2aaSAndroid Build Coastguard Worker        expect(glyphInfo.graphemeLayoutBounds[3]).toBeCloseTo(27, 3);
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getGlyphInfoAt(9999)).toBeFalsy();
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker        // This should hit the last character on the first line.
153*c8dee2aaSAndroid Build Coastguard Worker        const lastGlyphOnFirstLine = paragraph.getClosestGlyphInfoAtCoordinate(180, 21);
154*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.graphemeClusterTextRange.start).toEqual(13);
155*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.graphemeClusterTextRange.end).toEqual(14);
156*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.dir).toEqual(CanvasKit.TextDirection.LTR);
157*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.isEllipsis).toEqual(false);
158*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.graphemeLayoutBounds[0]).toBeCloseTo(172.08, 3);
159*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.graphemeLayoutBounds[1]).toBeCloseTo(-0.24, 3);
160*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.graphemeLayoutBounds[2]).toBeCloseTo(186.18, 3);
161*c8dee2aaSAndroid Build Coastguard Worker        expect(lastGlyphOnFirstLine.graphemeLayoutBounds[3]).toBeCloseTo(27, 3);
162*c8dee2aaSAndroid Build Coastguard Worker
163*c8dee2aaSAndroid Build Coastguard Worker        const unresolvedGlyphs = paragraph.unresolvedCodepoints();
164*c8dee2aaSAndroid Build Coastguard Worker        expect(unresolvedGlyphs.length).toEqual(0, unresolvedGlyphs);
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
167*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
170*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
171*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
172*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
173*c8dee2aaSAndroid Build Coastguard Worker    });
174*c8dee2aaSAndroid Build Coastguard Worker
175*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_foreground_and_background_color', (canvas) => {
176*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
177*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
178*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
179*c8dee2aaSAndroid Build Coastguard Worker
180*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
181*c8dee2aaSAndroid Build Coastguard Worker
182*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
183*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
184*c8dee2aaSAndroid Build Coastguard Worker                foregroundColor: CanvasKit.Color4f(1.0, 0, 0, 0.8),
185*c8dee2aaSAndroid Build Coastguard Worker                backgroundColor: CanvasKit.Color4f(0, 0, 1.0, 0.8),
186*c8dee2aaSAndroid Build Coastguard Worker                // color should default to black
187*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
188*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
189*c8dee2aaSAndroid Build Coastguard Worker            },
190*c8dee2aaSAndroid Build Coastguard Worker
191*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
192*c8dee2aaSAndroid Build Coastguard Worker        });
193*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
194*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(
195*c8dee2aaSAndroid Build Coastguard Worker            'This text has a red foregroundColor and a blue backgroundColor.');
196*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
197*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
198*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
201*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
202*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
203*c8dee2aaSAndroid Build Coastguard Worker    });
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_foreground_stroke_paint', (canvas) => {
206*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
207*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
208*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
211*c8dee2aaSAndroid Build Coastguard Worker
212*c8dee2aaSAndroid Build Coastguard Worker        const textStyle = {
213*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Noto Serif'],
214*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 40,
215*c8dee2aaSAndroid Build Coastguard Worker        };
216*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
217*c8dee2aaSAndroid Build Coastguard Worker            textStyle: textStyle,
218*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
219*c8dee2aaSAndroid Build Coastguard Worker        });
220*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker        const fg = new CanvasKit.Paint();
223*c8dee2aaSAndroid Build Coastguard Worker        fg.setColor(CanvasKit.BLACK);
224*c8dee2aaSAndroid Build Coastguard Worker        fg.setStyle(CanvasKit.PaintStyle.Stroke);
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker        const bg = new CanvasKit.Paint();
227*c8dee2aaSAndroid Build Coastguard Worker        bg.setColor(CanvasKit.TRANSPARENT);
228*c8dee2aaSAndroid Build Coastguard Worker
229*c8dee2aaSAndroid Build Coastguard Worker        builder.pushPaintStyle(textStyle, fg, bg);
230*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(
231*c8dee2aaSAndroid Build Coastguard Worker            'This text is stroked in black and has no fill');
232*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
233*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
234*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
235*c8dee2aaSAndroid Build Coastguard Worker        // Again 5px to the right so you can tell the fill is transparent
236*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 15, 10);
237*c8dee2aaSAndroid Build Coastguard Worker
238*c8dee2aaSAndroid Build Coastguard Worker        fg.delete();
239*c8dee2aaSAndroid Build Coastguard Worker        bg.delete();
240*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
241*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
242*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
243*c8dee2aaSAndroid Build Coastguard Worker    });
244*c8dee2aaSAndroid Build Coastguard Worker
245*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_letter_word_spacing', (canvas) => {
246*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
247*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
248*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
249*c8dee2aaSAndroid Build Coastguard Worker
250*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
251*c8dee2aaSAndroid Build Coastguard Worker
252*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
253*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
254*c8dee2aaSAndroid Build Coastguard Worker                // color should default to black
255*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
256*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
257*c8dee2aaSAndroid Build Coastguard Worker                letterSpacing: 5,
258*c8dee2aaSAndroid Build Coastguard Worker                wordSpacing: 10,
259*c8dee2aaSAndroid Build Coastguard Worker            },
260*c8dee2aaSAndroid Build Coastguard Worker
261*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
262*c8dee2aaSAndroid Build Coastguard Worker        });
263*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
264*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(
265*c8dee2aaSAndroid Build Coastguard Worker            'This text should have a lot of space between the letters and words.');
266*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
267*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
268*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
271*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
272*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
273*c8dee2aaSAndroid Build Coastguard Worker    });
274*c8dee2aaSAndroid Build Coastguard Worker
275*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_shadows', (canvas) => {
276*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
277*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
278*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
279*c8dee2aaSAndroid Build Coastguard Worker
280*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
281*c8dee2aaSAndroid Build Coastguard Worker
282*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
283*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
284*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.WHITE,
285*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
286*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
287*c8dee2aaSAndroid Build Coastguard Worker                shadows: [{color: CanvasKit.BLACK, blurRadius: 15},
288*c8dee2aaSAndroid Build Coastguard Worker                          {color: CanvasKit.RED, blurRadius: 5, offset: [10, 10]}],
289*c8dee2aaSAndroid Build Coastguard Worker            },
290*c8dee2aaSAndroid Build Coastguard Worker
291*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
292*c8dee2aaSAndroid Build Coastguard Worker        });
293*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
294*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('This text should have a shadow behind it.');
295*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
296*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
297*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
298*c8dee2aaSAndroid Build Coastguard Worker
299*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
300*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
301*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
302*c8dee2aaSAndroid Build Coastguard Worker    });
303*c8dee2aaSAndroid Build Coastguard Worker
304*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_strut_style', (canvas) => {
305*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
306*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
307*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
308*c8dee2aaSAndroid Build Coastguard Worker
309*c8dee2aaSAndroid Build Coastguard Worker        // The lines in this paragraph should have the same height despite the third
310*c8dee2aaSAndroid Build Coastguard Worker        // line having a larger font size.
311*c8dee2aaSAndroid Build Coastguard Worker        const paraStrutStyle = new CanvasKit.ParagraphStyle({
312*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
313*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Roboto'],
314*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
315*c8dee2aaSAndroid Build Coastguard Worker            },
316*c8dee2aaSAndroid Build Coastguard Worker            strutStyle: {
317*c8dee2aaSAndroid Build Coastguard Worker                strutEnabled: true,
318*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Roboto'],
319*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 28,
320*c8dee2aaSAndroid Build Coastguard Worker                heightMultiplier: 1.5,
321*c8dee2aaSAndroid Build Coastguard Worker                forceStrutHeight: true,
322*c8dee2aaSAndroid Build Coastguard Worker            },
323*c8dee2aaSAndroid Build Coastguard Worker        });
324*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
325*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
326*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Roboto'],
327*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
328*c8dee2aaSAndroid Build Coastguard Worker            },
329*c8dee2aaSAndroid Build Coastguard Worker        });
330*c8dee2aaSAndroid Build Coastguard Worker        const roboto28Style = new CanvasKit.TextStyle({
331*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.BLACK,
332*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto'],
333*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 28,
334*c8dee2aaSAndroid Build Coastguard Worker        });
335*c8dee2aaSAndroid Build Coastguard Worker        const roboto32Style = new CanvasKit.TextStyle({
336*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.BLACK,
337*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto'],
338*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 32,
339*c8dee2aaSAndroid Build Coastguard Worker        });
340*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStrutStyle, fontMgr);
341*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(roboto28Style);
342*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('This paragraph\n');
343*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(roboto32Style);
344*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('is using\n');
345*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
346*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(roboto28Style);
347*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('a strut style!\n');
348*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
349*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
350*c8dee2aaSAndroid Build Coastguard Worker
351*c8dee2aaSAndroid Build Coastguard Worker        const builder2 = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
352*c8dee2aaSAndroid Build Coastguard Worker        builder2.pushStyle(roboto28Style);
353*c8dee2aaSAndroid Build Coastguard Worker        builder2.addText('This paragraph\n');
354*c8dee2aaSAndroid Build Coastguard Worker        builder2.pushStyle(roboto32Style);
355*c8dee2aaSAndroid Build Coastguard Worker        builder2.addText('is not using\n');
356*c8dee2aaSAndroid Build Coastguard Worker        builder2.pop();
357*c8dee2aaSAndroid Build Coastguard Worker        builder2.pushStyle(roboto28Style);
358*c8dee2aaSAndroid Build Coastguard Worker        builder2.addText('a strut style!\n');
359*c8dee2aaSAndroid Build Coastguard Worker        builder2.pop();
360*c8dee2aaSAndroid Build Coastguard Worker        builder2.pop();
361*c8dee2aaSAndroid Build Coastguard Worker
362*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
363*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
364*c8dee2aaSAndroid Build Coastguard Worker
365*c8dee2aaSAndroid Build Coastguard Worker        const paragraph2 = builder2.build();
366*c8dee2aaSAndroid Build Coastguard Worker        paragraph2.layout(300);
367*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
368*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph2, 220, 10);
369*c8dee2aaSAndroid Build Coastguard Worker
370*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
371*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
372*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
373*c8dee2aaSAndroid Build Coastguard Worker    });
374*c8dee2aaSAndroid Build Coastguard Worker
375*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_font_features', (canvas) => {
376*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
377*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
378*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
379*c8dee2aaSAndroid Build Coastguard Worker
380*c8dee2aaSAndroid Build Coastguard Worker
381*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
382*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
383*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
384*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Roboto'],
385*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 30,
386*c8dee2aaSAndroid Build Coastguard Worker                fontFeatures: [{name: 'smcp', value: 1}]
387*c8dee2aaSAndroid Build Coastguard Worker            },
388*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
389*c8dee2aaSAndroid Build Coastguard Worker        });
390*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
391*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('This Text Should Be In Small Caps');
392*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
393*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
394*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
395*c8dee2aaSAndroid Build Coastguard Worker
396*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
397*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
398*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
399*c8dee2aaSAndroid Build Coastguard Worker    });
400*c8dee2aaSAndroid Build Coastguard Worker
401*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_font_variations', (canvas) => {
402*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(robotoVariableFontBuffer);
403*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
404*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Roboto Slab');
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
407*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
408*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
409*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Roboto Slab'],
410*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 30,
411*c8dee2aaSAndroid Build Coastguard Worker            },
412*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
413*c8dee2aaSAndroid Build Coastguard Worker        });
414*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
415*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Normal\n');
416*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(new CanvasKit.TextStyle({
417*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto Slab'],
418*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 30,
419*c8dee2aaSAndroid Build Coastguard Worker            fontVariations: [{axis: 'wght', value: 900}]
420*c8dee2aaSAndroid Build Coastguard Worker        }));
421*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Heavy Weight\n');
422*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(new CanvasKit.TextStyle({
423*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto Slab'],
424*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 30,
425*c8dee2aaSAndroid Build Coastguard Worker            fontVariations: [{axis: 'wght', value: 100}]
426*c8dee2aaSAndroid Build Coastguard Worker        }));
427*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Light Weight\n');
428*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
429*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
430*c8dee2aaSAndroid Build Coastguard Worker
431*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
432*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
433*c8dee2aaSAndroid Build Coastguard Worker
434*c8dee2aaSAndroid Build Coastguard Worker        canvas.clear(CanvasKit.WHITE);
435*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
436*c8dee2aaSAndroid Build Coastguard Worker
437*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
438*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
439*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
440*c8dee2aaSAndroid Build Coastguard Worker    });
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_placeholders', (canvas) => {
443*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
444*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
445*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
446*c8dee2aaSAndroid Build Coastguard Worker
447*c8dee2aaSAndroid Build Coastguard Worker
448*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
449*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
450*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
451*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Roboto'],
452*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
453*c8dee2aaSAndroid Build Coastguard Worker            },
454*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Center,
455*c8dee2aaSAndroid Build Coastguard Worker        });
456*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
457*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('There should be ');
458*c8dee2aaSAndroid Build Coastguard Worker        builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.AboveBaseline,
459*c8dee2aaSAndroid Build Coastguard Worker                               CanvasKit.TextBaseline.Ideographic);
460*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('a space in this sentence.\n');
461*c8dee2aaSAndroid Build Coastguard Worker
462*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('There should be ');
463*c8dee2aaSAndroid Build Coastguard Worker        builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.BelowBaseline,
464*c8dee2aaSAndroid Build Coastguard Worker                               CanvasKit.TextBaseline.Ideographic);
465*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('a dropped space in this sentence.\n');
466*c8dee2aaSAndroid Build Coastguard Worker
467*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('There should be ');
468*c8dee2aaSAndroid Build Coastguard Worker        builder.addPlaceholder(10, 10, null, null, 20);
469*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('an offset space in this sentence.\n');
470*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
471*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(300);
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker        let rects = paragraph.getRectsForPlaceholders();
474*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
475*c8dee2aaSAndroid Build Coastguard Worker
476*c8dee2aaSAndroid Build Coastguard Worker        for (const r of rects) {
477*c8dee2aaSAndroid Build Coastguard Worker            const p = new CanvasKit.Paint();
478*c8dee2aaSAndroid Build Coastguard Worker            p.setColor(CanvasKit.Color(0, 0, 255));
479*c8dee2aaSAndroid Build Coastguard Worker            p.setStyle(CanvasKit.PaintStyle.Stroke);
480*c8dee2aaSAndroid Build Coastguard Worker            // Account for the (10, 10) offset when we painted the paragraph.
481*c8dee2aaSAndroid Build Coastguard Worker            const rect = r.rect;
482*c8dee2aaSAndroid Build Coastguard Worker            expect(r.dir).toEqual(CanvasKit.TextDirection.LTR);
483*c8dee2aaSAndroid Build Coastguard Worker            const placeholder =
484*c8dee2aaSAndroid Build Coastguard Worker                CanvasKit.LTRBRect(rect[0]+10,rect[1]+10,rect[2]+10,rect[3]+10);
485*c8dee2aaSAndroid Build Coastguard Worker            canvas.drawRect(placeholder, p);
486*c8dee2aaSAndroid Build Coastguard Worker            p.delete();
487*c8dee2aaSAndroid Build Coastguard Worker        }
488*c8dee2aaSAndroid Build Coastguard Worker
489*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
490*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
491*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
492*c8dee2aaSAndroid Build Coastguard Worker    });
493*c8dee2aaSAndroid Build Coastguard Worker
494*c8dee2aaSAndroid Build Coastguard Worker    // loosely based on SkParagraph_GetRectsForRangeParagraph test in c++ code.
495*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_rects', (canvas) => {
496*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
497*c8dee2aaSAndroid Build Coastguard Worker
498*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 550;
499*c8dee2aaSAndroid Build Coastguard Worker        const hStyle = CanvasKit.RectHeightStyle.Max;
500*c8dee2aaSAndroid Build Coastguard Worker        const wStyle = CanvasKit.RectWidthStyle.Tight;
501*c8dee2aaSAndroid Build Coastguard Worker
502*c8dee2aaSAndroid Build Coastguard Worker        const mallocedColor = CanvasKit.Malloc(Float32Array, 4);
503*c8dee2aaSAndroid Build Coastguard Worker        mallocedColor.toTypedArray().set([0.9, 0.1, 0.1, 1.0]);
504*c8dee2aaSAndroid Build Coastguard Worker
505*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
506*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
507*c8dee2aaSAndroid Build Coastguard Worker                color: mallocedColor,
508*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
509*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 50,
510*c8dee2aaSAndroid Build Coastguard Worker            },
511*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Left,
512*c8dee2aaSAndroid Build Coastguard Worker            maxLines: 10,
513*c8dee2aaSAndroid Build Coastguard Worker        });
514*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
515*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('12345,  \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345');
516*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
517*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.Free(mallocedColor);
518*c8dee2aaSAndroid Build Coastguard Worker
519*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
520*c8dee2aaSAndroid Build Coastguard Worker
521*c8dee2aaSAndroid Build Coastguard Worker        const ranges = [
522*c8dee2aaSAndroid Build Coastguard Worker            {
523*c8dee2aaSAndroid Build Coastguard Worker                start: 0,
524*c8dee2aaSAndroid Build Coastguard Worker                end: 0,
525*c8dee2aaSAndroid Build Coastguard Worker                expectedNum: 0,
526*c8dee2aaSAndroid Build Coastguard Worker            },
527*c8dee2aaSAndroid Build Coastguard Worker            {
528*c8dee2aaSAndroid Build Coastguard Worker                start: 0,
529*c8dee2aaSAndroid Build Coastguard Worker                end: 1,
530*c8dee2aaSAndroid Build Coastguard Worker                expectedNum: 1,
531*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.Color(200, 0, 200),
532*c8dee2aaSAndroid Build Coastguard Worker            },
533*c8dee2aaSAndroid Build Coastguard Worker            {
534*c8dee2aaSAndroid Build Coastguard Worker                start: 2,
535*c8dee2aaSAndroid Build Coastguard Worker                end: 8,
536*c8dee2aaSAndroid Build Coastguard Worker                expectedNum: 1,
537*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.Color(255, 0, 0),
538*c8dee2aaSAndroid Build Coastguard Worker            },
539*c8dee2aaSAndroid Build Coastguard Worker            {
540*c8dee2aaSAndroid Build Coastguard Worker                start: 8,
541*c8dee2aaSAndroid Build Coastguard Worker                end: 21,
542*c8dee2aaSAndroid Build Coastguard Worker                expectedNum: 1,
543*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.Color(0, 255, 0),
544*c8dee2aaSAndroid Build Coastguard Worker            },
545*c8dee2aaSAndroid Build Coastguard Worker            {
546*c8dee2aaSAndroid Build Coastguard Worker                start: 30,
547*c8dee2aaSAndroid Build Coastguard Worker                end: 100,
548*c8dee2aaSAndroid Build Coastguard Worker                expectedNum: 4,
549*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.Color(0, 0, 255),
550*c8dee2aaSAndroid Build Coastguard Worker            },
551*c8dee2aaSAndroid Build Coastguard Worker            {
552*c8dee2aaSAndroid Build Coastguard Worker                start: 19,
553*c8dee2aaSAndroid Build Coastguard Worker                end: 22,
554*c8dee2aaSAndroid Build Coastguard Worker                expectedNum: 1,
555*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.Color(0, 200, 200),
556*c8dee2aaSAndroid Build Coastguard Worker            }
557*c8dee2aaSAndroid Build Coastguard Worker        ];
558*c8dee2aaSAndroid Build Coastguard Worker        // Move it down a bit so we can see the rects that go above 0,0
559*c8dee2aaSAndroid Build Coastguard Worker        canvas.translate(10, 10);
560*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 0, 0);
561*c8dee2aaSAndroid Build Coastguard Worker
562*c8dee2aaSAndroid Build Coastguard Worker        for (const test of ranges) {
563*c8dee2aaSAndroid Build Coastguard Worker            let rects = paragraph.getRectsForRange(test.start, test.end, hStyle, wStyle);
564*c8dee2aaSAndroid Build Coastguard Worker            expect(Array.isArray(rects)).toEqual(true);
565*c8dee2aaSAndroid Build Coastguard Worker            expect(rects.length).toEqual(test.expectedNum);
566*c8dee2aaSAndroid Build Coastguard Worker
567*c8dee2aaSAndroid Build Coastguard Worker            for (const r of rects) {
568*c8dee2aaSAndroid Build Coastguard Worker                expect(r.dir).toEqual(CanvasKit.TextDirection.LTR);
569*c8dee2aaSAndroid Build Coastguard Worker                const p = new CanvasKit.Paint();
570*c8dee2aaSAndroid Build Coastguard Worker                p.setColor(test.color);
571*c8dee2aaSAndroid Build Coastguard Worker                p.setStyle(CanvasKit.PaintStyle.Stroke);
572*c8dee2aaSAndroid Build Coastguard Worker                canvas.drawRect(r.rect, p);
573*c8dee2aaSAndroid Build Coastguard Worker                p.delete();
574*c8dee2aaSAndroid Build Coastguard Worker            }
575*c8dee2aaSAndroid Build Coastguard Worker        }
576*c8dee2aaSAndroid Build Coastguard Worker        expect(CanvasKit.RectHeightStyle.Strut).toBeTruthy();
577*c8dee2aaSAndroid Build Coastguard Worker
578*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
579*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
580*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
581*c8dee2aaSAndroid Build Coastguard Worker    });
582*c8dee2aaSAndroid Build Coastguard Worker
583*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_emoji', (canvas) => {
584*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer, emojiFontBuffer]);
585*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(2);
586*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
587*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(1)).toEqual('Noto Color Emoji');
588*c8dee2aaSAndroid Build Coastguard Worker
589*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 450;
590*c8dee2aaSAndroid Build Coastguard Worker
591*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
592*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
593*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
594*c8dee2aaSAndroid Build Coastguard Worker                // Put text first, otherwise the "emoji space" is used and that looks bad.
595*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif', 'Noto Color Emoji'],
596*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 30,
597*c8dee2aaSAndroid Build Coastguard Worker            },
598*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Left,
599*c8dee2aaSAndroid Build Coastguard Worker            maxLines: 10,
600*c8dee2aaSAndroid Build Coastguard Worker        });
601*c8dee2aaSAndroid Build Coastguard Worker
602*c8dee2aaSAndroid Build Coastguard Worker        const textStyle = new CanvasKit.TextStyle({
603*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.BLACK,
604*c8dee2aaSAndroid Build Coastguard Worker            // The number 4 matches an emoji and looks strange w/o this additional style.
605*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Noto Serif'],
606*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 30,
607*c8dee2aaSAndroid Build Coastguard Worker        });
608*c8dee2aaSAndroid Build Coastguard Worker
609*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
610*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(textStyle);
611*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('4 flags on following line:\n');
612*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
613*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`��️‍�� ���� ���� ����\n`);
614*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Rainbow Italy Liberia USA\n\n');
615*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Emoji below should wrap:\n');
616*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`����������������‍��‍����‍��‍��‍����‍��‍����‍��‍��‍����‍��‍����‍��‍��‍����‍��‍����‍��‍��‍����‍��‍����‍��‍��‍����‍��‍����‍��‍��‍����‍��‍����‍��‍��‍��`);
617*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
618*c8dee2aaSAndroid Build Coastguard Worker
619*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
620*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
621*c8dee2aaSAndroid Build Coastguard Worker
622*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
623*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
624*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
625*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
626*c8dee2aaSAndroid Build Coastguard Worker
627*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
628*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
629*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
630*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
631*c8dee2aaSAndroid Build Coastguard Worker    });
632*c8dee2aaSAndroid Build Coastguard Worker
633*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_hits', (canvas) => {
634*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer]);
635*c8dee2aaSAndroid Build Coastguard Worker
636*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 300;
637*c8dee2aaSAndroid Build Coastguard Worker
638*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
639*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
640*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
641*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
642*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 50,
643*c8dee2aaSAndroid Build Coastguard Worker            },
644*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Left,
645*c8dee2aaSAndroid Build Coastguard Worker            maxLines: 10,
646*c8dee2aaSAndroid Build Coastguard Worker        });
647*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
648*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('UNCOPYRIGHTABLE');
649*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
650*c8dee2aaSAndroid Build Coastguard Worker
651*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
652*c8dee2aaSAndroid Build Coastguard Worker        canvas.translate(10, 10);
653*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 0, 0);
654*c8dee2aaSAndroid Build Coastguard Worker
655*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
656*c8dee2aaSAndroid Build Coastguard Worker
657*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.Color(255, 0, 0));
658*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Fill);
659*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawCircle(20, 30, 3, paint);
660*c8dee2aaSAndroid Build Coastguard Worker
661*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.Color(0, 0, 255));
662*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawCircle(80, 90, 3, paint);
663*c8dee2aaSAndroid Build Coastguard Worker
664*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.Color(0, 255, 0));
665*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawCircle(280, 2, 3, paint);
666*c8dee2aaSAndroid Build Coastguard Worker
667*c8dee2aaSAndroid Build Coastguard Worker        let posU = paragraph.getGlyphPositionAtCoordinate(20, 30);
668*c8dee2aaSAndroid Build Coastguard Worker        expect(posU).toEqual({
669*c8dee2aaSAndroid Build Coastguard Worker            pos: 1,
670*c8dee2aaSAndroid Build Coastguard Worker            affinity: CanvasKit.Affinity.Upstream
671*c8dee2aaSAndroid Build Coastguard Worker        });
672*c8dee2aaSAndroid Build Coastguard Worker        let posA = paragraph.getGlyphPositionAtCoordinate(80, 90);
673*c8dee2aaSAndroid Build Coastguard Worker        expect(posA).toEqual({
674*c8dee2aaSAndroid Build Coastguard Worker            pos: 11,
675*c8dee2aaSAndroid Build Coastguard Worker            affinity: CanvasKit.Affinity.Downstream
676*c8dee2aaSAndroid Build Coastguard Worker        });
677*c8dee2aaSAndroid Build Coastguard Worker        let posG = paragraph.getGlyphPositionAtCoordinate(280, 2);
678*c8dee2aaSAndroid Build Coastguard Worker        expect(posG).toEqual({
679*c8dee2aaSAndroid Build Coastguard Worker            pos: 9,
680*c8dee2aaSAndroid Build Coastguard Worker            affinity: CanvasKit.Affinity.Upstream
681*c8dee2aaSAndroid Build Coastguard Worker        });
682*c8dee2aaSAndroid Build Coastguard Worker
683*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
684*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
685*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
686*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
687*c8dee2aaSAndroid Build Coastguard Worker    });
688*c8dee2aaSAndroid Build Coastguard Worker
689*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_styles', (canvas) => {
690*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
691*c8dee2aaSAndroid Build Coastguard Worker
692*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
693*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
694*c8dee2aaSAndroid Build Coastguard Worker
695*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
696*c8dee2aaSAndroid Build Coastguard Worker
697*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
698*c8dee2aaSAndroid Build Coastguard Worker
699*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
700*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
701*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
702*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
703*c8dee2aaSAndroid Build Coastguard Worker                fontStyle: {
704*c8dee2aaSAndroid Build Coastguard Worker                    weight: CanvasKit.FontWeight.Light,
705*c8dee2aaSAndroid Build Coastguard Worker                }
706*c8dee2aaSAndroid Build Coastguard Worker            },
707*c8dee2aaSAndroid Build Coastguard Worker            textDirection: CanvasKit.TextDirection.RTL,
708*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
709*c8dee2aaSAndroid Build Coastguard Worker        });
710*c8dee2aaSAndroid Build Coastguard Worker
711*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
712*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Default text\n');
713*c8dee2aaSAndroid Build Coastguard Worker
714*c8dee2aaSAndroid Build Coastguard Worker        const boldItalic = new CanvasKit.TextStyle({
715*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.RED,
716*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Noto Serif'],
717*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 20,
718*c8dee2aaSAndroid Build Coastguard Worker            fontStyle: {
719*c8dee2aaSAndroid Build Coastguard Worker                weight: CanvasKit.FontWeight.Bold,
720*c8dee2aaSAndroid Build Coastguard Worker                width: CanvasKit.FontWidth.Expanded,
721*c8dee2aaSAndroid Build Coastguard Worker                slant: CanvasKit.FontSlant.Italic,
722*c8dee2aaSAndroid Build Coastguard Worker            }
723*c8dee2aaSAndroid Build Coastguard Worker        });
724*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
725*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Bold, Expanded, Italic\n`);
726*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
727*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`back to normal`);
728*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
729*c8dee2aaSAndroid Build Coastguard Worker
730*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
731*c8dee2aaSAndroid Build Coastguard Worker
732*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
733*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
734*c8dee2aaSAndroid Build Coastguard Worker
735*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
736*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
737*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
738*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
739*c8dee2aaSAndroid Build Coastguard Worker    });
740*c8dee2aaSAndroid Build Coastguard Worker
741*c8dee2aaSAndroid Build Coastguard Worker    it('paragraph_rounding_hack', () => {
742*c8dee2aaSAndroid Build Coastguard Worker        const paraStyleDefault = new CanvasKit.ParagraphStyle({
743*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
744*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
745*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
746*c8dee2aaSAndroid Build Coastguard Worker                fontStyle: {
747*c8dee2aaSAndroid Build Coastguard Worker                    weight: CanvasKit.FontWeight.Light,
748*c8dee2aaSAndroid Build Coastguard Worker                }
749*c8dee2aaSAndroid Build Coastguard Worker            },
750*c8dee2aaSAndroid Build Coastguard Worker            textDirection: CanvasKit.TextDirection.RTL,
751*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
752*c8dee2aaSAndroid Build Coastguard Worker        });
753*c8dee2aaSAndroid Build Coastguard Worker        expect(paraStyleDefault.applyRoundingHack).toEqual(true);
754*c8dee2aaSAndroid Build Coastguard Worker
755*c8dee2aaSAndroid Build Coastguard Worker        const paraStyleOverride = new CanvasKit.ParagraphStyle({
756*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
757*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
758*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
759*c8dee2aaSAndroid Build Coastguard Worker                fontStyle: {
760*c8dee2aaSAndroid Build Coastguard Worker                    weight: CanvasKit.FontWeight.Light,
761*c8dee2aaSAndroid Build Coastguard Worker                }
762*c8dee2aaSAndroid Build Coastguard Worker            },
763*c8dee2aaSAndroid Build Coastguard Worker            textDirection: CanvasKit.TextDirection.RTL,
764*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
765*c8dee2aaSAndroid Build Coastguard Worker            applyRoundingHack: false,
766*c8dee2aaSAndroid Build Coastguard Worker        });
767*c8dee2aaSAndroid Build Coastguard Worker        expect(paraStyleOverride.applyRoundingHack).toEqual(false);
768*c8dee2aaSAndroid Build Coastguard Worker    });
769*c8dee2aaSAndroid Build Coastguard Worker
770*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_font_provider', (canvas) => {
771*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
772*c8dee2aaSAndroid Build Coastguard Worker
773*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
774*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
775*c8dee2aaSAndroid Build Coastguard Worker
776*c8dee2aaSAndroid Build Coastguard Worker        // Register Noto Serif as 'sans-serif'.
777*c8dee2aaSAndroid Build Coastguard Worker        const fontSrc = CanvasKit.TypefaceFontProvider.Make();
778*c8dee2aaSAndroid Build Coastguard Worker        fontSrc.registerFont(notoSerifFontBuffer, 'sans-serif');
779*c8dee2aaSAndroid Build Coastguard Worker        fontSrc.registerFont(notoSerifBoldItalicFontBuffer, 'sans-serif');
780*c8dee2aaSAndroid Build Coastguard Worker
781*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
782*c8dee2aaSAndroid Build Coastguard Worker
783*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
784*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
785*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['sans-serif'],
786*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
787*c8dee2aaSAndroid Build Coastguard Worker                fontStyle: {
788*c8dee2aaSAndroid Build Coastguard Worker                    weight: CanvasKit.FontWeight.Light,
789*c8dee2aaSAndroid Build Coastguard Worker                }
790*c8dee2aaSAndroid Build Coastguard Worker            },
791*c8dee2aaSAndroid Build Coastguard Worker            textDirection: CanvasKit.TextDirection.RTL,
792*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
793*c8dee2aaSAndroid Build Coastguard Worker        });
794*c8dee2aaSAndroid Build Coastguard Worker
795*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.MakeFromFontProvider(paraStyle, fontSrc);
796*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Default text\n');
797*c8dee2aaSAndroid Build Coastguard Worker
798*c8dee2aaSAndroid Build Coastguard Worker        const boldItalic = new CanvasKit.TextStyle({
799*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.RED,
800*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['sans-serif'],
801*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 20,
802*c8dee2aaSAndroid Build Coastguard Worker            fontStyle: {
803*c8dee2aaSAndroid Build Coastguard Worker                weight: CanvasKit.FontWeight.Bold,
804*c8dee2aaSAndroid Build Coastguard Worker                width: CanvasKit.FontWidth.Expanded,
805*c8dee2aaSAndroid Build Coastguard Worker                slant: CanvasKit.FontSlant.Italic,
806*c8dee2aaSAndroid Build Coastguard Worker            }
807*c8dee2aaSAndroid Build Coastguard Worker        });
808*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
809*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Bold, Expanded, Italic\n`);
810*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
811*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`back to normal`);
812*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
813*c8dee2aaSAndroid Build Coastguard Worker
814*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
815*c8dee2aaSAndroid Build Coastguard Worker
816*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
817*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
818*c8dee2aaSAndroid Build Coastguard Worker
819*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
820*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
821*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
822*c8dee2aaSAndroid Build Coastguard Worker        fontSrc.delete();
823*c8dee2aaSAndroid Build Coastguard Worker    });
824*c8dee2aaSAndroid Build Coastguard Worker
825*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_font_collection', (canvas) => {
826*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
827*c8dee2aaSAndroid Build Coastguard Worker
828*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
829*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
830*c8dee2aaSAndroid Build Coastguard Worker
831*c8dee2aaSAndroid Build Coastguard Worker        // Register Noto Serif as 'sans-serif'.
832*c8dee2aaSAndroid Build Coastguard Worker        const fontSrc = CanvasKit.TypefaceFontProvider.Make();
833*c8dee2aaSAndroid Build Coastguard Worker        fontSrc.registerFont(notoSerifFontBuffer, 'sans-serif');
834*c8dee2aaSAndroid Build Coastguard Worker        const fontCollection = CanvasKit.FontCollection.Make();
835*c8dee2aaSAndroid Build Coastguard Worker        fontCollection.setDefaultFontManager(fontSrc);
836*c8dee2aaSAndroid Build Coastguard Worker
837*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
838*c8dee2aaSAndroid Build Coastguard Worker
839*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
840*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
841*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['sans-serif'],
842*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
843*c8dee2aaSAndroid Build Coastguard Worker            },
844*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
845*c8dee2aaSAndroid Build Coastguard Worker        });
846*c8dee2aaSAndroid Build Coastguard Worker
847*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.MakeFromFontCollection(
848*c8dee2aaSAndroid Build Coastguard Worker	    paraStyle, fontCollection);
849*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('ABC DEF GHI');
850*c8dee2aaSAndroid Build Coastguard Worker
851*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
852*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
853*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
854*c8dee2aaSAndroid Build Coastguard Worker
855*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
856*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
857*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
858*c8dee2aaSAndroid Build Coastguard Worker        fontSrc.delete();
859*c8dee2aaSAndroid Build Coastguard Worker    });
860*c8dee2aaSAndroid Build Coastguard Worker
861*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_text_styles', (canvas) => {
862*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
863*c8dee2aaSAndroid Build Coastguard Worker
864*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.GREEN);
865*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
866*c8dee2aaSAndroid Build Coastguard Worker
867*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
868*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
869*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
870*c8dee2aaSAndroid Build Coastguard Worker
871*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
872*c8dee2aaSAndroid Build Coastguard Worker
873*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
874*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
875*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
876*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
877*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
878*c8dee2aaSAndroid Build Coastguard Worker                decoration: CanvasKit.UnderlineDecoration,
879*c8dee2aaSAndroid Build Coastguard Worker                decorationThickness: 1.5, // multiplier based on font size
880*c8dee2aaSAndroid Build Coastguard Worker                decorationStyle: CanvasKit.DecorationStyle.Wavy,
881*c8dee2aaSAndroid Build Coastguard Worker            },
882*c8dee2aaSAndroid Build Coastguard Worker        });
883*c8dee2aaSAndroid Build Coastguard Worker
884*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
885*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
886*c8dee2aaSAndroid Build Coastguard Worker
887*c8dee2aaSAndroid Build Coastguard Worker        const blueText = new CanvasKit.TextStyle({
888*c8dee2aaSAndroid Build Coastguard Worker            backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
889*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.Color(48, 37, 199),
890*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Noto Serif'],
891*c8dee2aaSAndroid Build Coastguard Worker            textBaseline: CanvasKit.TextBaseline.Ideographic,
892*c8dee2aaSAndroid Build Coastguard Worker            decoration: CanvasKit.LineThroughDecoration,
893*c8dee2aaSAndroid Build Coastguard Worker            decorationThickness: 1.5, // multiplier based on font size
894*c8dee2aaSAndroid Build Coastguard Worker        });
895*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(blueText);
896*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`);
897*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
898*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(` I'm done with the blue now. `);
899*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Now I hope we should stop before we get 8 lines tall. `);
900*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
901*c8dee2aaSAndroid Build Coastguard Worker
902*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
903*c8dee2aaSAndroid Build Coastguard Worker
904*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3);
905*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getHeight()).toEqual(227);
906*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3);
907*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getLongestLine()).toBeCloseTo(195.664, 3);
908*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1167.140, 3);
909*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getMaxWidth()).toEqual(200);
910*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3);
911*c8dee2aaSAndroid Build Coastguard Worker        // Check "VAVAVAVAVAVAVA"
912*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getWordBoundary(8)).toEqual({
913*c8dee2aaSAndroid Build Coastguard Worker            start: 0,
914*c8dee2aaSAndroid Build Coastguard Worker            end: 14,
915*c8dee2aaSAndroid Build Coastguard Worker        });
916*c8dee2aaSAndroid Build Coastguard Worker        // Check "I"
917*c8dee2aaSAndroid Build Coastguard Worker        expect(paragraph.getWordBoundary(25)).toEqual({
918*c8dee2aaSAndroid Build Coastguard Worker            start: 25,
919*c8dee2aaSAndroid Build Coastguard Worker            end: 26,
920*c8dee2aaSAndroid Build Coastguard Worker        });
921*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
922*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
923*c8dee2aaSAndroid Build Coastguard Worker
924*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
925*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
926*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
927*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
928*c8dee2aaSAndroid Build Coastguard Worker    });
929*c8dee2aaSAndroid Build Coastguard Worker
930*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_text_styles_mixed_leading_distribution', (canvas) => {
931*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
932*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
933*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
934*c8dee2aaSAndroid Build Coastguard Worker
935*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 200;
936*c8dee2aaSAndroid Build Coastguard Worker
937*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
938*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
939*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
940*c8dee2aaSAndroid Build Coastguard Worker                backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
941*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
942*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 10,
943*c8dee2aaSAndroid Build Coastguard Worker                heightMultiplier: 10,
944*c8dee2aaSAndroid Build Coastguard Worker            },
945*c8dee2aaSAndroid Build Coastguard Worker        });
946*c8dee2aaSAndroid Build Coastguard Worker
947*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
948*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Not half leading');
949*c8dee2aaSAndroid Build Coastguard Worker
950*c8dee2aaSAndroid Build Coastguard Worker        const halfLeadingText = new CanvasKit.TextStyle({
951*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.Color(48, 37, 199),
952*c8dee2aaSAndroid Build Coastguard Worker            backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
953*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Noto Serif'],
954*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 10,
955*c8dee2aaSAndroid Build Coastguard Worker            heightMultiplier: 10,
956*c8dee2aaSAndroid Build Coastguard Worker            halfLeading: true,
957*c8dee2aaSAndroid Build Coastguard Worker        });
958*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(halfLeadingText);
959*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Half Leading Text');
960*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
961*c8dee2aaSAndroid Build Coastguard Worker
962*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
963*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 0, 0);
964*c8dee2aaSAndroid Build Coastguard Worker
965*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
966*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
967*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
968*c8dee2aaSAndroid Build Coastguard Worker    });
969*c8dee2aaSAndroid Build Coastguard Worker
970*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_mixed_text_height_behavior', (canvas) => {
971*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
972*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.countFamilies()).toEqual(1);
973*c8dee2aaSAndroid Build Coastguard Worker        expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
974*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
975*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
976*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
977*c8dee2aaSAndroid Build Coastguard Worker
978*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 220;
979*c8dee2aaSAndroid Build Coastguard Worker        const behaviors = ["All", "DisableFirstAscent", "DisableLastDescent", "DisableAll"];
980*c8dee2aaSAndroid Build Coastguard Worker
981*c8dee2aaSAndroid Build Coastguard Worker        for (let i = 0; i < behaviors.length; i++) {
982*c8dee2aaSAndroid Build Coastguard Worker            const style = new CanvasKit.ParagraphStyle({
983*c8dee2aaSAndroid Build Coastguard Worker                textStyle: {
984*c8dee2aaSAndroid Build Coastguard Worker                    color: CanvasKit.BLACK,
985*c8dee2aaSAndroid Build Coastguard Worker                    fontFamilies: ['Noto Serif'],
986*c8dee2aaSAndroid Build Coastguard Worker                    fontSize: 20,
987*c8dee2aaSAndroid Build Coastguard Worker                    heightMultiplier: 3, // make the difference more obvious
988*c8dee2aaSAndroid Build Coastguard Worker                },
989*c8dee2aaSAndroid Build Coastguard Worker                textHeightBehavior: CanvasKit.TextHeightBehavior[behaviors[i]],
990*c8dee2aaSAndroid Build Coastguard Worker            });
991*c8dee2aaSAndroid Build Coastguard Worker            const builder = CanvasKit.ParagraphBuilder.Make(style, fontMgr);
992*c8dee2aaSAndroid Build Coastguard Worker            builder.addText('Text height behavior\nof '+behaviors[i]);
993*c8dee2aaSAndroid Build Coastguard Worker            const paragraph = builder.build();
994*c8dee2aaSAndroid Build Coastguard Worker            paragraph.layout(wrapTo);
995*c8dee2aaSAndroid Build Coastguard Worker            canvas.drawParagraph(paragraph, 0, 150 * i);
996*c8dee2aaSAndroid Build Coastguard Worker            canvas.drawRect(CanvasKit.LTRBRect(0, 150 * i, wrapTo, 150 * i + 120), paint);
997*c8dee2aaSAndroid Build Coastguard Worker            paragraph.delete();
998*c8dee2aaSAndroid Build Coastguard Worker            builder.delete();
999*c8dee2aaSAndroid Build Coastguard Worker        }
1000*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
1001*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1002*c8dee2aaSAndroid Build Coastguard Worker    });
1003*c8dee2aaSAndroid Build Coastguard Worker
1004*c8dee2aaSAndroid Build Coastguard Worker    it('should not crash if we omit font family on pushed textStyle', () => {
1005*c8dee2aaSAndroid Build Coastguard Worker        const surface = CanvasKit.MakeCanvasSurface('test');
1006*c8dee2aaSAndroid Build Coastguard Worker        expect(surface).toBeTruthy('Could not make surface');
1007*c8dee2aaSAndroid Build Coastguard Worker
1008*c8dee2aaSAndroid Build Coastguard Worker        const canvas = surface.getCanvas();
1009*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
1010*c8dee2aaSAndroid Build Coastguard Worker
1011*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
1012*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
1013*c8dee2aaSAndroid Build Coastguard Worker
1014*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
1015*c8dee2aaSAndroid Build Coastguard Worker
1016*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
1017*c8dee2aaSAndroid Build Coastguard Worker
1018*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
1019*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
1020*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
1021*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
1022*c8dee2aaSAndroid Build Coastguard Worker            },
1023*c8dee2aaSAndroid Build Coastguard Worker            textDirection: CanvasKit.TextDirection.RTL,
1024*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
1025*c8dee2aaSAndroid Build Coastguard Worker        });
1026*c8dee2aaSAndroid Build Coastguard Worker
1027*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1028*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Default text\n');
1029*c8dee2aaSAndroid Build Coastguard Worker
1030*c8dee2aaSAndroid Build Coastguard Worker        const boldItalic = new CanvasKit.TextStyle({
1031*c8dee2aaSAndroid Build Coastguard Worker            fontStyle: {
1032*c8dee2aaSAndroid Build Coastguard Worker                weight: CanvasKit.FontWeight.Bold,
1033*c8dee2aaSAndroid Build Coastguard Worker                slant: CanvasKit.FontSlant.Italic,
1034*c8dee2aaSAndroid Build Coastguard Worker            }
1035*c8dee2aaSAndroid Build Coastguard Worker        });
1036*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
1037*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Bold, Italic\n`); // doesn't show up, but we don't crash
1038*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
1039*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`back to normal`);
1040*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
1041*c8dee2aaSAndroid Build Coastguard Worker
1042*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
1043*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
1044*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
1045*c8dee2aaSAndroid Build Coastguard Worker
1046*c8dee2aaSAndroid Build Coastguard Worker        surface.flush();
1047*c8dee2aaSAndroid Build Coastguard Worker
1048*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
1049*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
1050*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
1051*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1052*c8dee2aaSAndroid Build Coastguard Worker    });
1053*c8dee2aaSAndroid Build Coastguard Worker
1054*c8dee2aaSAndroid Build Coastguard Worker    it('should not crash if we omit font family on paragraph style', () => {
1055*c8dee2aaSAndroid Build Coastguard Worker        const surface = CanvasKit.MakeCanvasSurface('test');
1056*c8dee2aaSAndroid Build Coastguard Worker        expect(surface).toBeTruthy('Could not make surface');
1057*c8dee2aaSAndroid Build Coastguard Worker
1058*c8dee2aaSAndroid Build Coastguard Worker        const canvas = surface.getCanvas();
1059*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
1060*c8dee2aaSAndroid Build Coastguard Worker
1061*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.RED);
1062*c8dee2aaSAndroid Build Coastguard Worker        paint.setStyle(CanvasKit.PaintStyle.Stroke);
1063*c8dee2aaSAndroid Build Coastguard Worker
1064*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
1065*c8dee2aaSAndroid Build Coastguard Worker
1066*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
1067*c8dee2aaSAndroid Build Coastguard Worker
1068*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
1069*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
1070*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
1071*c8dee2aaSAndroid Build Coastguard Worker            },
1072*c8dee2aaSAndroid Build Coastguard Worker            textDirection: CanvasKit.TextDirection.RTL,
1073*c8dee2aaSAndroid Build Coastguard Worker            disableHinting: true,
1074*c8dee2aaSAndroid Build Coastguard Worker        });
1075*c8dee2aaSAndroid Build Coastguard Worker
1076*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1077*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Default text\n');
1078*c8dee2aaSAndroid Build Coastguard Worker
1079*c8dee2aaSAndroid Build Coastguard Worker        const boldItalic = new CanvasKit.TextStyle({
1080*c8dee2aaSAndroid Build Coastguard Worker            fontStyle: {
1081*c8dee2aaSAndroid Build Coastguard Worker                weight: CanvasKit.FontWeight.Bold,
1082*c8dee2aaSAndroid Build Coastguard Worker                slant: CanvasKit.FontSlant.Italic,
1083*c8dee2aaSAndroid Build Coastguard Worker            }
1084*c8dee2aaSAndroid Build Coastguard Worker        });
1085*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
1086*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Bold, Italic\n`);
1087*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
1088*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`back to normal`);
1089*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
1090*c8dee2aaSAndroid Build Coastguard Worker
1091*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
1092*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
1093*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
1094*c8dee2aaSAndroid Build Coastguard Worker
1095*c8dee2aaSAndroid Build Coastguard Worker        surface.flush();
1096*c8dee2aaSAndroid Build Coastguard Worker
1097*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
1098*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
1099*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1100*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
1101*c8dee2aaSAndroid Build Coastguard Worker    });
1102*c8dee2aaSAndroid Build Coastguard Worker
1103*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_builder_with_reset', (canvas) => {
1104*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
1105*c8dee2aaSAndroid Build Coastguard Worker
1106*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
1107*c8dee2aaSAndroid Build Coastguard Worker
1108*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
1109*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
1110*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
1111*c8dee2aaSAndroid Build Coastguard Worker            },
1112*c8dee2aaSAndroid Build Coastguard Worker        });
1113*c8dee2aaSAndroid Build Coastguard Worker
1114*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1115*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('Default text\n');
1116*c8dee2aaSAndroid Build Coastguard Worker
1117*c8dee2aaSAndroid Build Coastguard Worker        const boldItalic = new CanvasKit.TextStyle({
1118*c8dee2aaSAndroid Build Coastguard Worker            fontStyle: {
1119*c8dee2aaSAndroid Build Coastguard Worker                weight: CanvasKit.FontWeight.Bold,
1120*c8dee2aaSAndroid Build Coastguard Worker                slant: CanvasKit.FontSlant.Italic,
1121*c8dee2aaSAndroid Build Coastguard Worker            }
1122*c8dee2aaSAndroid Build Coastguard Worker        });
1123*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
1124*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Bold, Italic\n`);
1125*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
1126*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
1127*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
1128*c8dee2aaSAndroid Build Coastguard Worker
1129*c8dee2aaSAndroid Build Coastguard Worker        builder.reset();
1130*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('This builder has been reused\n');
1131*c8dee2aaSAndroid Build Coastguard Worker
1132*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
1133*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`2 Bold, Italic\n`);
1134*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
1135*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`2 back to normal`);
1136*c8dee2aaSAndroid Build Coastguard Worker        const paragraph2 = builder.build();
1137*c8dee2aaSAndroid Build Coastguard Worker        paragraph2.layout(wrapTo);
1138*c8dee2aaSAndroid Build Coastguard Worker
1139*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
1140*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph2, 10, 100);
1141*c8dee2aaSAndroid Build Coastguard Worker
1142*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
1143*c8dee2aaSAndroid Build Coastguard Worker        paragraph2.delete();
1144*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1145*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
1146*c8dee2aaSAndroid Build Coastguard Worker    });
1147*c8dee2aaSAndroid Build Coastguard Worker
1148*c8dee2aaSAndroid Build Coastguard Worker    // This helped find and resolve skbug.com/13247
1149*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_saved_to_skpicture', (canvas) => {
1150*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
1151*c8dee2aaSAndroid Build Coastguard Worker
1152*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
1153*c8dee2aaSAndroid Build Coastguard Worker
1154*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
1155*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
1156*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
1157*c8dee2aaSAndroid Build Coastguard Worker            },
1158*c8dee2aaSAndroid Build Coastguard Worker        });
1159*c8dee2aaSAndroid Build Coastguard Worker
1160*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1161*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('This was saved to an SkPicture\n');
1162*c8dee2aaSAndroid Build Coastguard Worker
1163*c8dee2aaSAndroid Build Coastguard Worker        const boldItalic = new CanvasKit.TextStyle({
1164*c8dee2aaSAndroid Build Coastguard Worker            fontStyle: {
1165*c8dee2aaSAndroid Build Coastguard Worker                weight: CanvasKit.FontWeight.Bold,
1166*c8dee2aaSAndroid Build Coastguard Worker                slant: CanvasKit.FontSlant.Italic,
1167*c8dee2aaSAndroid Build Coastguard Worker            }
1168*c8dee2aaSAndroid Build Coastguard Worker        });
1169*c8dee2aaSAndroid Build Coastguard Worker        builder.pushStyle(boldItalic);
1170*c8dee2aaSAndroid Build Coastguard Worker        builder.addText(`Bold, Italic\n`);
1171*c8dee2aaSAndroid Build Coastguard Worker        builder.pop();
1172*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
1173*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
1174*c8dee2aaSAndroid Build Coastguard Worker
1175*c8dee2aaSAndroid Build Coastguard Worker        const recorder = new CanvasKit.PictureRecorder();
1176*c8dee2aaSAndroid Build Coastguard Worker        const skpCanvas = recorder.beginRecording(CanvasKit.LTRBRect(0, 0, 200, 200));
1177*c8dee2aaSAndroid Build Coastguard Worker        skpCanvas.drawParagraph(paragraph, 10, 10);
1178*c8dee2aaSAndroid Build Coastguard Worker        const picture = recorder.finishRecordingAsPicture();
1179*c8dee2aaSAndroid Build Coastguard Worker
1180*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawPicture(CanvasKit.MakePicture(picture.serialize()));
1181*c8dee2aaSAndroid Build Coastguard Worker
1182*c8dee2aaSAndroid Build Coastguard Worker        picture.delete();
1183*c8dee2aaSAndroid Build Coastguard Worker        recorder.delete();
1184*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
1185*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1186*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
1187*c8dee2aaSAndroid Build Coastguard Worker    });
1188*c8dee2aaSAndroid Build Coastguard Worker
1189*c8dee2aaSAndroid Build Coastguard Worker    it('should replace tab characters', () => {
1190*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
1191*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
1192*c8dee2aaSAndroid Build Coastguard Worker
1193*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
1194*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
1195*c8dee2aaSAndroid Build Coastguard Worker                color: CanvasKit.BLACK,
1196*c8dee2aaSAndroid Build Coastguard Worker                fontFamilies: ['Noto Serif'],
1197*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 20,
1198*c8dee2aaSAndroid Build Coastguard Worker            },
1199*c8dee2aaSAndroid Build Coastguard Worker            textAlign: CanvasKit.TextAlign.Left,
1200*c8dee2aaSAndroid Build Coastguard Worker            replaceTabCharacters: true,
1201*c8dee2aaSAndroid Build Coastguard Worker        });
1202*c8dee2aaSAndroid Build Coastguard Worker
1203*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1204*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('1\t2');
1205*c8dee2aaSAndroid Build Coastguard Worker
1206*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
1207*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
1208*c8dee2aaSAndroid Build Coastguard Worker
1209*c8dee2aaSAndroid Build Coastguard Worker        const lines = paragraph.getShapedLines();
1210*c8dee2aaSAndroid Build Coastguard Worker
1211*c8dee2aaSAndroid Build Coastguard Worker        expect(lines.length).toEqual(1);
1212*c8dee2aaSAndroid Build Coastguard Worker        expect(lines[0].runs.length).toEqual(1);
1213*c8dee2aaSAndroid Build Coastguard Worker        expect(lines[0].runs[0].glyphs.length).toEqual(3);
1214*c8dee2aaSAndroid Build Coastguard Worker
1215*c8dee2aaSAndroid Build Coastguard Worker        // The tab should not be a missing glyph.
1216*c8dee2aaSAndroid Build Coastguard Worker        expect(lines[0].runs[0].glyphs[1]).not.toEqual(0);
1217*c8dee2aaSAndroid Build Coastguard Worker
1218*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
1219*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1220*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
1221*c8dee2aaSAndroid Build Coastguard Worker    });
1222*c8dee2aaSAndroid Build Coastguard Worker
1223*c8dee2aaSAndroid Build Coastguard Worker    gm('paragraph_fontSize_and_heightMultiplier_0', (canvas) => {
1224*c8dee2aaSAndroid Build Coastguard Worker        const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
1225*c8dee2aaSAndroid Build Coastguard Worker        const wrapTo = 250;
1226*c8dee2aaSAndroid Build Coastguard Worker        const paraStyle = new CanvasKit.ParagraphStyle({
1227*c8dee2aaSAndroid Build Coastguard Worker            textStyle: {
1228*c8dee2aaSAndroid Build Coastguard Worker                fontSize: 0,
1229*c8dee2aaSAndroid Build Coastguard Worker                heightMultiplier: 0,
1230*c8dee2aaSAndroid Build Coastguard Worker            },
1231*c8dee2aaSAndroid Build Coastguard Worker        });
1232*c8dee2aaSAndroid Build Coastguard Worker
1233*c8dee2aaSAndroid Build Coastguard Worker        const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1234*c8dee2aaSAndroid Build Coastguard Worker        builder.addText('This should not be visible');
1235*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
1236*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(wrapTo);
1237*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(paragraph, 10, 10);
1238*c8dee2aaSAndroid Build Coastguard Worker
1239*c8dee2aaSAndroid Build Coastguard Worker        let rects = paragraph.getRectsForRange(0, 1, CanvasKit.RectHeightStyle.Tight, CanvasKit.RectWidthStyle.Tight);
1240*c8dee2aaSAndroid Build Coastguard Worker        const paint = new CanvasKit.Paint();
1241*c8dee2aaSAndroid Build Coastguard Worker        paint.setColor(CanvasKit.Color(255, 0, 0));
1242*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(rects[0].rect, paint);
1243*c8dee2aaSAndroid Build Coastguard Worker        expect(rects[0].dir).toEqual(CanvasKit.TextDirection.LTR);
1244*c8dee2aaSAndroid Build Coastguard Worker        paint.delete();
1245*c8dee2aaSAndroid Build Coastguard Worker
1246*c8dee2aaSAndroid Build Coastguard Worker        paragraph.delete();
1247*c8dee2aaSAndroid Build Coastguard Worker        fontMgr.delete();
1248*c8dee2aaSAndroid Build Coastguard Worker        builder.delete();
1249*c8dee2aaSAndroid Build Coastguard Worker    });
1250*c8dee2aaSAndroid Build Coastguard Worker
1251*c8dee2aaSAndroid Build Coastguard Worker});
1252