xref: /aosp_15_r20/external/skia/modules/canvaskit/font.js (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
2CanvasKit._extraInitializations.push(function() {
3
4  CanvasKit.Canvas.prototype.drawText = function(str, x, y, paint, font) {
5    // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
6    // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
7    var strLen = lengthBytesUTF8(str);
8    // Add 1 for null terminator, which we need when copying/converting, but can ignore
9    // when we call into Skia.
10    var strPtr = CanvasKit._malloc(strLen + 1);
11    stringToUTF8(str, strPtr, strLen + 1);
12    this._drawSimpleText(strPtr, strLen, x, y, font, paint);
13    CanvasKit._free(strPtr);
14  };
15
16  CanvasKit.Canvas.prototype.drawGlyphs = function(glyphs, positions, x, y, font, paint) {
17    if (!(glyphs.length*2 <= positions.length)) {
18        throw 'Not enough positions for the array of gyphs';
19    }
20    CanvasKit.setCurrentContext(this._context);
21    const glyphs_ptr    = copy1dArray(glyphs, 'HEAPU16');
22    const positions_ptr = copy1dArray(positions, 'HEAPF32');
23
24    this._drawGlyphs(glyphs.length, glyphs_ptr, positions_ptr, x, y, font, paint);
25
26    freeArraysThatAreNotMallocedByUsers(positions_ptr, positions);
27    freeArraysThatAreNotMallocedByUsers(glyphs_ptr,    glyphs);
28  };
29
30  // Glyphs should be a Uint16Array of glyph ids, e.g. provided by Font.getGlyphIDs.
31  // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type.
32  // The return value will be a Float32Array that is 4 times as long as the input array. For each
33  // glyph, there will be 4 floats for left, top, right, bottom (relative to 0, 0) for that glyph.
34  CanvasKit.Font.prototype.getGlyphBounds = function(glyphs, paint, optionalOutputArray) {
35    var glyphPtr = copy1dArray(glyphs, 'HEAPU16');
36    var bytesPerRect = 4 * 4;
37    var rectPtr = CanvasKit._malloc(glyphs.length * bytesPerRect);
38    this._getGlyphWidthBounds(glyphPtr, glyphs.length, nullptr, rectPtr, paint || null);
39
40    var rects = new Float32Array(CanvasKit.HEAPU8.buffer, rectPtr, glyphs.length * 4);
41    freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs);
42    if (optionalOutputArray) {
43      optionalOutputArray.set(rects);
44      CanvasKit._free(rectPtr);
45      return optionalOutputArray;
46    }
47    var rv = Float32Array.from(rects);
48    CanvasKit._free(rectPtr);
49    return rv;
50  };
51
52  CanvasKit.Font.prototype.getGlyphIDs = function(str, numGlyphIDs, optionalOutputArray) {
53    if (!numGlyphIDs) {
54      numGlyphIDs = str.length;
55    }
56    // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
57    // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
58    // Add 1 for null terminator
59    var strBytes = lengthBytesUTF8(str) + 1;
60    var strPtr = CanvasKit._malloc(strBytes);
61    stringToUTF8(str, strPtr, strBytes); // This includes the null terminator
62
63    var bytesPerGlyph = 2;
64    var glyphPtr = CanvasKit._malloc(numGlyphIDs * bytesPerGlyph);
65    // We don't need to compute the id for the null terminator, so subtract 1.
66    var actualIDs = this._getGlyphIDs(strPtr, strBytes - 1, numGlyphIDs, glyphPtr);
67    CanvasKit._free(strPtr);
68    if (actualIDs < 0) {
69      Debug('Could not get glyphIDs');
70      CanvasKit._free(glyphPtr);
71      return null;
72    }
73    var glyphs = new Uint16Array(CanvasKit.HEAPU8.buffer, glyphPtr, actualIDs);
74    if (optionalOutputArray) {
75      optionalOutputArray.set(glyphs);
76      CanvasKit._free(glyphPtr);
77      return optionalOutputArray;
78    }
79    var rv = Uint16Array.from(glyphs);
80    CanvasKit._free(glyphPtr);
81    return rv;
82  };
83
84  CanvasKit.Font.prototype.getGlyphIntercepts = function(glyphs, positions, top, bottom) {
85    var gPtr = copy1dArray(glyphs, 'HEAPU16');
86    var pPtr = copy1dArray(positions, 'HEAPF32');
87    return this._getGlyphIntercepts(gPtr, glyphs.length, !wasMalloced(glyphs),
88                                    pPtr, positions.length, !wasMalloced(positions),
89                                    top, bottom);
90  };
91
92  // Glyphs should be a Uint16Array of glyph ids, e.g. provided by Font.getGlyphIDs.
93  // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type.
94  // The return value will be a Float32Array that has one width per input glyph.
95  CanvasKit.Font.prototype.getGlyphWidths = function(glyphs, paint, optionalOutputArray) {
96    var glyphPtr = copy1dArray(glyphs, 'HEAPU16');
97    var bytesPerWidth = 4;
98    var widthPtr = CanvasKit._malloc(glyphs.length * bytesPerWidth);
99    this._getGlyphWidthBounds(glyphPtr, glyphs.length, widthPtr, nullptr, paint || null);
100
101    var widths = new Float32Array(CanvasKit.HEAPU8.buffer, widthPtr, glyphs.length);
102    freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs);
103    if (optionalOutputArray) {
104      optionalOutputArray.set(widths);
105      CanvasKit._free(widthPtr);
106      return optionalOutputArray;
107    }
108    var rv = Float32Array.from(widths);
109    CanvasKit._free(widthPtr);
110    return rv;
111  };
112
113  // arguments should all be arrayBuffers or be an array of arrayBuffers.
114  CanvasKit.FontMgr.FromData = function() {
115    if (!arguments.length) {
116      Debug('Could not make FontMgr from no font sources');
117      return null;
118    }
119    var fonts = arguments;
120    if (fonts.length === 1 && Array.isArray(fonts[0])) {
121      fonts = arguments[0];
122    }
123    if (!fonts.length) {
124      Debug('Could not make FontMgr from no font sources');
125      return null;
126    }
127    var dPtrs = [];
128    var sizes = [];
129    for (var i = 0; i < fonts.length; i++) {
130      var data = new Uint8Array(fonts[i]);
131      var dptr = copy1dArray(data, 'HEAPU8');
132      dPtrs.push(dptr);
133      sizes.push(data.byteLength);
134    }
135    // Pointers are 32 bit unsigned ints
136    var datasPtr = copy1dArray(dPtrs, 'HEAPU32');
137    var sizesPtr = copy1dArray(sizes, 'HEAPU32');
138    var fm = CanvasKit.FontMgr._fromData(datasPtr, sizesPtr, fonts.length);
139    // The FontMgr has taken ownership of the bytes we allocated in the for loop.
140    CanvasKit._free(datasPtr);
141    CanvasKit._free(sizesPtr);
142    return fm;
143  };
144
145  CanvasKit.Typeface.MakeTypefaceFromData = function(fontData) {
146    var data = new Uint8Array(fontData);
147
148    var fptr = copy1dArray(data, 'HEAPU8');
149    var font = CanvasKit.Typeface._MakeTypefaceFromData(fptr, data.byteLength);
150    if (!font) {
151      Debug('Could not decode font data');
152      // We do not need to free the data since the C++ will do that for us
153      // when the font is deleted (or fails to decode);
154      return null;
155    }
156    return font;
157  };
158
159  // TODO(kjlubick) remove this after clients have migrated.
160  CanvasKit.Typeface["MakeFreeTypeFaceFromData"] = CanvasKit.Typeface.MakeTypefaceFromData;
161
162  CanvasKit.Typeface.prototype.getGlyphIDs = function(str, numGlyphIDs, optionalOutputArray) {
163    if (!numGlyphIDs) {
164      numGlyphIDs = str.length;
165    }
166    // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
167    // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
168    // Add 1 for null terminator
169    var strBytes = lengthBytesUTF8(str) + 1;
170    var strPtr = CanvasKit._malloc(strBytes);
171    stringToUTF8(str, strPtr, strBytes); // This includes the null terminator
172
173    var bytesPerGlyph = 2;
174    var glyphPtr = CanvasKit._malloc(numGlyphIDs * bytesPerGlyph);
175    // We don't need to compute the id for the null terminator, so subtract 1.
176    var actualIDs = this._getGlyphIDs(strPtr, strBytes - 1, numGlyphIDs, glyphPtr);
177    CanvasKit._free(strPtr);
178    if (actualIDs < 0) {
179      Debug('Could not get glyphIDs');
180      CanvasKit._free(glyphPtr);
181      return null;
182    }
183    var glyphs = new Uint16Array(CanvasKit.HEAPU8.buffer, glyphPtr, actualIDs);
184    if (optionalOutputArray) {
185      optionalOutputArray.set(glyphs);
186      CanvasKit._free(glyphPtr);
187      return optionalOutputArray;
188    }
189    var rv = Uint16Array.from(glyphs);
190    CanvasKit._free(glyphPtr);
191    return rv;
192  };
193
194  CanvasKit.TextBlob.MakeOnPath = function(str, path, font, initialOffset) {
195    if (!str || !str.length) {
196      Debug('ignoring 0 length string');
197      return;
198    }
199    if (!path || !path.countPoints()) {
200      Debug('ignoring empty path');
201      return;
202    }
203    if (path.countPoints() === 1) {
204      Debug('path has 1 point, returning normal textblob');
205      return this.MakeFromText(str, font);
206    }
207
208    if (!initialOffset) {
209      initialOffset = 0;
210    }
211
212    var ids = font.getGlyphIDs(str);
213    var widths = font.getGlyphWidths(ids);
214
215    var rsx = [];
216    var meas = new CanvasKit.ContourMeasureIter(path, false, 1);
217    var cont = meas.next();
218    var dist = initialOffset;
219    var xycs = new Float32Array(4);
220    for (var i = 0; i < str.length && cont; i++) {
221      var width = widths[i];
222      dist += width/2;
223      if (dist > cont.length()) {
224        // jump to next contour
225        cont.delete();
226        cont = meas.next();
227        if (!cont) {
228          // We have come to the end of the path - terminate the string
229          // right here.
230          str = str.substring(0, i);
231          break;
232        }
233        dist = width/2;
234      }
235
236      // Gives us the (x, y) coordinates as well as the cos/sin of the tangent
237      // line at that position.
238      cont.getPosTan(dist, xycs);
239      var cx = xycs[0];
240      var cy = xycs[1];
241      var cosT = xycs[2];
242      var sinT = xycs[3];
243
244      var adjustedX = cx - (width/2 * cosT);
245      var adjustedY = cy - (width/2 * sinT);
246
247      rsx.push(cosT, sinT, adjustedX, adjustedY);
248      dist += width/2;
249    }
250    var retVal = this.MakeFromRSXform(str, rsx, font);
251    cont && cont.delete();
252    meas.delete();
253    return retVal;
254  };
255
256  CanvasKit.TextBlob.MakeFromRSXform = function(str, rsxForms, font) {
257    // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
258    // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
259    // Add 1 for null terminator
260    var strLen = lengthBytesUTF8(str) + 1;
261    var strPtr = CanvasKit._malloc(strLen);
262    // Add 1 for the null terminator.
263    stringToUTF8(str, strPtr, strLen);
264
265    var rPtr = copy1dArray(rsxForms, 'HEAPF32');
266
267    var blob = CanvasKit.TextBlob._MakeFromRSXform(strPtr, strLen - 1, rPtr, font);
268    CanvasKit._free(strPtr);
269    if (!blob) {
270      Debug('Could not make textblob from string "' + str + '"');
271      return null;
272    }
273    return blob;
274  };
275
276  // Glyphs should be a Uint32Array of glyph ids, e.g. provided by Font.getGlyphIDs.
277  // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type.
278  CanvasKit.TextBlob.MakeFromRSXformGlyphs = function(glyphs, rsxForms, font) {
279    // Currently on the C++ side, glyph ids are 16bit, but there is an effort to change that.
280    var glyphPtr = copy1dArray(glyphs, 'HEAPU16');
281    var bytesPerGlyph = 2;
282
283    var rPtr = copy1dArray(rsxForms, 'HEAPF32');
284
285    var blob = CanvasKit.TextBlob._MakeFromRSXformGlyphs(glyphPtr, glyphs.length * bytesPerGlyph, rPtr, font);
286    freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs);
287    if (!blob) {
288      Debug('Could not make textblob from glyphs "' + glyphs + '"');
289      return null;
290    }
291    return blob;
292  };
293
294  // Glyphs should be a Uint32Array of glyph ids, e.g. provided by Font.getGlyphIDs.
295  // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type.
296  CanvasKit.TextBlob.MakeFromGlyphs = function(glyphs, font) {
297    // Currently on the C++ side, glyph ids are 16bit, but there is an effort to change that.
298    var glyphPtr = copy1dArray(glyphs, 'HEAPU16');
299    var bytesPerGlyph = 2;
300    var blob = CanvasKit.TextBlob._MakeFromGlyphs(glyphPtr, glyphs.length * bytesPerGlyph, font);
301    freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs);
302    if (!blob) {
303      Debug('Could not make textblob from glyphs "' + glyphs + '"');
304      return null;
305    }
306    return blob;
307  };
308
309  CanvasKit.TextBlob.MakeFromText = function(str, font) {
310    // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
311    // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
312    // Add 1 for null terminator
313    var strLen = lengthBytesUTF8(str) + 1;
314    var strPtr = CanvasKit._malloc(strLen);
315    // Add 1 for the null terminator.
316    stringToUTF8(str, strPtr, strLen);
317
318    var blob = CanvasKit.TextBlob._MakeFromText(strPtr, strLen - 1, font);
319    CanvasKit._free(strPtr);
320    if (!blob) {
321      Debug('Could not make textblob from string "' + str + '"');
322      return null;
323    }
324    return blob;
325  };
326
327  // A helper to return the right type for GlyphIDs stored internally. When that changes, this
328  // will also be changed, which will help avoid future breakages.
329  CanvasKit.MallocGlyphIDs = function(numGlyphIDs) {
330    return CanvasKit.Malloc(Uint16Array, numGlyphIDs);
331  }
332});
333