blob: 8c14eaa9a19c1ca6e443d019c47af26b5848bc5a [file] [log] [blame]
<!DOCTYPE html>
<title>CanvasKit Paragraph (with & without ICU)</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="/build/canvaskit.js"></script>
<style>
canvas {
border: 1px dashed #AAA;
}
#withICU {
border-color: red;
}
#withoutICU {
border-color: green;
}
</style>
<table>
<thead>
<th><h2 style="color: red;">With ICU</h2></th>
<th></th>
<th><h2 style="color: green;">Without ICU</h2></th>
</thead>
<tr>
<td><canvas id="withICU" width=600 height=600></canvas></td>
<td style="width: 20px;"></td>
<td><canvas id="withoutICU" width=600 height=600 tabindex='-1'></canvas></td>
</tr>
</table>
<script type="text/javascript" charset="utf-8">
var CanvasKit = null;
var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
const ckLoaded = CanvasKitInit({locateFile: (file) => '/build/'+file});
const loadFonts = [
fetch(cdn + 'Roboto-Regular.ttf').then((response) => response.arrayBuffer()),
fetch('https://fonts.gstatic.com/s/notoemoji/v26/bMrnmSyK7YY-MEu6aWjPDs-ar6uWaGWuob-r0jwvS-FGJCMY.ttf').then((response) => response.arrayBuffer()),
fetch('https://fonts.gstatic.com/s/notosanssymbols/v34/rP2up3q65FkAtHfwd-eIS2brbDN6gxP34F9jRRCe4W3gfQ8gavVFRkzrbQ.ttf').then((response) => response.arrayBuffer()),
fetch('https://fonts.gstatic.com/s/notosanssymbols2/v15/I_uyMoGduATTei9eI8daxVHDyfisHr71ypPqfX71-AI.ttf').then((response) => response.arrayBuffer()),
fetch('https://fonts.gstatic.com/s/notosansarabic/v18/nwpxtLGrOAZMl5nJ_wfgRg3DrWFZWsnVBJ_sS6tlqHHFlhQ5l3sQWIHPqzCfyGyvu3CBFQLaig.ttf').then((response) => response.arrayBuffer()),
];
ckLoaded.then((CK) => {
CanvasKit = CK;
});
Promise.all([ckLoaded, Promise.all(loadFonts)]).then((results) => {
ParagraphWithICU(...results);
ParagraphWithoutICU(...results);
});
const sampleText = 'The لاquick 😠brown\nfox ate a hamburgerfons and got sick.\n';
const fontFamilies = [
'Roboto',
'Noto Emoji',
'Noto Sans Arabic',
'Noto Sans Symbols',
'Noto Sans Symbols 2',
];
function ParagraphWithICU(CanvasKit, fonts) {
if (!CanvasKit || !fonts) {
return;
}
const surface = CanvasKit.MakeCanvasSurface('withICU');
if (!surface) {
console.error('Could not make surface');
return;
}
const canvas = surface.getCanvas();
const fontMgr = CanvasKit.FontMgr.FromData(fonts);
const paraStyle = new CanvasKit.ParagraphStyle({
textStyle: {
color: CanvasKit.BLACK,
fontFamilies: fontFamilies,
fontSize: 50,
},
textAlign: CanvasKit.TextAlign.Left,
maxLines: 4,
});
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
builder.addText(sampleText);
const paragraph = builder.build();
fontMgr.delete();
function drawFrame(canvas) {
drawParagraph(canvas, paragraph);
surface.requestAnimationFrame(drawFrame);
}
surface.requestAnimationFrame(drawFrame);
}
function ParagraphWithoutICU(CanvasKit, fonts) {
if (!CanvasKit || !fonts) {
return;
}
const surface = CanvasKit.MakeCanvasSurface('withoutICU');
if (!surface) {
console.error('Could not make surface');
return;
}
const fontMgr = CanvasKit.FontMgr.FromData(fonts);
const paraStyle = new CanvasKit.ParagraphStyle({
textStyle: {
color: CanvasKit.BLACK,
fontFamilies: fontFamilies,
fontSize: 50,
},
maxLines: 4,
textAlign: CanvasKit.TextAlign.Left,
});
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
builder.addText(sampleText);
const text = sampleText;
// Pass 3 bidi regions. The array is made of triplets {start, end, direction}.
const mallocedBidis = CanvasKit.Malloc(Uint32Array, 9);
mallocedBidis.toTypedArray().set([
0, 4, CanvasKit.TextDirection.LTR.value,
4, 6, CanvasKit.TextDirection.RTL.value,
6, text.length, CanvasKit.TextDirection.LTR.value,
]);
// Pass the entire text as one word. It's only used for the method
// getWords.
const mallocedWords = CanvasKit.Malloc(Uint32Array, 2);
mallocedWords.toTypedArray().set([0, text.length]);
const graphemeBoundaries = getGraphemeBoundaries(text);
const mallocedGraphemes = CanvasKit.Malloc(Uint32Array, graphemeBoundaries.length);
mallocedGraphemes.toTypedArray().set(graphemeBoundaries);
const lineBreaks = getLineBreaks(text);
const mallocedLineBreaks = CanvasKit.Malloc(Uint32Array, lineBreaks.length);
mallocedLineBreaks.toTypedArray().set(lineBreaks);
const paragraph = builder.buildWithClientInfo(
mallocedBidis,
mallocedWords,
mallocedGraphemes,
mallocedLineBreaks
);
fontMgr.delete();
function drawFrame(canvas) {
drawParagraph(canvas, paragraph);
surface.requestAnimationFrame(drawFrame);
}
surface.requestAnimationFrame(drawFrame);
return surface;
}
function drawParagraph(canvas, paragraph) {
const fontPaint = new CanvasKit.Paint();
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
fontPaint.setAntiAlias(true);
canvas.clear(CanvasKit.WHITE);
const wrapTo = 350 + 150 * Math.sin(Date.now() / 4000);
paragraph.layout(wrapTo);
const rects = [
...paragraph.getRectsForRange(2, 8, CanvasKit.RectHeightStyle.Tight, CanvasKit.RectWidthStyle.Tight),
...paragraph.getRectsForRange(12, 16, CanvasKit.RectHeightStyle.Tight, CanvasKit.RectWidthStyle.Tight),
];
const rectPaint = new CanvasKit.Paint();
const colors = [CanvasKit.CYAN, CanvasKit.MAGENTA, CanvasKit.BLUE, CanvasKit.YELLOW];
for (const rect of rects) {
rectPaint.setColor(colors.shift() || CanvasKit.RED);
canvas.drawRect(rect, rectPaint);
}
canvas.drawParagraph(paragraph, 0, 0);
canvas.drawLine(wrapTo, 0, wrapTo, 400, fontPaint);
}
const SOFT = 0;
const HARD = 1;
function getLineBreaks(text) {
const breaks = [0, SOFT];
const iterator = new Intl.v8BreakIterator(['en'], {type: 'line'});
iterator.adoptText(text);
iterator.first();
while (iterator.next() != -1) {
breaks.push(iterator.current(), getBreakType(iterator.breakType()));
}
console.log(breaks);
return breaks;
}
function getBreakType(v8BreakType) {
return v8BreakType == 'none' ? SOFT : HARD;
}
function getGraphemeBoundaries(text) {
const segmenter = new Intl.Segmenter(['en'], {type: 'grapheme'});
const segments = segmenter.segment(text);
const graphemeBoundaries = [];
for (const segment of segments) {
graphemeBoundaries.push(segment.index);
}
graphemeBoundaries.push(text.length);
return graphemeBoundaries;
}
</script>