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