1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkColor.h"
9 #include "include/core/SkFontStyle.h"
10 #include "include/core/SkPictureRecorder.h"
11 #include "include/core/SkString.h"
12 
13 #include "modules/skparagraph/include/DartTypes.h"
14 #include "modules/skparagraph/include/Paragraph.h"
15 #include "modules/skparagraph/include/TextStyle.h"
16 #include "modules/skparagraph/include/TypefaceFontProvider.h"
17 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
18 #include "modules/skparagraph/src/ParagraphImpl.h"
19 
20 #include <string>
21 #include <vector>
22 
23 #include <emscripten.h>
24 #include <emscripten/bind.h>
25 #include "modules/canvaskit/WasmCommon.h"
26 
27 using namespace emscripten;
28 using namespace skia_private;
29 
30 namespace para = skia::textlayout;
31 
toSkColor4f(WASMPointerF32 cPtr)32 SkColor4f toSkColor4f(WASMPointerF32 cPtr) {
33     float* fourFloats = reinterpret_cast<float*>(cPtr);
34     SkColor4f color = {fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3]};
35     return color;
36 }
37 
38 struct SimpleFontStyle {
39     SkFontStyle::Slant slant;
40     SkFontStyle::Weight weight;
41     SkFontStyle::Width width;
42 };
43 
44 struct SimpleTextStyle {
45     WASMPointerF32 colorPtr;
46     WASMPointerF32 foregroundColorPtr;
47     WASMPointerF32 backgroundColorPtr;
48     uint8_t decoration;
49     SkScalar decorationThickness;
50     WASMPointerF32 decorationColorPtr;
51     para::TextDecorationStyle decorationStyle;
52     para::TextBaseline textBaseline;
53     SkScalar fontSize;
54     SkScalar letterSpacing;
55     SkScalar wordSpacing;
56     SkScalar heightMultiplier;
57     bool halfLeading;
58     WASMPointerU8 localePtr;
59     int localeLen;
60     SimpleFontStyle fontStyle;
61 
62     WASMPointerU8 fontFamiliesPtr;
63     int fontFamiliesLen;
64 
65     int shadowLen;
66     WASMPointerF32 shadowColorsPtr;
67     WASMPointerF32 shadowOffsetsPtr;
68     WASMPointerF32 shadowBlurRadiiPtr;
69 
70     int fontFeatureLen;
71     WASMPointerF32 fontFeatureNamesPtr;
72     WASMPointerF32 fontFeatureValuesPtr;
73 
74     int fontVariationLen;
75     WASMPointerF32 fontVariationAxesPtr;
76     WASMPointerF32 fontVariationValuesPtr;
77 };
78 
79 struct SimpleStrutStyle {
80     WASMPointerU32 fontFamiliesPtr;
81     int fontFamiliesLen;
82     SimpleFontStyle fontStyle;
83     SkScalar fontSize;
84     SkScalar heightMultiplier;
85     bool halfLeading;
86     SkScalar leading;
87     bool strutEnabled;
88     bool forceStrutHeight;
89 };
90 
toStrutStyle(const SimpleStrutStyle & s)91 para::StrutStyle toStrutStyle(const SimpleStrutStyle& s) {
92     para::StrutStyle ss;
93 
94     const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
95     if (fontFamilies != nullptr) {
96         std::vector<SkString> ff;
97         for (int i = 0; i < s.fontFamiliesLen; i++) {
98             ff.emplace_back(fontFamilies[i]);
99         }
100         ss.setFontFamilies(ff);
101     }
102 
103     SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
104     ss.setFontStyle(fs);
105 
106     if (s.fontSize != -1) {
107         ss.setFontSize(s.fontSize);
108     }
109     if (s.heightMultiplier != -1) {
110         ss.setHeight(s.heightMultiplier);
111         ss.setHeightOverride(true);
112     }
113     ss.setHalfLeading(s.halfLeading);
114 
115     if (s.leading != 0) {
116         ss.setLeading(s.leading);
117     }
118 
119     ss.setStrutEnabled(s.strutEnabled);
120     ss.setForceStrutHeight(s.forceStrutHeight);
121 
122     return ss;
123 }
124 
toTextStyle(const SimpleTextStyle & s)125 para::TextStyle toTextStyle(const SimpleTextStyle& s) {
126     para::TextStyle ts;
127 
128     // textstyle.color doesn't support a 4f color, however the foreground and background fields
129     // below do.
130     ts.setColor(toSkColor4f(s.colorPtr).toSkColor());
131 
132     // It is functionally important that these paints be unset when no value was provided.
133     if (s.foregroundColorPtr) {
134         SkPaint p1;
135         p1.setColor4f(toSkColor4f(s.foregroundColorPtr));
136         ts.setForegroundColor(p1);
137     }
138 
139     if (s.backgroundColorPtr) {
140         SkPaint p2;
141         p2.setColor4f(toSkColor4f(s.backgroundColorPtr));
142         ts.setBackgroundColor(p2);
143     }
144 
145     if (s.fontSize != -1) {
146         ts.setFontSize(s.fontSize);
147     }
148     if (s.letterSpacing != 0) {
149         ts.setLetterSpacing(s.letterSpacing);
150     }
151     if (s.wordSpacing != 0) {
152         ts.setWordSpacing(s.wordSpacing);
153     }
154 
155     if (s.heightMultiplier != -1) {
156         ts.setHeight(s.heightMultiplier);
157         ts.setHeightOverride(true);
158     }
159 
160     ts.setHalfLeading(s.halfLeading);
161 
162     ts.setDecoration(para::TextDecoration(s.decoration));
163     ts.setDecorationStyle(s.decorationStyle);
164     if (s.decorationThickness != 0) {
165         ts.setDecorationThicknessMultiplier(s.decorationThickness);
166     }
167     if (s.decorationColorPtr) {
168         ts.setDecorationColor(toSkColor4f(s.decorationColorPtr).toSkColor());
169     }
170 
171     if (s.localeLen > 0) {
172         const char* localePtr = reinterpret_cast<const char*>(s.localePtr);
173         SkString lStr(localePtr, s.localeLen);
174         ts.setLocale(lStr);
175     }
176 
177     const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
178     if (fontFamilies != nullptr) {
179         std::vector<SkString> ff;
180         for (int i = 0; i < s.fontFamiliesLen; i++) {
181             ff.emplace_back(fontFamilies[i]);
182         }
183         ts.setFontFamilies(ff);
184     }
185 
186     ts.setTextBaseline(s.textBaseline);
187 
188     SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
189     ts.setFontStyle(fs);
190 
191     if (s.shadowLen > 0) {
192         const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(s.shadowColorsPtr);
193         const SkPoint* offsets = reinterpret_cast<const SkPoint*>(s.shadowOffsetsPtr);
194         const float* blurRadii = reinterpret_cast<const float*>(s.shadowBlurRadiiPtr);
195         for (int i = 0; i < s.shadowLen; i++) {
196             para::TextShadow shadow(colors[i].toSkColor(), offsets[i], blurRadii[i]);
197             ts.addShadow(shadow);
198         }
199     }
200 
201 
202     if (s.fontFeatureLen > 0) {
203         const char** fontFeatureNames = reinterpret_cast<const char**>(s.fontFeatureNamesPtr);
204         const int* fontFeatureValues = reinterpret_cast<const int*>(s.fontFeatureValuesPtr);
205         for (int i = 0; i < s.fontFeatureLen; i++) {
206             // Font features names are 4-character simple strings.
207             SkString name(fontFeatureNames[i], 4);
208             ts.addFontFeature(name, fontFeatureValues[i]);
209         }
210     }
211 
212     if (s.fontVariationLen > 0) {
213         const char** fontVariationAxes = reinterpret_cast<const char**>(s.fontVariationAxesPtr);
214         const float* fontVariationValues = reinterpret_cast<const float*>(s.fontVariationValuesPtr);
215         std::vector<SkFontArguments::VariationPosition::Coordinate> coordinates;
216         for (int i = 0; i < s.fontVariationLen; i++) {
217             // Font variation axis tags are 4-character simple strings.
218             SkString axis(fontVariationAxes[i]);
219             if (axis.size() != 4) {
220                 continue;
221             }
222             coordinates.push_back({
223                 SkSetFourByteTag(axis[0], axis[1], axis[2], axis[3]),
224                 fontVariationValues[i]
225             });
226         }
227         SkFontArguments::VariationPosition position = {
228             coordinates.data(),
229             static_cast<int>(coordinates.size())
230         };
231         ts.setFontArguments(SkFontArguments().setVariationDesignPosition(position));
232     }
233 
234     return ts;
235 }
236 
237 struct SimpleParagraphStyle {
238     bool disableHinting;
239     WASMPointerU8 ellipsisPtr;
240     size_t ellipsisLen;
241     SkScalar heightMultiplier;
242     size_t maxLines;
243     bool replaceTabCharacters;
244     para::TextAlign textAlign;
245     para::TextDirection textDirection;
246     para::TextHeightBehavior textHeightBehavior;
247     SimpleTextStyle textStyle;
248     SimpleStrutStyle strutStyle;
249 };
250 
toParagraphStyle(const SimpleParagraphStyle & s)251 para::ParagraphStyle toParagraphStyle(const SimpleParagraphStyle& s) {
252     para::ParagraphStyle ps;
253     if (s.disableHinting) {
254         ps.turnHintingOff();
255     }
256 
257     if (s.ellipsisLen > 0) {
258         const char* ellipsisPtr = reinterpret_cast<const char*>(s.ellipsisPtr);
259         SkString eStr(ellipsisPtr, s.ellipsisLen);
260         ps.setEllipsis(eStr);
261     }
262     ps.setTextAlign(s.textAlign);
263     ps.setTextDirection(s.textDirection);
264     auto ts = toTextStyle(s.textStyle);
265     ps.setTextStyle(ts);
266     auto ss = toStrutStyle(s.strutStyle);
267     ps.setStrutStyle(ss);
268     if (s.heightMultiplier != -1) {
269         ps.setHeight(s.heightMultiplier);
270     }
271     if (s.maxLines != 0) {
272         ps.setMaxLines(s.maxLines);
273     }
274     ps.setTextHeightBehavior(s.textHeightBehavior);
275     ps.setReplaceTabCharacters(s.replaceTabCharacters);
276     return ps;
277 }
278 
279 struct SimpleTextBox {
280     SkRect rect;
281     // This isn't the most efficient way to represent this, but it is much easier to keep
282     // everything as floats when unpacking on the JS side.
283     // 0.0 = RTL, 1.0 = LTr
284     SkScalar direction;
285 };
286 
TextBoxesToFloat32Array(std::vector<para::TextBox> boxes)287 Float32Array TextBoxesToFloat32Array(std::vector<para::TextBox> boxes) {
288     // Pack these text boxes into an array of n groups of 5 SkScalar (floats)
289     if (!boxes.size()) {
290         return emscripten::val::null();
291     }
292     SimpleTextBox* rects = new SimpleTextBox[boxes.size()];
293     for (size_t i = 0; i < boxes.size(); i++) {
294         rects[i].rect = boxes[i].rect;
295         if (boxes[i].direction == para::TextDirection::kRtl) {
296             rects[i].direction = 0;
297         } else {
298             rects[i].direction = 1;
299         }
300     }
301     float* fPtr = reinterpret_cast<float*>(rects);
302     // Of note: now that we have cast rects to float*, emscripten is smart enough to wrap this
303     // into a Float32Array for us.
304     return Float32Array(typed_memory_view(boxes.size() * 5, fPtr));
305 }
306 
GetRectsForRange(para::Paragraph & self,unsigned start,unsigned end,para::RectHeightStyle heightStyle,para::RectWidthStyle widthStyle)307 Float32Array GetRectsForRange(para::Paragraph& self,
308                               unsigned start,
309                               unsigned end,
310                               para::RectHeightStyle heightStyle,
311                               para::RectWidthStyle widthStyle) {
312     std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
313     return TextBoxesToFloat32Array(boxes);
314 }
315 
GetRectsForPlaceholders(para::Paragraph & self)316 Float32Array GetRectsForPlaceholders(para::Paragraph& self) {
317     std::vector<para::TextBox> boxes = self.getRectsForPlaceholders();
318     return TextBoxesToFloat32Array(boxes);
319 }
320 
GetLineMetrics(para::Paragraph & self)321 JSArray GetLineMetrics(para::Paragraph& self) {
322     std::vector<skia::textlayout::LineMetrics> metrics;
323     self.getLineMetrics(metrics);
324     JSArray result = emscripten::val::array();
325     for (auto metric : metrics) {
326         JSObject m = emscripten::val::object();
327         m.set("startIndex", metric.fStartIndex);
328         m.set("endIndex", metric.fEndIndex);
329         m.set("endExcludingWhitespaces", metric.fEndExcludingWhitespaces);
330         m.set("endIncludingNewline", metric.fEndIncludingNewline);
331         m.set("isHardBreak", metric.fHardBreak);
332         m.set("ascent", metric.fAscent);
333         m.set("descent", metric.fDescent);
334         m.set("height", metric.fHeight);
335         m.set("width", metric.fWidth);
336         m.set("left", metric.fLeft);
337         m.set("baseline", metric.fBaseline);
338         m.set("lineNumber", metric.fLineNumber);
339         result.call<void>("push", m);
340     }
341     return result;
342 }
343 
344 /*
345  *  Returns Lines[]
346  */
GetShapedLines(para::Paragraph & self)347 JSArray GetShapedLines(para::Paragraph& self) {
348     struct LineAccumulate {
349         int         lineNumber  = -1;   // deliberately -1 from starting value
350         uint32_t    minOffset   = 0xFFFFFFFF;
351         uint32_t    maxOffset   = 0;
352         float       minAscent   = 0;
353         float       maxDescent  = 0;
354         // not really accumulated, but definitely set
355         float       baseline    = 0;
356 
357         void reset(int newLineNum) {
358             new (this) LineAccumulate;
359             this->lineNumber = newLineNum;
360         }
361     };
362 
363     // where we accumulate our js output
364     JSArray  jlines = emscripten::val::array();
365     JSObject jline = emscripten::val::null();
366     JSArray  jruns = emscripten::val::null();
367     LineAccumulate accum;
368 
369     self.visit([&](int lineNumber, const para::Paragraph::VisitorInfo* info) {
370         if (!info) {
371             if (!jline) return; // how???
372             // end of current line
373             JSObject range = emscripten::val::object();
374             range.set("first", accum.minOffset);
375             range.set("last",  accum.maxOffset);
376             jline.set("textRange", range);
377 
378             jline.set("top", accum.baseline + accum.minAscent);
379             jline.set("bottom", accum.baseline + accum.maxDescent);
380             jline.set("baseline", accum.baseline);
381             return;
382         }
383 
384         if (lineNumber != accum.lineNumber) {
385             SkASSERT(lineNumber == accum.lineNumber + 1);   // assume monotonic
386 
387             accum.reset(lineNumber);
388             jruns = emscripten::val::array();
389 
390             jline = emscripten::val::object();
391             jline.set("runs", jruns);
392             // will assign textRange and metrics on end-of-line signal
393 
394             jlines.call<void>("push", jline);
395         }
396 
397         // append the run
398         const int N = info->count;   // glyphs
399         const int N1 = N + 1;       // positions, offsets have 1 extra (trailing) slot
400 
401         JSObject jrun = emscripten::val::object();
402 
403         jrun.set("flags",    info->flags);
404 
405 // TODO: figure out how to set a wrapped sk_sp<SkTypeface>
406 //        jrun.set("typeface", info->font.getTypeface());
407         jrun.set("typeface",    emscripten::val::null());
408         jrun.set("size",        info->font.getSize());
409         if (info->font.getScaleX()) {
410             jrun.set("scaleX",  info->font.getScaleX());
411         }
412 
413         jrun.set("glyphs",   MakeTypedArray(N,  info->glyphs));
414         jrun.set("offsets",  MakeTypedArray(N1, info->utf8Starts));
415 
416         // we need to modify the positions, so make a temp copy
417         AutoSTMalloc<32, SkPoint> positions(N1);
418         for (int i = 0; i < N; ++i) {
419             positions.get()[i] = info->positions[i] + info->origin;
420         }
421         positions.get()[N] = { info->advanceX, positions.get()[N - 1].fY };
422         jrun.set("positions", MakeTypedArray(N1*2, (const float*)positions.get()));
423 
424         jruns.call<void>("push", jrun);
425 
426         // update accum
427         {   SkFontMetrics fm;
428             info->font.getMetrics(&fm);
429 
430             accum.minAscent  = std::min(accum.minAscent,  fm.fAscent);
431             accum.maxDescent = std::max(accum.maxDescent, fm.fDescent);
432             accum.baseline   = info->origin.fY;
433 
434             accum.minOffset  = std::min(accum.minOffset,  info->utf8Starts[0]);
435             accum.maxOffset  = std::max(accum.maxOffset,  info->utf8Starts[N]);
436         }
437 
438     });
439     return jlines;
440 }
441 
convertArrayU32(WASMPointerU32 array,size_t count)442 std::vector<SkUnicode::Position> convertArrayU32(WASMPointerU32 array, size_t count) {
443     std::vector<size_t> vec;
444     vec.resize(count);
445     SkUnicode::Position* data = reinterpret_cast<SkUnicode::Position*>(array);
446     std::memcpy(vec.data(), data, count * sizeof(size_t));
447     return vec;
448 }
449 
EMSCRIPTEN_BINDINGS(Paragraph)450 EMSCRIPTEN_BINDINGS(Paragraph) {
451 
452     class_<para::Paragraph>("Paragraph")
453         .function("didExceedMaxLines", ¶::Paragraph::didExceedMaxLines)
454         .function("getAlphabeticBaseline", ¶::Paragraph::getAlphabeticBaseline)
455         .function("getGlyphPositionAtCoordinate", ¶::Paragraph::getGlyphPositionAtCoordinate)
456         .function("getHeight", ¶::Paragraph::getHeight)
457         .function("getIdeographicBaseline", ¶::Paragraph::getIdeographicBaseline)
458         .function("getLineMetrics", &GetLineMetrics)
459         .function("getLongestLine", ¶::Paragraph::getLongestLine)
460         .function("getMaxIntrinsicWidth", ¶::Paragraph::getMaxIntrinsicWidth)
461         .function("getMaxWidth", ¶::Paragraph::getMaxWidth)
462         .function("getMinIntrinsicWidth", ¶::Paragraph::getMinIntrinsicWidth)
463         .function("_getRectsForPlaceholders", &GetRectsForPlaceholders)
464         .function("_getRectsForRange", &GetRectsForRange)
465         .function("getShapedLines", &GetShapedLines)
466         .function("getWordBoundary", ¶::Paragraph::getWordBoundary)
467         .function("layout", ¶::Paragraph::layout);
468 
469     class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
470             .class_function(
471                     "_Make",
472                     optional_override([](SimpleParagraphStyle style, sk_sp<SkFontMgr> fontMgr)
473                                               -> std::unique_ptr<para::ParagraphBuilderImpl> {
474                         auto fc = sk_make_sp<para::FontCollection>();
475                         fc->setDefaultFontManager(fontMgr);
476                         fc->enableFontFallback();
477                         auto ps = toParagraphStyle(style);
478                         auto pb = para::ParagraphBuilderImpl::make(ps, fc);
479                         return std::unique_ptr<para::ParagraphBuilderImpl>(
480                                 static_cast<para::ParagraphBuilderImpl*>(pb.release()));
481                     }),
482                     allow_raw_pointers())
483             .class_function(
484                     "_MakeFromFontProvider",
485                     optional_override([](SimpleParagraphStyle style,
486                                          sk_sp<para::TypefaceFontProvider> fontProvider)
487                                               -> std::unique_ptr<para::ParagraphBuilderImpl> {
488                         auto fc = sk_make_sp<para::FontCollection>();
489                         fc->setDefaultFontManager(fontProvider);
490                         fc->enableFontFallback();
491                         auto ps = toParagraphStyle(style);
492                         auto pb = para::ParagraphBuilderImpl::make(ps, fc);
493                         return std::unique_ptr<para::ParagraphBuilderImpl>(
494                                 static_cast<para::ParagraphBuilderImpl*>(pb.release()));
495                     }),
496                     allow_raw_pointers())
497             .class_function(
498                     "_ShapeText",
499                     optional_override([](JSString jtext, JSArray jruns, float width) -> JSArray {
500                 std::string textStorage = jtext.as<std::string>();
501                 const char* text = textStorage.data();
502                 size_t      textCount = textStorage.size();
503 
504                 auto fc = sk_make_sp<para::FontCollection>();
505                 fc->setDefaultFontManager(SkFontMgr::RefDefault());
506                 fc->enableFontFallback();
507 
508                 para::ParagraphStyle pstyle;
509                 {
510                     // For the most part this is ignored, since we set an explicit TextStyle
511                     // for all of our text runs, but it is required by SkParagraph.
512                     para::TextStyle style;
513                     style.setFontFamilies({SkString("sans-serif")});
514                     style.setFontSize(32);
515                     pstyle.setTextStyle(style);
516                 }
517 
518                 auto pb = para::ParagraphBuilder::make(pstyle, fc);
519 
520                 // tease apart the FontBlock runs
521                 size_t runCount = jruns["length"].as<size_t>();
522                 for (size_t i = 0; i < runCount; ++i) {
523                     emscripten::val r = jruns[i];
524 
525                     para::TextStyle style;
526                     style.setTypeface(r["typeface"].as< sk_sp<SkTypeface> >());
527                     style.setFontSize(r["size"].as<float>());
528 
529                     const size_t subTextCount = r["length"].as<size_t>();
530                     if (subTextCount > textCount) {
531                         return emscripten::val("block runs exceed text length!");
532                     }
533 
534                     pb->pushStyle(style);
535                     pb->addText(text, subTextCount);
536                     pb->pop();
537 
538                     text += subTextCount;
539                     textCount -= subTextCount;
540                 }
541                 if (textCount != 0) {
542                     return emscripten::val("Didn't have enough block runs to cover text");
543                 }
544 
545                 auto pa = pb->Build();
546                 pa->layout(width);
547 
548                 // workaround until this is fixed in SkParagraph
549                 {
550                     SkPictureRecorder rec;
551                     pa->paint(rec.beginRecording({0,0,9999,9999}), 0, 0);
552                 }
553                 return GetShapedLines(*pa);
554             }),
555             allow_raw_pointers())
556             .function("addText",
557                       optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
558                           return self.addText(text.c_str(), text.length());
559                       }))
560             .function("build", ¶::ParagraphBuilderImpl::Build, allow_raw_pointers())
561             .function("pop", ¶::ParagraphBuilderImpl::pop)
562             .function("reset", ¶::ParagraphBuilderImpl::Reset, allow_raw_pointers())
563             .function("_pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
564                                                          SimpleTextStyle textStyle) {
565                           auto ts = toTextStyle(textStyle);
566                           self.pushStyle(ts);
567                       }))
568             // A method of pushing a textStyle with paints instead of colors for foreground and
569             // background. Since SimpleTextStyle is a value object, it cannot contain paints, which
570             // are not primitives. This binding is here to accept them. Any color that is specified
571             // in the textStyle is overridden.
572             .function("_pushPaintStyle",
573                       optional_override([](para::ParagraphBuilderImpl& self,
574                                            SimpleTextStyle textStyle, SkPaint foreground,
575                                            SkPaint background) {
576                           auto ts = toTextStyle(textStyle);
577                           ts.setForegroundColor(foreground);
578                           ts.setBackgroundColor(background);
579                           self.pushStyle(ts);
580                       }))
581             .function("_addPlaceholder", optional_override([](para::ParagraphBuilderImpl& self,
582                                                               SkScalar width,
583                                                               SkScalar height,
584                                                               para::PlaceholderAlignment alignment,
585                                                               para::TextBaseline baseline,
586                                                               SkScalar offset) {
587                           para::PlaceholderStyle ps(width, height, alignment, baseline, offset);
588                           self.addPlaceholder(ps);
589                       }))
590             .function("getText",
591                       optional_override([](para::ParagraphBuilderImpl& self) -> JSString {
592                           auto text = self.getText();
593                           return emscripten::val(std::string(text.data(), text.size()).c_str());
594                       }))
595             .function("_setWordsUtf8",
596                       optional_override([](para::ParagraphBuilderImpl& self,
597                                            WASMPointerU32 clientWords, size_t wordsNum) {
598                       self.setWordsUtf8(convertArrayU32(clientWords, wordsNum));
599                   }))
600             .function("_setWordsUtf16",
601                       optional_override([](para::ParagraphBuilderImpl& self,
602                                            WASMPointerU32 clientWords, size_t wordsNum) {
603                       self.setWordsUtf16(convertArrayU32(clientWords, wordsNum));
604                   }))
605             .function("_setGraphemeBreaksUtf8",
606                       optional_override([](para::ParagraphBuilderImpl& self,
607                                            WASMPointerU32 clientGraphemes, size_t graphemesNum) {
608                       self.setGraphemeBreaksUtf8(convertArrayU32(clientGraphemes, graphemesNum));
609                   }))
610             .function("_setGraphemeBreaksUtf16",
611                       optional_override([](para::ParagraphBuilderImpl& self,
612                                            WASMPointerU32 clientGraphemes, size_t graphemesNum) {
613                       self.setGraphemeBreaksUtf16(convertArrayU32(clientGraphemes, graphemesNum));
614                   }))
615             .function("_setLineBreaksUtf8",
616                       optional_override([](para::ParagraphBuilderImpl& self,
617                                            WASMPointerU32 clientLineBreaks, size_t lineBreaksNum) {
618                       SkUnicode::Position* lineBreakData = reinterpret_cast<SkUnicode::Position*>(clientLineBreaks);
619                       std::vector<SkUnicode::LineBreakBefore> lineBreaks;
620                       for (size_t i = 0; i < lineBreaksNum; i += 2) {
621                           auto pos = lineBreakData[i];
622                           auto breakType = lineBreakData[i+1];
623                           if (breakType == 0) {
624                               lineBreaks.emplace_back(pos, SkUnicode::LineBreakType::kSoftLineBreak);
625                           } else {
626                               lineBreaks.emplace_back(pos, SkUnicode::LineBreakType::kHardLineBreak);
627                           }
628                       }
629                       self.setLineBreaksUtf8(std::move(lineBreaks));
630                   }))
631             .function("_setLineBreaksUtf16",
632                       optional_override([](para::ParagraphBuilderImpl& self,
633                                            WASMPointerU32 clientLineBreaks, size_t lineBreaksNum) {
634                       SkUnicode::Position* lineBreakData = reinterpret_cast<SkUnicode::Position*>(clientLineBreaks);
635                       std::vector<SkUnicode::LineBreakBefore> lineBreaks;
636                       for (size_t i = 0; i < lineBreaksNum; i += 2) {
637                           auto pos = lineBreakData[i];
638                           auto breakType = lineBreakData[i+1];
639                           if (breakType == 0) {
640                               lineBreaks.emplace_back(pos, SkUnicode::LineBreakType::kSoftLineBreak);
641                           } else {
642                               lineBreaks.emplace_back(pos, SkUnicode::LineBreakType::kHardLineBreak);
643                           }
644                       }
645                       self.setLineBreaksUtf16(std::move(lineBreaks));
646                   }));
647 
648     class_<para::TypefaceFontProvider, base<SkFontMgr>>("TypefaceFontProvider")
649       .smart_ptr<sk_sp<para::TypefaceFontProvider>>("sk_sp<TypefaceFontProvider>")
650       .class_function("Make", optional_override([]()-> sk_sp<para::TypefaceFontProvider> {
651           return sk_make_sp<para::TypefaceFontProvider>();
652       }))
653       .function("_registerFont", optional_override([](para::TypefaceFontProvider& self,
654                                                       sk_sp<SkTypeface> typeface,
655                                                       WASMPointerU8 familyPtr) {
656           const char* fPtr = reinterpret_cast<const char*>(familyPtr);
657           SkString fStr(fPtr);
658           self.registerTypeface(typeface, fStr);
659       }), allow_raw_pointers());
660 
661 
662     // These value objects make it easier to send data across the wire.
663     value_object<para::PositionWithAffinity>("PositionWithAffinity")
664         .field("pos",      ¶::PositionWithAffinity::position)
665         .field("affinity", ¶::PositionWithAffinity::affinity);
666 
667     value_object<SimpleFontStyle>("FontStyle")
668         .field("slant",     &SimpleFontStyle::slant)
669         .field("weight",    &SimpleFontStyle::weight)
670         .field("width",     &SimpleFontStyle::width);
671 
672     value_object<SimpleParagraphStyle>("ParagraphStyle")
673         .field("disableHinting",       &SimpleParagraphStyle::disableHinting)
674         .field("_ellipsisPtr",         &SimpleParagraphStyle::ellipsisPtr)
675         .field("_ellipsisLen",         &SimpleParagraphStyle::ellipsisLen)
676         .field("heightMultiplier",     &SimpleParagraphStyle::heightMultiplier)
677         .field("maxLines",             &SimpleParagraphStyle::maxLines)
678         .field("replaceTabCharacters", &SimpleParagraphStyle::replaceTabCharacters)
679         .field("textAlign",            &SimpleParagraphStyle::textAlign)
680         .field("textDirection",        &SimpleParagraphStyle::textDirection)
681         .field("textHeightBehavior",   &SimpleParagraphStyle::textHeightBehavior)
682         .field("textStyle",            &SimpleParagraphStyle::textStyle)
683         .field("strutStyle",           &SimpleParagraphStyle::strutStyle);
684 
685     value_object<SimpleStrutStyle>("StrutStyle")
686         .field("_fontFamiliesPtr", &SimpleStrutStyle::fontFamiliesPtr)
687         .field("_fontFamiliesLen", &SimpleStrutStyle::fontFamiliesLen)
688         .field("strutEnabled",     &SimpleStrutStyle::strutEnabled)
689         .field("fontSize",         &SimpleStrutStyle::fontSize)
690         .field("fontStyle",        &SimpleStrutStyle::fontStyle)
691         .field("heightMultiplier", &SimpleStrutStyle::heightMultiplier)
692         .field("halfLeading",      &SimpleStrutStyle::halfLeading)
693         .field("leading",          &SimpleStrutStyle::leading)
694         .field("forceStrutHeight", &SimpleStrutStyle::forceStrutHeight);
695 
696     value_object<SimpleTextStyle>("TextStyle")
697         .field("_colorPtr",             &SimpleTextStyle::colorPtr)
698         .field("_foregroundColorPtr",   &SimpleTextStyle::foregroundColorPtr)
699         .field("_backgroundColorPtr",   &SimpleTextStyle::backgroundColorPtr)
700         .field("decoration",            &SimpleTextStyle::decoration)
701         .field("decorationThickness",   &SimpleTextStyle::decorationThickness)
702         .field("_decorationColorPtr",   &SimpleTextStyle::decorationColorPtr)
703         .field("decorationStyle",       &SimpleTextStyle::decorationStyle)
704         .field("_fontFamiliesPtr",      &SimpleTextStyle::fontFamiliesPtr)
705         .field("_fontFamiliesLen",      &SimpleTextStyle::fontFamiliesLen)
706         .field("fontSize",              &SimpleTextStyle::fontSize)
707         .field("letterSpacing",         &SimpleTextStyle::letterSpacing)
708         .field("wordSpacing",           &SimpleTextStyle::wordSpacing)
709         .field("heightMultiplier",      &SimpleTextStyle::heightMultiplier)
710         .field("halfLeading",           &SimpleTextStyle::halfLeading)
711         .field("_localePtr",            &SimpleTextStyle::localePtr)
712         .field("_localeLen",            &SimpleTextStyle::localeLen)
713         .field("fontStyle",             &SimpleTextStyle::fontStyle)
714         .field("_shadowLen",            &SimpleTextStyle::shadowLen)
715         .field("_shadowColorsPtr",      &SimpleTextStyle::shadowColorsPtr)
716         .field("_shadowOffsetsPtr",     &SimpleTextStyle::shadowOffsetsPtr)
717         .field("_shadowBlurRadiiPtr",   &SimpleTextStyle::shadowBlurRadiiPtr)
718         .field("_fontFeatureLen",       &SimpleTextStyle::fontFeatureLen)
719         .field("_fontFeatureNamesPtr",  &SimpleTextStyle::fontFeatureNamesPtr)
720         .field("_fontFeatureValuesPtr", &SimpleTextStyle::fontFeatureValuesPtr)
721         .field("_fontVariationLen",     &SimpleTextStyle::fontVariationLen)
722         .field("_fontVariationAxesPtr", &SimpleTextStyle::fontVariationAxesPtr)
723         .field("_fontVariationValuesPtr", &SimpleTextStyle::fontVariationValuesPtr);
724 
725     // The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
726     // with the type we are using.
727     // TODO(kjlubick) make this a typedarray.
728     value_object<para::SkRange<size_t>>("URange")
729         .field("start",    ¶::SkRange<size_t>::start)
730         .field("end",      ¶::SkRange<size_t>::end);
731 
732     // TextDecoration should be a const because they can be combined
733     constant("NoDecoration", int(para::TextDecoration::kNoDecoration));
734     constant("UnderlineDecoration", int(para::TextDecoration::kUnderline));
735     constant("OverlineDecoration", int(para::TextDecoration::kOverline));
736     constant("LineThroughDecoration", int(para::TextDecoration::kLineThrough));
737 }
738