xref: /aosp_15_r20/external/skia/modules/canvaskit/paragraph.js (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1(function(CanvasKit){
2  CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
3  CanvasKit._extraInitializations.push(function() {
4
5    CanvasKit.Paragraph.prototype.getRectsForRange = function(start, end, hStyle, wStyle) {
6    /**
7     * @type {Float32Array}
8     */
9      var floatArray = this._getRectsForRange(start, end, hStyle, wStyle);
10      return floatArrayToRects(floatArray);
11    }
12
13    CanvasKit.Paragraph.prototype.getRectsForPlaceholders = function() {
14        /**
15        * @type {Float32Array}
16        */
17        var floatArray = this._getRectsForPlaceholders();
18        return floatArrayToRects(floatArray);
19    }
20
21    function convertDirection(glyphInfo) {
22      if (glyphInfo) {
23        if (glyphInfo['dir'] === 0) {
24          glyphInfo['dir'] = CanvasKit.TextDirection.RTL;
25        } else {
26          glyphInfo['dir'] = CanvasKit.TextDirection.LTR;
27        }
28      }
29      return glyphInfo;
30    }
31
32    CanvasKit.Paragraph.prototype.getGlyphInfoAt = function(index) {
33      return convertDirection(this._getGlyphInfoAt(index));
34    }
35
36    CanvasKit.Paragraph.prototype.getClosestGlyphInfoAtCoordinate = function(dx, dy) {
37      return convertDirection(this._getClosestGlyphInfoAtCoordinate(dx, dy));
38    }
39
40    function floatArrayToRects(floatArray) {
41        if (!floatArray || !floatArray.length) {
42            return [];
43        }
44        var ret = [];
45        for (var i = 0; i < floatArray.length; i+=5) {
46            var rect = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
47            var dir = CanvasKit.TextDirection.LTR;
48            if (floatArray[i+4] === 0) {
49                dir = CanvasKit.TextDirection.RTL;
50            }
51            ret.push({'rect': rect, 'dir': dir});
52        }
53        CanvasKit._free(floatArray.byteOffset);
54        return ret;
55    }
56
57    // Registers the font (provided as an arrayBuffer) with the alias `family`.
58    CanvasKit.TypefaceFontProvider.prototype.registerFont = function(font, family) {
59      var typeface = CanvasKit.Typeface.MakeTypefaceFromData(font);
60      if (!typeface) {
61          Debug('Could not decode font data');
62          // We do not need to free the data since the C++ will do that for us
63          // when the font is deleted (or fails to decode);
64          return null;
65      }
66      var familyPtr = cacheOrCopyString(family);
67      this._registerFont(typeface, familyPtr);
68    }
69
70    // These helpers fill out all fields, because emscripten complains if we
71    // have undefined and it expects, for example, a float.
72    // TODO(kjlubick) For efficiency, we should probably just return opaque WASM objects so we do
73    //   not have to keep copying them across the wire.
74    CanvasKit.ParagraphStyle = function(s) {
75      // Use [''] to tell closure not to minify the names
76      s['disableHinting'] = s['disableHinting'] || false;
77      if (s['ellipsis']) {
78        var str = s['ellipsis'];
79        s['_ellipsisPtr'] = cacheOrCopyString(str);
80        s['_ellipsisLen'] = lengthBytesUTF8(str);
81      } else {
82        s['_ellipsisPtr'] = nullptr;
83        s['_ellipsisLen'] = 0;
84      }
85
86      if (s['heightMultiplier'] == null) {
87        s['heightMultiplier'] = -1
88      }
89      s['maxLines'] = s['maxLines'] || 0;
90      s['replaceTabCharacters'] = s['replaceTabCharacters'] || false;
91      s['strutStyle'] = strutStyle(s['strutStyle']);
92      s['textAlign'] = s['textAlign'] || CanvasKit.TextAlign.Start;
93      s['textDirection'] = s['textDirection'] || CanvasKit.TextDirection.LTR;
94      s['textHeightBehavior'] = s['textHeightBehavior'] || CanvasKit.TextHeightBehavior.All;
95      s['textStyle'] = CanvasKit.TextStyle(s['textStyle']);
96      s['applyRoundingHack'] = s['applyRoundingHack'] !== false;
97      return s;
98    };
99
100    function fontStyle(s) {
101      s = s || {};
102      // Can't check for falsey as 0 width means "invisible".
103      if (s['weight'] === undefined) {
104        s['weight'] = CanvasKit.FontWeight.Normal;
105      }
106      s['width'] = s['width'] || CanvasKit.FontWidth.Normal;
107      s['slant'] = s['slant'] || CanvasKit.FontSlant.Upright;
108      return s;
109    }
110
111    function strutStyle(s) {
112        s = s || {};
113        s['strutEnabled'] = s['strutEnabled'] || false;
114
115        if (s['strutEnabled'] && Array.isArray(s['fontFamilies']) && s['fontFamilies'].length) {
116            s['_fontFamiliesPtr'] = naiveCopyStrArray(s['fontFamilies']);
117            s['_fontFamiliesLen'] = s['fontFamilies'].length;
118        } else {
119            s['_fontFamiliesPtr'] = nullptr;
120            s['_fontFamiliesLen'] = 0;
121        }
122        s['fontStyle'] = fontStyle(s['fontStyle']);
123        if (s['fontSize'] == null) {
124          s['fontSize'] = -1
125        }
126        if (s['heightMultiplier'] == null) {
127          s['heightMultiplier'] = -1
128        }
129        s['halfLeading'] = s['halfLeading'] || false;
130        s['leading'] = s['leading'] || 0;
131        s['forceStrutHeight'] = s['forceStrutHeight'] || false;
132        return s;
133    }
134
135    CanvasKit.TextStyle = function(s) {
136       // Use [''] to tell closure not to minify the names
137      if (!s['color']) {
138        s['color'] = CanvasKit.BLACK;
139      }
140
141      s['decoration'] = s['decoration'] || 0;
142      s['decorationThickness'] = s['decorationThickness'] || 0;
143      s['decorationStyle'] = s['decorationStyle'] || CanvasKit.DecorationStyle.Solid;
144      s['textBaseline'] = s['textBaseline'] || CanvasKit.TextBaseline.Alphabetic;
145      if (s['fontSize'] == null) {
146        s['fontSize'] = -1
147      }
148      s['letterSpacing'] = s['letterSpacing'] || 0;
149      s['wordSpacing'] = s['wordSpacing'] || 0;
150      if (s['heightMultiplier'] == null) {
151        s['heightMultiplier'] = -1
152      }
153      s['halfLeading'] = s['halfLeading'] || false;
154      s['fontStyle'] = fontStyle(s['fontStyle']);
155
156      // Properties which need to be Malloc'ed are set in `copyArrays`.
157
158      return s;
159    };
160
161    // returns a pointer to a place on the heap that has an array
162    // of char* (effectively a char**). For now, this does the naive thing
163    // and depends on the string being null-terminated. This should be used
164    // for simple, well-formed things (e.g. font-families), not arbitrary
165    // text that should be drawn. If we need this to handle more complex
166    // strings, it should return two pointers, a pointer of the
167    // string array and a pointer to an array of the strings byte lengths.
168    function naiveCopyStrArray(strings) {
169      if (!strings || !strings.length) {
170        return nullptr;
171      }
172      var sPtrs = [];
173      for (var i = 0; i < strings.length; i++) {
174        var strPtr = cacheOrCopyString(strings[i]);
175        sPtrs.push(strPtr);
176      }
177      return copy1dArray(sPtrs, 'HEAPU32');
178    }
179
180    // maps string -> malloc'd pointer
181    var stringCache = {};
182
183    // cacheOrCopyString copies a string from JS into WASM on the heap and returns the pointer
184    // to the memory of the string. It is expected that a caller to this helper will *not* free
185    // that memory, so it is cached. Thus, if a future call to this function with the same string
186    // will return the cached pointer, preventing the memory usage from growing unbounded (in
187    // a normal use case).
188    function cacheOrCopyString(str) {
189      if (stringCache[str]) {
190        return stringCache[str];
191      }
192      // Add 1 for null terminator, which we need when copying/converting
193      var strLen = lengthBytesUTF8(str) + 1;
194      var strPtr = CanvasKit._malloc(strLen);
195      stringToUTF8(str, strPtr, strLen);
196      stringCache[str] = strPtr;
197      return strPtr;
198    }
199
200    // These scratch arrays are allocated once to copy the color data into, which saves us
201    // having to free them after every invocation.
202    var scratchForegroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
203    var scratchBackgroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
204    var scratchDecorationColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
205
206    function copyArrays(textStyle) {
207      // These color fields were arrays, but will set to WASM pointers before we pass this
208      // object over the WASM interface.
209      textStyle['_colorPtr'] = copyColorToWasm(textStyle['color']);
210      textStyle['_foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js
211      textStyle['_backgroundColorPtr'] = nullptr;
212      textStyle['_decorationColorPtr'] = nullptr;
213      if (textStyle['foregroundColor']) {
214        textStyle['_foregroundColorPtr'] = copyColorToWasm(textStyle['foregroundColor'], scratchForegroundColorPtr);
215      }
216      if (textStyle['backgroundColor']) {
217        textStyle['_backgroundColorPtr'] = copyColorToWasm(textStyle['backgroundColor'], scratchBackgroundColorPtr);
218      }
219      if (textStyle['decorationColor']) {
220        textStyle['_decorationColorPtr'] = copyColorToWasm(textStyle['decorationColor'], scratchDecorationColorPtr);
221      }
222
223      if (Array.isArray(textStyle['fontFamilies']) && textStyle['fontFamilies'].length) {
224        textStyle['_fontFamiliesPtr'] = naiveCopyStrArray(textStyle['fontFamilies']);
225        textStyle['_fontFamiliesLen'] = textStyle['fontFamilies'].length;
226      } else {
227        textStyle['_fontFamiliesPtr'] = nullptr;
228        textStyle['_fontFamiliesLen'] = 0;
229        Debug('no font families provided, text may draw wrong or not at all');
230      }
231
232      if (textStyle['locale']) {
233        var str = textStyle['locale'];
234        textStyle['_localePtr'] = cacheOrCopyString(str);
235        textStyle['_localeLen'] = lengthBytesUTF8(str);
236      } else {
237        textStyle['_localePtr'] = nullptr;
238        textStyle['_localeLen'] = 0;
239      }
240
241      if (Array.isArray(textStyle['shadows']) && textStyle['shadows'].length) {
242        var shadows = textStyle['shadows'];
243        var shadowColors = shadows.map(function (s) { return s['color'] || CanvasKit.BLACK; });
244        var shadowBlurRadii = shadows.map(function (s) { return s['blurRadius'] || 0.0; });
245        textStyle['_shadowLen'] = shadows.length;
246        // 2 floats per point, 4 bytes per float
247        var ptr = CanvasKit._malloc(shadows.length * 2 * 4);
248        var adjustedPtr = ptr / 4;  // 4 bytes per float
249        for (var i = 0; i < shadows.length; i++) {
250          var offset = shadows[i]['offset'] || [0, 0];
251          CanvasKit.HEAPF32[adjustedPtr] = offset[0];
252          CanvasKit.HEAPF32[adjustedPtr + 1] = offset[1];
253          adjustedPtr += 2;
254        }
255        textStyle['_shadowColorsPtr'] = copyFlexibleColorArray(shadowColors).colorPtr;
256        textStyle['_shadowOffsetsPtr'] = ptr;
257        textStyle['_shadowBlurRadiiPtr'] = copy1dArray(shadowBlurRadii, 'HEAPF32');
258      } else {
259        textStyle['_shadowLen'] = 0;
260        textStyle['_shadowColorsPtr'] = nullptr;
261        textStyle['_shadowOffsetsPtr'] = nullptr;
262        textStyle['_shadowBlurRadiiPtr'] = nullptr;
263      }
264
265      if (Array.isArray(textStyle['fontFeatures']) && textStyle['fontFeatures'].length) {
266        var fontFeatures = textStyle['fontFeatures'];
267        var fontFeatureNames = fontFeatures.map(function (s) { return s['name']; });
268        var fontFeatureValues = fontFeatures.map(function (s) { return s['value']; });
269        textStyle['_fontFeatureLen'] = fontFeatures.length;
270        textStyle['_fontFeatureNamesPtr'] = naiveCopyStrArray(fontFeatureNames);
271        textStyle['_fontFeatureValuesPtr'] = copy1dArray(fontFeatureValues, 'HEAPU32');
272      } else {
273        textStyle['_fontFeatureLen'] = 0;
274        textStyle['_fontFeatureNamesPtr'] = nullptr;
275        textStyle['_fontFeatureValuesPtr'] = nullptr;
276      }
277
278      if (Array.isArray(textStyle['fontVariations']) && textStyle['fontVariations'].length) {
279        var fontVariations = textStyle['fontVariations'];
280        var fontVariationAxes = fontVariations.map(function (s) { return s['axis']; });
281        var fontVariationValues = fontVariations.map(function (s) { return s['value']; });
282        textStyle['_fontVariationLen'] = fontVariations.length;
283        textStyle['_fontVariationAxesPtr'] = naiveCopyStrArray(fontVariationAxes);
284        textStyle['_fontVariationValuesPtr'] = copy1dArray(fontVariationValues, 'HEAPF32');
285      } else {
286        textStyle['_fontVariationLen'] = 0;
287        textStyle['_fontVariationAxesPtr'] = nullptr;
288        textStyle['_fontVariationValuesPtr'] = nullptr;
289      }
290    }
291
292    function freeArrays(textStyle) {
293      // The font family strings will get copied to a vector on the C++ side, which is owned by
294      // the text style.
295      CanvasKit._free(textStyle['_fontFamiliesPtr']);
296      CanvasKit._free(textStyle['_shadowColorsPtr']);
297      CanvasKit._free(textStyle['_shadowOffsetsPtr']);
298      CanvasKit._free(textStyle['_shadowBlurRadiiPtr']);
299      CanvasKit._free(textStyle['_fontFeatureNamesPtr']);
300      CanvasKit._free(textStyle['_fontFeatureValuesPtr']);
301      CanvasKit._free(textStyle['_fontVariationAxesPtr']);
302      CanvasKit._free(textStyle['_fontVariationValuesPtr']);
303    }
304
305    CanvasKit.ParagraphBuilder.Make = function(paragraphStyle, fontManager) {
306      copyArrays(paragraphStyle['textStyle']);
307
308      var result =  CanvasKit.ParagraphBuilder._Make(paragraphStyle, fontManager);
309      freeArrays(paragraphStyle['textStyle']);
310      return result;
311    };
312
313    CanvasKit.ParagraphBuilder.MakeFromFontProvider = function(paragraphStyle, fontProvider) {
314        copyArrays(paragraphStyle['textStyle']);
315
316        var result =  CanvasKit.ParagraphBuilder._MakeFromFontProvider(paragraphStyle, fontProvider);
317        freeArrays(paragraphStyle['textStyle']);
318        return result;
319    };
320
321    CanvasKit.ParagraphBuilder.MakeFromFontCollection = function(paragraphStyle, fontCollection) {
322        copyArrays(paragraphStyle['textStyle']);
323
324        var result = CanvasKit.ParagraphBuilder._MakeFromFontCollection(
325	    paragraphStyle, fontCollection);
326        freeArrays(paragraphStyle['textStyle']);
327        return result;
328    };
329
330    CanvasKit.ParagraphBuilder.ShapeText = function(text, blocks, width) {
331        let length = 0;
332        for (const b of blocks) {
333            length += b.length;
334        }
335        if (length !== text.length) {
336            throw "Accumulated block lengths must equal text.length";
337        }
338        return CanvasKit.ParagraphBuilder._ShapeText(text, blocks, width);
339    };
340
341    CanvasKit.ParagraphBuilder.prototype.pushStyle = function(textStyle) {
342      copyArrays(textStyle);
343      this._pushStyle(textStyle);
344      freeArrays(textStyle);
345    };
346
347    CanvasKit.ParagraphBuilder.prototype.pushPaintStyle = function(textStyle, fg, bg) {
348      copyArrays(textStyle);
349      this._pushPaintStyle(textStyle, fg, bg);
350      freeArrays(textStyle);
351    };
352
353    CanvasKit.ParagraphBuilder.prototype.addPlaceholder =
354          function(width, height, alignment, baseline, offset) {
355      width = width || 0;
356      height = height || 0;
357      alignment = alignment || CanvasKit.PlaceholderAlignment.Baseline;
358      baseline = baseline || CanvasKit.TextBaseline.Alphabetic;
359      offset = offset || 0;
360      this._addPlaceholder(width, height, alignment, baseline, offset);
361    };
362
363    CanvasKit.ParagraphBuilder.prototype.setWordsUtf8 = function(words) {
364      var bPtr = copy1dArray(words, 'HEAPU32');
365      this._setWordsUtf8(bPtr, words && words.length || 0);
366      freeArraysThatAreNotMallocedByUsers(bPtr,     words);
367    };
368    CanvasKit.ParagraphBuilder.prototype.setWordsUtf16 = function(words) {
369      var bPtr = copy1dArray(words, 'HEAPU32');
370      this._setWordsUtf16(bPtr, words && words.length || 0);
371      freeArraysThatAreNotMallocedByUsers(bPtr, words);
372    };
373
374    CanvasKit.ParagraphBuilder.prototype.setGraphemeBreaksUtf8 = function(graphemeBreaks) {
375      var bPtr = copy1dArray(graphemeBreaks, 'HEAPU32');
376      this._setGraphemeBreaksUtf8(bPtr, graphemeBreaks && graphemeBreaks.length || 0);
377      freeArraysThatAreNotMallocedByUsers(bPtr,     graphemeBreaks);
378    };
379    CanvasKit.ParagraphBuilder.prototype.setGraphemeBreaksUtf16 = function(graphemeBreaks) {
380      var bPtr = copy1dArray(graphemeBreaks, 'HEAPU32');
381      this._setGraphemeBreaksUtf16(bPtr, graphemeBreaks && graphemeBreaks.length || 0);
382      freeArraysThatAreNotMallocedByUsers(bPtr, graphemeBreaks);
383    };
384
385    CanvasKit.ParagraphBuilder.prototype.setLineBreaksUtf8 = function(lineBreaks) {
386      var bPtr = copy1dArray(lineBreaks, 'HEAPU32');
387      this._setLineBreaksUtf8(bPtr, lineBreaks && lineBreaks.length || 0);
388      freeArraysThatAreNotMallocedByUsers(bPtr,     lineBreaks);
389    };
390    CanvasKit.ParagraphBuilder.prototype.setLineBreaksUtf16 = function(lineBreaks) {
391      var bPtr = copy1dArray(lineBreaks, 'HEAPU32');
392      this._setLineBreaksUtf16(bPtr, lineBreaks && lineBreaks.length || 0);
393      freeArraysThatAreNotMallocedByUsers(bPtr, lineBreaks);
394    };
395});
396}(Module)); // When this file is loaded in, the high level object is "Module";
397