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