blob: f5e149acf58ae3cfe6b89811b791c83c01a66002 [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"
16#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
17#include "modules/skparagraph/src/ParagraphImpl.h"
18
19#include <string>
20#include <vector>
21
22#include <emscripten.h>
23#include <emscripten/bind.h>
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040024#include "modules/canvaskit/WasmCommon.h"
Kevin Lubick369f6a52019-10-03 11:22:08 -040025
26using namespace emscripten;
27
28namespace para = skia::textlayout;
29
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040030SkColor4f toSkColor4f(uintptr_t /* float* */ cPtr) {
31 float* fourFloats = reinterpret_cast<float*>(cPtr);
32 SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
33 return color;
34}
35
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040036struct SimpleFontStyle {
37 SkFontStyle::Slant slant;
38 SkFontStyle::Weight weight;
39 SkFontStyle::Width width;
40};
41
Kevin Lubick369f6a52019-10-03 11:22:08 -040042struct SimpleTextStyle {
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040043 uintptr_t /* float* */ colorPtr;
44 uintptr_t /* float* */ foregroundColorPtr;
45 uintptr_t /* float* */ backgroundColorPtr;
Kevin Lubick369f6a52019-10-03 11:22:08 -040046 uint8_t decoration;
Kevin Lubick369f6a52019-10-03 11:22:08 -040047 SkScalar decorationThickness;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040048 SkScalar fontSize;
49 SimpleFontStyle fontStyle;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040050
Kevin Lubick0c8884b2020-05-14 08:27:53 -040051 uintptr_t /* const char** */ fontFamiliesPtr;
52 int fontFamiliesLen;
Kevin Lubick369f6a52019-10-03 11:22:08 -040053};
54
55para::TextStyle toTextStyle(const SimpleTextStyle& s) {
56 para::TextStyle ts;
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040057
58 // textstyle.color doesn't support a 4f color, however the foreground and background fields below do.
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040059 ts.setColor(toSkColor4f(s.colorPtr).toSkColor());
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040060
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040061 // It is functionally important that these paints be unset when no value was provided.
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040062 if (s.foregroundColorPtr) {
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040063 SkPaint p1;
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040064 p1.setColor4f(toSkColor4f(s.foregroundColorPtr));
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040065 ts.setForegroundColor(p1);
Robert Phillipscb77eab2020-03-24 14:19:40 +000066 }
Kevin Lubick369f6a52019-10-03 11:22:08 -040067
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040068 if (s.backgroundColorPtr) {
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040069 SkPaint p2;
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -040070 p2.setColor4f(toSkColor4f(s.backgroundColorPtr));
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040071 ts.setBackgroundColor(p2);
Robert Phillipscb77eab2020-03-24 14:19:40 +000072 }
Kevin Lubick369f6a52019-10-03 11:22:08 -040073
74 if (s.fontSize != 0) {
75 ts.setFontSize(s.fontSize);
76 }
77
78 ts.setDecoration(para::TextDecoration(s.decoration));
79 if (s.decorationThickness != 0) {
80 ts.setDecorationThicknessMultiplier(s.decorationThickness);
81 }
82
Kevin Lubick0c8884b2020-05-14 08:27:53 -040083 const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
84 if (s.fontFamiliesLen > 0 && fontFamilies != nullptr) {
Kevin Lubick369f6a52019-10-03 11:22:08 -040085 std::vector<SkString> ff;
Kevin Lubick0c8884b2020-05-14 08:27:53 -040086 for (int i = 0; i < s.fontFamiliesLen; i++) {
Kevin Lubick369f6a52019-10-03 11:22:08 -040087 ff.emplace_back(fontFamilies[i]);
88 }
89 ts.setFontFamilies(ff);
90 }
91
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040092 SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
93 ts.setFontStyle(fs);
94
Kevin Lubick369f6a52019-10-03 11:22:08 -040095 return ts;
96}
97
98struct SimpleParagraphStyle {
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040099 bool disableHinting;
100 uintptr_t /* const char* */ ellipsisPtr;
101 size_t ellipsisLen;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400102 SkScalar heightMultiplier;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400103 size_t maxLines;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400104 para::TextAlign textAlign;
105 para::TextDirection textDirection;
106 SimpleTextStyle textStyle;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400107};
108
109para::ParagraphStyle toParagraphStyle(const SimpleParagraphStyle& s) {
110 para::ParagraphStyle ps;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400111 if (s.disableHinting) {
112 ps.turnHintingOff();
113 }
114
115 if (s.ellipsisLen > 0) {
116 const char* ellipsisPtr = reinterpret_cast<const char*>(s.ellipsisPtr);
117 SkString eStr(ellipsisPtr, s.ellipsisLen);
118 ps.setEllipsis(eStr);
119 }
120 ps.setTextAlign(s.textAlign);
121 ps.setTextDirection(s.textDirection);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400122 auto ts = toTextStyle(s.textStyle);
123 ps.setTextStyle(ts);
124 if (s.heightMultiplier != 0) {
125 ps.setHeight(s.heightMultiplier);
126 }
Kevin Lubick369f6a52019-10-03 11:22:08 -0400127 if (s.maxLines != 0) {
128 ps.setMaxLines(s.maxLines);
129 }
130 return ps;
131}
132
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500133struct SimpleTextBox {
134 SkRect rect;
135 // This isn't the most efficient way to represent this, but it is much easier to keep
136 // everything as floats when unpacking on the JS side.
137 // 0.0 = RTL, 1.0 = LTr
138 SkScalar direction;
139};
140
Kevin Lubick369f6a52019-10-03 11:22:08 -0400141Float32Array GetRectsForRange(para::ParagraphImpl& self, unsigned start, unsigned end,
142 para::RectHeightStyle heightStyle, para::RectWidthStyle widthStyle) {
143 std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500144 // Pack these text boxes into an array of n groups of 5 SkScalar (floats)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400145 if (!boxes.size()) {
146 return emscripten::val::null();
147 }
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500148 SimpleTextBox* rects = new SimpleTextBox[boxes.size()];
Kevin Lubick369f6a52019-10-03 11:22:08 -0400149 for (int i = 0; i< boxes.size(); i++) {
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500150 rects[i].rect = boxes[i].rect;
151 if (boxes[i].direction == para::TextDirection::kRtl) {
152 rects[i].direction = 0;
153 } else {
154 rects[i].direction = 1;
155 }
Kevin Lubick369f6a52019-10-03 11:22:08 -0400156 }
157 float* fPtr = reinterpret_cast<float*>(rects);
158 // Of note: now that we have cast rects to float*, emscripten is smart enough to wrap this
159 // into a Float32Array for us.
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500160 return Float32Array(typed_memory_view(boxes.size()*5, fPtr));
Kevin Lubick369f6a52019-10-03 11:22:08 -0400161}
162
163EMSCRIPTEN_BINDINGS(Paragraph) {
164
165 class_<para::Paragraph>("Paragraph");
166
167 // This "base<>" tells Emscripten that ParagraphImpl is a Paragraph and can get substituted
168 // in properly in drawParagraph. However, Emscripten will not let us bind pure virtual methods
Kevin Lubick04912672019-11-15 14:48:55 -0500169 // so we have to "expose" the ParagraphImpl in those cases.
Kevin Lubick369f6a52019-10-03 11:22:08 -0400170 class_<para::ParagraphImpl, base<para::Paragraph>>("ParagraphImpl")
Kevin Lubick04912672019-11-15 14:48:55 -0500171 .function("didExceedMaxLines", &para::Paragraph::didExceedMaxLines)
172 .function("getAlphabeticBaseline", &para::Paragraph::getAlphabeticBaseline)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400173 .function("getGlyphPositionAtCoordinate", &para::ParagraphImpl::getGlyphPositionAtCoordinate)
Kevin Lubick04912672019-11-15 14:48:55 -0500174 .function("getHeight", &para::Paragraph::getHeight)
175 .function("getIdeographicBaseline", &para::Paragraph::getIdeographicBaseline)
176 .function("getLongestLine", &para::Paragraph::getLongestLine)
177 .function("getMaxIntrinsicWidth", &para::Paragraph::getMaxIntrinsicWidth)
178 .function("getMaxWidth", &para::Paragraph::getMaxWidth)
179 .function("getMinIntrinsicWidth", &para::Paragraph::getMinIntrinsicWidth)
180 .function("_getRectsForRange", &GetRectsForRange)
181 .function("getWordBoundary", &para::ParagraphImpl::getWordBoundary)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400182 .function("layout", &para::ParagraphImpl::layout);
183
184 class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -0400185 .class_function("_Make", optional_override([](SimpleParagraphStyle style,
Kevin Lubick369f6a52019-10-03 11:22:08 -0400186 sk_sp<SkFontMgr> fontMgr)-> para::ParagraphBuilderImpl {
187 auto fc = sk_make_sp<para::FontCollection>();
188 fc->setDefaultFontManager(fontMgr);
189 auto ps = toParagraphStyle(style);
190 para::ParagraphBuilderImpl pbi(ps, fc);
191 return pbi;
192 }), allow_raw_pointers())
193 .function("addText", optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
194 return self.addText(text.c_str(), text.length());
195 }))
196 .function("build", &para::ParagraphBuilderImpl::Build, allow_raw_pointers())
197 .function("pop", &para::ParagraphBuilderImpl::pop)
Nathaniel Nifong1bedbeb2020-05-04 16:46:17 -0400198 .function("_pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
Kevin Lubick369f6a52019-10-03 11:22:08 -0400199 SimpleTextStyle textStyle) {
200 auto ts = toTextStyle(textStyle);
201 self.pushStyle(ts);
Nathaniel Nifonge09b3142020-08-04 09:06:54 -0400202 }))
203 // A method of pushing a textStyle with paints instead of colors for foreground and
204 // 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.
205 .function("_pushPaintStyle", optional_override([](para::ParagraphBuilderImpl& self,
206 SimpleTextStyle textStyle, SkPaint foreground, SkPaint background) {
207 auto ts = toTextStyle(textStyle);
208 ts.setForegroundColor(foreground);
209 ts.setBackgroundColor(background);
210 self.pushStyle(ts);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400211 }));
212
213
214 enum_<para::Affinity>("Affinity")
215 .value("Upstream", para::Affinity::kUpstream)
216 .value("Downstream", para::Affinity::kDownstream);
217
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400218 enum_<SkFontStyle::Slant>("FontSlant")
219 .value("Upright", SkFontStyle::Slant::kUpright_Slant)
220 .value("Italic", SkFontStyle::Slant::kItalic_Slant)
221 .value("Oblique", SkFontStyle::Slant::kOblique_Slant);
222
223 enum_<SkFontStyle::Weight>("FontWeight")
224 .value("Invisible", SkFontStyle::Weight::kInvisible_Weight)
225 .value("Thin", SkFontStyle::Weight::kThin_Weight)
226 .value("ExtraLight", SkFontStyle::Weight::kExtraLight_Weight)
227 .value("Light", SkFontStyle::Weight::kLight_Weight)
228 .value("Normal", SkFontStyle::Weight::kNormal_Weight)
229 .value("Medium", SkFontStyle::Weight::kMedium_Weight)
230 .value("SemiBold", SkFontStyle::Weight::kSemiBold_Weight)
231 .value("Bold", SkFontStyle::Weight::kBold_Weight)
232 .value("ExtraBold", SkFontStyle::Weight::kExtraBold_Weight)
233 .value("Black" , SkFontStyle::Weight::kBlack_Weight)
234 .value("ExtraBlack", SkFontStyle::Weight::kExtraBlack_Weight);
235
236 enum_<SkFontStyle::Width>("FontWidth")
237 .value("UltraCondensed", SkFontStyle::Width::kUltraCondensed_Width)
238 .value("ExtraCondensed", SkFontStyle::Width::kExtraCondensed_Width)
239 .value("Condensed", SkFontStyle::Width::kCondensed_Width)
240 .value("SemiCondensed", SkFontStyle::Width::kSemiCondensed_Width)
241 .value("Normal", SkFontStyle::Width::kNormal_Width)
242 .value("SemiExpanded", SkFontStyle::Width::kSemiExpanded_Width)
243 .value("Expanded", SkFontStyle::Width::kExpanded_Width)
244 .value("ExtraExpanded", SkFontStyle::Width::kExtraExpanded_Width)
245 .value("UltraExpanded", SkFontStyle::Width::kUltraExpanded_Width);
246
Kevin Lubick369f6a52019-10-03 11:22:08 -0400247 enum_<para::RectHeightStyle>("RectHeightStyle")
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500248 .value("Tight", para::RectHeightStyle::kTight)
249 .value("Max", para::RectHeightStyle::kMax)
250 .value("IncludeLineSpacingMiddle", para::RectHeightStyle::kIncludeLineSpacingMiddle)
251 .value("IncludeLineSpacingTop", para::RectHeightStyle::kIncludeLineSpacingTop)
252 .value("IncludeLineSpacingBottom", para::RectHeightStyle::kIncludeLineSpacingBottom);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400253
254 enum_<para::RectWidthStyle>("RectWidthStyle")
255 .value("Tight", para::RectWidthStyle::kTight)
256 .value("Max", para::RectWidthStyle::kMax);
257
258 enum_<para::TextAlign>("TextAlign")
259 .value("Left", para::TextAlign::kLeft)
260 .value("Right", para::TextAlign::kRight)
261 .value("Center", para::TextAlign::kCenter)
262 .value("Justify", para::TextAlign::kJustify)
263 .value("Start", para::TextAlign::kStart)
264 .value("End", para::TextAlign::kEnd);
265
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400266 enum_<para::TextDirection>("TextDirection")
267 .value("LTR", para::TextDirection::kLtr)
268 .value("RTL", para::TextDirection::kRtl);
269
Kevin Lubick369f6a52019-10-03 11:22:08 -0400270
271 value_object<para::PositionWithAffinity>("PositionWithAffinity")
272 .field("pos", &para::PositionWithAffinity::position)
273 .field("affinity", &para::PositionWithAffinity::affinity);
274
Kevin Lubick04912672019-11-15 14:48:55 -0500275 value_object<SimpleFontStyle>("FontStyle")
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400276 .field("slant", &SimpleFontStyle::slant)
277 .field("weight", &SimpleFontStyle::weight)
278 .field("width", &SimpleFontStyle::width);
279
Kevin Lubick369f6a52019-10-03 11:22:08 -0400280 value_object<SimpleParagraphStyle>("ParagraphStyle")
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400281 .field("disableHinting", &SimpleParagraphStyle::disableHinting)
282 .field("_ellipsisPtr", &SimpleParagraphStyle::ellipsisPtr)
283 .field("_ellipsisLen", &SimpleParagraphStyle::ellipsisLen)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400284 .field("heightMultiplier", &SimpleParagraphStyle::heightMultiplier)
285 .field("maxLines", &SimpleParagraphStyle::maxLines)
286 .field("textAlign", &SimpleParagraphStyle::textAlign)
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400287 .field("textDirection", &SimpleParagraphStyle::textDirection)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400288 .field("textStyle", &SimpleParagraphStyle::textStyle);
289
290 value_object<SimpleTextStyle>("TextStyle")
Kevin Lubick0c8884b2020-05-14 08:27:53 -0400291 .field("_colorPtr", &SimpleTextStyle::colorPtr)
292 .field("_foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
293 .field("_backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400294 .field("decoration", &SimpleTextStyle::decoration)
295 .field("decorationThickness", &SimpleTextStyle::decorationThickness)
Kevin Lubick0c8884b2020-05-14 08:27:53 -0400296 .field("_fontFamiliesPtr", &SimpleTextStyle::fontFamiliesPtr)
297 .field("_fontFamiliesLen", &SimpleTextStyle::fontFamiliesLen)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400298 .field("fontSize", &SimpleTextStyle::fontSize)
Kevin Lubick0c8884b2020-05-14 08:27:53 -0400299 .field("fontStyle", &SimpleTextStyle::fontStyle);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400300
Kevin Lubick04912672019-11-15 14:48:55 -0500301 // The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
302 // with the type we are using.
303 value_object<para::SkRange<size_t>>("URange")
304 .field("start", &para::SkRange<size_t>::start)
305 .field("end", &para::SkRange<size_t>::end);
306
Kevin Lubick369f6a52019-10-03 11:22:08 -0400307 // TextDecoration should be a const because they can be combined
308 constant("NoDecoration", int(para::TextDecoration::kNoDecoration));
309 constant("UnderlineDecoration", int(para::TextDecoration::kUnderline));
310 constant("OverlineDecoration", int(para::TextDecoration::kOverline));
311 constant("LineThroughDecoration", int(para::TextDecoration::kLineThrough));
312}