blob: f5d59f352706f60f166413ee1035a05ef8c6196d [file] [log] [blame]
Kevin Lubick369f6a52019-10-03 11:22:08 -04001/*
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"
Kevin Lubickd3b1fe62019-10-21 10:50:26 -04009#include "include/core/SkFontStyle.h"
Kevin Lubick369f6a52019-10-03 11:22:08 -040010#include "include/core/SkString.h"
11
12#include "modules/skparagraph/include/DartTypes.h"
13#include "modules/skparagraph/include/Paragraph.h"
14#include "modules/skparagraph/include/ParagraphBuilder.h"
15#include "modules/skparagraph/include/TextStyle.h"
Harry Terkelsen10f019c2020-08-04 13:21:09 -070016#include "modules/skparagraph/include/TypefaceFontProvider.h"
Kevin Lubick369f6a52019-10-03 11:22:08 -040017#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>
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040025#include "modules/canvaskit/WasmCommon.h"
Kevin Lubick369f6a52019-10-03 11:22:08 -040026
27using namespace emscripten;
28
29namespace para = skia::textlayout;
30
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040031SkColor4f 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
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040037struct SimpleFontStyle {
38 SkFontStyle::Slant slant;
39 SkFontStyle::Weight weight;
40 SkFontStyle::Width width;
41};
42
Kevin Lubick369f6a52019-10-03 11:22:08 -040043struct SimpleTextStyle {
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040044 uintptr_t /* float* */ colorPtr;
45 uintptr_t /* float* */ foregroundColorPtr;
46 uintptr_t /* float* */ backgroundColorPtr;
Kevin Lubick369f6a52019-10-03 11:22:08 -040047 uint8_t decoration;
Kevin Lubick369f6a52019-10-03 11:22:08 -040048 SkScalar decorationThickness;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040049 SkScalar fontSize;
50 SimpleFontStyle fontStyle;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040051
Kevin Lubick0c8884b2020-05-14 08:27:53 -040052 uintptr_t /* const char** */ fontFamiliesPtr;
53 int fontFamiliesLen;
Kevin Lubick369f6a52019-10-03 11:22:08 -040054};
55
56para::TextStyle toTextStyle(const SimpleTextStyle& s) {
57 para::TextStyle ts;
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040058
59 // textstyle.color doesn't support a 4f color, however the foreground and background fields below do.
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040060 ts.setColor(toSkColor4f(s.colorPtr).toSkColor());
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040061
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040062 // It is functionally important that these paints be unset when no value was provided.
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040063 if (s.foregroundColorPtr) {
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040064 SkPaint p1;
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040065 p1.setColor4f(toSkColor4f(s.foregroundColorPtr));
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040066 ts.setForegroundColor(p1);
Robert Phillipscb77eab2020-03-24 14:19:40 +000067 }
Kevin Lubick369f6a52019-10-03 11:22:08 -040068
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040069 if (s.backgroundColorPtr) {
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040070 SkPaint p2;
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040071 p2.setColor4f(toSkColor4f(s.backgroundColorPtr));
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040072 ts.setBackgroundColor(p2);
Robert Phillipscb77eab2020-03-24 14:19:40 +000073 }
Kevin Lubick369f6a52019-10-03 11:22:08 -040074
75 if (s.fontSize != 0) {
76 ts.setFontSize(s.fontSize);
77 }
78
79 ts.setDecoration(para::TextDecoration(s.decoration));
80 if (s.decorationThickness != 0) {
81 ts.setDecorationThicknessMultiplier(s.decorationThickness);
82 }
83
Kevin Lubick0c8884b2020-05-14 08:27:53 -040084 const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
85 if (s.fontFamiliesLen > 0 && fontFamilies != nullptr) {
Kevin Lubick369f6a52019-10-03 11:22:08 -040086 std::vector<SkString> ff;
Kevin Lubick0c8884b2020-05-14 08:27:53 -040087 for (int i = 0; i < s.fontFamiliesLen; i++) {
Kevin Lubick369f6a52019-10-03 11:22:08 -040088 ff.emplace_back(fontFamilies[i]);
89 }
90 ts.setFontFamilies(ff);
91 }
92
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040093 SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
94 ts.setFontStyle(fs);
95
Kevin Lubick369f6a52019-10-03 11:22:08 -040096 return ts;
97}
98
99struct SimpleParagraphStyle {
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400100 bool disableHinting;
101 uintptr_t /* const char* */ ellipsisPtr;
102 size_t ellipsisLen;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400103 SkScalar heightMultiplier;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400104 size_t maxLines;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400105 para::TextAlign textAlign;
106 para::TextDirection textDirection;
107 SimpleTextStyle textStyle;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400108};
109
110para::ParagraphStyle toParagraphStyle(const SimpleParagraphStyle& s) {
111 para::ParagraphStyle ps;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400112 if (s.disableHinting) {
113 ps.turnHintingOff();
114 }
115
116 if (s.ellipsisLen > 0) {
117 const char* ellipsisPtr = reinterpret_cast<const char*>(s.ellipsisPtr);
118 SkString eStr(ellipsisPtr, s.ellipsisLen);
119 ps.setEllipsis(eStr);
120 }
121 ps.setTextAlign(s.textAlign);
122 ps.setTextDirection(s.textDirection);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400123 auto ts = toTextStyle(s.textStyle);
124 ps.setTextStyle(ts);
125 if (s.heightMultiplier != 0) {
126 ps.setHeight(s.heightMultiplier);
127 }
Kevin Lubick369f6a52019-10-03 11:22:08 -0400128 if (s.maxLines != 0) {
129 ps.setMaxLines(s.maxLines);
130 }
131 return ps;
132}
133
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500134struct SimpleTextBox {
135 SkRect rect;
136 // This isn't the most efficient way to represent this, but it is much easier to keep
137 // everything as floats when unpacking on the JS side.
138 // 0.0 = RTL, 1.0 = LTr
139 SkScalar direction;
140};
141
Kevin Lubick369f6a52019-10-03 11:22:08 -0400142Float32Array GetRectsForRange(para::ParagraphImpl& self, unsigned start, unsigned end,
143 para::RectHeightStyle heightStyle, para::RectWidthStyle widthStyle) {
144 std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500145 // Pack these text boxes into an array of n groups of 5 SkScalar (floats)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400146 if (!boxes.size()) {
147 return emscripten::val::null();
148 }
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500149 SimpleTextBox* rects = new SimpleTextBox[boxes.size()];
Kevin Lubick369f6a52019-10-03 11:22:08 -0400150 for (int i = 0; i< boxes.size(); i++) {
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500151 rects[i].rect = boxes[i].rect;
152 if (boxes[i].direction == para::TextDirection::kRtl) {
153 rects[i].direction = 0;
154 } else {
155 rects[i].direction = 1;
156 }
Kevin Lubick369f6a52019-10-03 11:22:08 -0400157 }
158 float* fPtr = reinterpret_cast<float*>(rects);
159 // Of note: now that we have cast rects to float*, emscripten is smart enough to wrap this
160 // into a Float32Array for us.
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500161 return Float32Array(typed_memory_view(boxes.size()*5, fPtr));
Kevin Lubick369f6a52019-10-03 11:22:08 -0400162}
163
164EMSCRIPTEN_BINDINGS(Paragraph) {
165
166 class_<para::Paragraph>("Paragraph");
167
168 // This "base<>" tells Emscripten that ParagraphImpl is a Paragraph and can get substituted
169 // in properly in drawParagraph. However, Emscripten will not let us bind pure virtual methods
Kevin Lubick04912672019-11-15 14:48:55 -0500170 // so we have to "expose" the ParagraphImpl in those cases.
Kevin Lubick369f6a52019-10-03 11:22:08 -0400171 class_<para::ParagraphImpl, base<para::Paragraph>>("ParagraphImpl")
Kevin Lubick04912672019-11-15 14:48:55 -0500172 .function("didExceedMaxLines", &para::Paragraph::didExceedMaxLines)
173 .function("getAlphabeticBaseline", &para::Paragraph::getAlphabeticBaseline)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400174 .function("getGlyphPositionAtCoordinate", &para::ParagraphImpl::getGlyphPositionAtCoordinate)
Kevin Lubick04912672019-11-15 14:48:55 -0500175 .function("getHeight", &para::Paragraph::getHeight)
176 .function("getIdeographicBaseline", &para::Paragraph::getIdeographicBaseline)
177 .function("getLongestLine", &para::Paragraph::getLongestLine)
178 .function("getMaxIntrinsicWidth", &para::Paragraph::getMaxIntrinsicWidth)
179 .function("getMaxWidth", &para::Paragraph::getMaxWidth)
180 .function("getMinIntrinsicWidth", &para::Paragraph::getMinIntrinsicWidth)
181 .function("_getRectsForRange", &GetRectsForRange)
182 .function("getWordBoundary", &para::ParagraphImpl::getWordBoundary)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400183 .function("layout", &para::ParagraphImpl::layout);
184
185 class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -0400186 .class_function("_Make", optional_override([](SimpleParagraphStyle style,
Kevin Lubick369f6a52019-10-03 11:22:08 -0400187 sk_sp<SkFontMgr> fontMgr)-> para::ParagraphBuilderImpl {
188 auto fc = sk_make_sp<para::FontCollection>();
189 fc->setDefaultFontManager(fontMgr);
190 auto ps = toParagraphStyle(style);
191 para::ParagraphBuilderImpl pbi(ps, fc);
192 return pbi;
193 }), allow_raw_pointers())
Harry Terkelsen10f019c2020-08-04 13:21:09 -0700194 .class_function("_MakeFromFontProvider", optional_override([](SimpleParagraphStyle style,
195 sk_sp<para::TypefaceFontProvider> fontProvider)-> para::ParagraphBuilderImpl {
196 auto fc = sk_make_sp<para::FontCollection>();
197 fc->setDefaultFontManager(fontProvider);
198 auto ps = toParagraphStyle(style);
199 para::ParagraphBuilderImpl pbi(ps, fc);
200 return pbi;
201 }), allow_raw_pointers())
Kevin Lubick369f6a52019-10-03 11:22:08 -0400202 .function("addText", optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
203 return self.addText(text.c_str(), text.length());
204 }))
205 .function("build", &para::ParagraphBuilderImpl::Build, allow_raw_pointers())
206 .function("pop", &para::ParagraphBuilderImpl::pop)
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -0400207 .function("_pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
Kevin Lubick369f6a52019-10-03 11:22:08 -0400208 SimpleTextStyle textStyle) {
209 auto ts = toTextStyle(textStyle);
210 self.pushStyle(ts);
Nathaniel Nifonge09b3142020-08-04 09:06:54 -0400211 }))
212 // A method of pushing a textStyle with paints instead of colors for foreground and
213 // background. Since SimpleTextStyle is a value object, it cannot contain paints, which are not primitives. This binding is here to accept them. Any color that is specified in the textStyle is overridden.
214 .function("_pushPaintStyle", optional_override([](para::ParagraphBuilderImpl& self,
215 SimpleTextStyle textStyle, SkPaint foreground, SkPaint background) {
216 auto ts = toTextStyle(textStyle);
217 ts.setForegroundColor(foreground);
218 ts.setBackgroundColor(background);
219 self.pushStyle(ts);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400220 }));
221
Harry Terkelsen10f019c2020-08-04 13:21:09 -0700222 class_<para::TypefaceFontProvider, base<SkFontMgr>>("TypefaceFontProvider")
223 .smart_ptr<sk_sp<para::TypefaceFontProvider>>("sk_sp<TypefaceFontProvider>")
224 .class_function("Make", optional_override([]()-> sk_sp<para::TypefaceFontProvider> {
225 return sk_make_sp<para::TypefaceFontProvider>();
226 }))
227 .function("_registerFont", optional_override([](para::TypefaceFontProvider& self,
228 sk_sp<SkTypeface> typeface,
229 uintptr_t familyPtr) {
230 const char* fPtr = reinterpret_cast<const char*>(familyPtr);
231 SkString fStr(fPtr);
232 self.registerTypeface(typeface, fStr);
233 }), allow_raw_pointers());
234
Kevin Lubick369f6a52019-10-03 11:22:08 -0400235
236 enum_<para::Affinity>("Affinity")
237 .value("Upstream", para::Affinity::kUpstream)
238 .value("Downstream", para::Affinity::kDownstream);
239
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400240 enum_<SkFontStyle::Slant>("FontSlant")
241 .value("Upright", SkFontStyle::Slant::kUpright_Slant)
242 .value("Italic", SkFontStyle::Slant::kItalic_Slant)
243 .value("Oblique", SkFontStyle::Slant::kOblique_Slant);
244
245 enum_<SkFontStyle::Weight>("FontWeight")
246 .value("Invisible", SkFontStyle::Weight::kInvisible_Weight)
247 .value("Thin", SkFontStyle::Weight::kThin_Weight)
248 .value("ExtraLight", SkFontStyle::Weight::kExtraLight_Weight)
249 .value("Light", SkFontStyle::Weight::kLight_Weight)
250 .value("Normal", SkFontStyle::Weight::kNormal_Weight)
251 .value("Medium", SkFontStyle::Weight::kMedium_Weight)
252 .value("SemiBold", SkFontStyle::Weight::kSemiBold_Weight)
253 .value("Bold", SkFontStyle::Weight::kBold_Weight)
254 .value("ExtraBold", SkFontStyle::Weight::kExtraBold_Weight)
255 .value("Black" , SkFontStyle::Weight::kBlack_Weight)
256 .value("ExtraBlack", SkFontStyle::Weight::kExtraBlack_Weight);
257
258 enum_<SkFontStyle::Width>("FontWidth")
259 .value("UltraCondensed", SkFontStyle::Width::kUltraCondensed_Width)
260 .value("ExtraCondensed", SkFontStyle::Width::kExtraCondensed_Width)
261 .value("Condensed", SkFontStyle::Width::kCondensed_Width)
262 .value("SemiCondensed", SkFontStyle::Width::kSemiCondensed_Width)
263 .value("Normal", SkFontStyle::Width::kNormal_Width)
264 .value("SemiExpanded", SkFontStyle::Width::kSemiExpanded_Width)
265 .value("Expanded", SkFontStyle::Width::kExpanded_Width)
266 .value("ExtraExpanded", SkFontStyle::Width::kExtraExpanded_Width)
267 .value("UltraExpanded", SkFontStyle::Width::kUltraExpanded_Width);
268
Kevin Lubick369f6a52019-10-03 11:22:08 -0400269 enum_<para::RectHeightStyle>("RectHeightStyle")
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500270 .value("Tight", para::RectHeightStyle::kTight)
271 .value("Max", para::RectHeightStyle::kMax)
272 .value("IncludeLineSpacingMiddle", para::RectHeightStyle::kIncludeLineSpacingMiddle)
273 .value("IncludeLineSpacingTop", para::RectHeightStyle::kIncludeLineSpacingTop)
274 .value("IncludeLineSpacingBottom", para::RectHeightStyle::kIncludeLineSpacingBottom);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400275
276 enum_<para::RectWidthStyle>("RectWidthStyle")
277 .value("Tight", para::RectWidthStyle::kTight)
278 .value("Max", para::RectWidthStyle::kMax);
279
280 enum_<para::TextAlign>("TextAlign")
281 .value("Left", para::TextAlign::kLeft)
282 .value("Right", para::TextAlign::kRight)
283 .value("Center", para::TextAlign::kCenter)
284 .value("Justify", para::TextAlign::kJustify)
285 .value("Start", para::TextAlign::kStart)
286 .value("End", para::TextAlign::kEnd);
287
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400288 enum_<para::TextDirection>("TextDirection")
289 .value("LTR", para::TextDirection::kLtr)
290 .value("RTL", para::TextDirection::kRtl);
291
Kevin Lubick369f6a52019-10-03 11:22:08 -0400292
293 value_object<para::PositionWithAffinity>("PositionWithAffinity")
294 .field("pos", &para::PositionWithAffinity::position)
295 .field("affinity", &para::PositionWithAffinity::affinity);
296
Kevin Lubick04912672019-11-15 14:48:55 -0500297 value_object<SimpleFontStyle>("FontStyle")
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400298 .field("slant", &SimpleFontStyle::slant)
299 .field("weight", &SimpleFontStyle::weight)
300 .field("width", &SimpleFontStyle::width);
301
Kevin Lubick369f6a52019-10-03 11:22:08 -0400302 value_object<SimpleParagraphStyle>("ParagraphStyle")
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400303 .field("disableHinting", &SimpleParagraphStyle::disableHinting)
304 .field("_ellipsisPtr", &SimpleParagraphStyle::ellipsisPtr)
305 .field("_ellipsisLen", &SimpleParagraphStyle::ellipsisLen)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400306 .field("heightMultiplier", &SimpleParagraphStyle::heightMultiplier)
307 .field("maxLines", &SimpleParagraphStyle::maxLines)
308 .field("textAlign", &SimpleParagraphStyle::textAlign)
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400309 .field("textDirection", &SimpleParagraphStyle::textDirection)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400310 .field("textStyle", &SimpleParagraphStyle::textStyle);
311
312 value_object<SimpleTextStyle>("TextStyle")
Kevin Lubick0c8884b2020-05-14 08:27:53 -0400313 .field("_colorPtr", &SimpleTextStyle::colorPtr)
314 .field("_foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
315 .field("_backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400316 .field("decoration", &SimpleTextStyle::decoration)
317 .field("decorationThickness", &SimpleTextStyle::decorationThickness)
Kevin Lubick0c8884b2020-05-14 08:27:53 -0400318 .field("_fontFamiliesPtr", &SimpleTextStyle::fontFamiliesPtr)
319 .field("_fontFamiliesLen", &SimpleTextStyle::fontFamiliesLen)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400320 .field("fontSize", &SimpleTextStyle::fontSize)
Kevin Lubick0c8884b2020-05-14 08:27:53 -0400321 .field("fontStyle", &SimpleTextStyle::fontStyle);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400322
Kevin Lubick04912672019-11-15 14:48:55 -0500323 // The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
324 // with the type we are using.
325 value_object<para::SkRange<size_t>>("URange")
326 .field("start", &para::SkRange<size_t>::start)
327 .field("end", &para::SkRange<size_t>::end);
328
Kevin Lubick369f6a52019-10-03 11:22:08 -0400329 // TextDecoration should be a const because they can be combined
330 constant("NoDecoration", int(para::TextDecoration::kNoDecoration));
331 constant("UnderlineDecoration", int(para::TextDecoration::kUnderline));
332 constant("OverlineDecoration", int(para::TextDecoration::kOverline));
333 constant("LineThroughDecoration", int(para::TextDecoration::kLineThrough));
334}