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