blob: 7dc057a144a85ae4cb0690a1f85edb79df19f665 [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
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040030struct SimpleFontStyle {
31 SkFontStyle::Slant slant;
32 SkFontStyle::Weight weight;
33 SkFontStyle::Width width;
34};
35
Kevin Lubick369f6a52019-10-03 11:22:08 -040036struct SimpleTextStyle {
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040037 SimpleColor4f color;
38 SimpleColor4f foregroundColor;
39 SimpleColor4f backgroundColor;
Kevin Lubick369f6a52019-10-03 11:22:08 -040040 uint8_t decoration;
Kevin Lubick369f6a52019-10-03 11:22:08 -040041 SkScalar decorationThickness;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040042 SkScalar fontSize;
43 SimpleFontStyle fontStyle;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040044
Kevin Lubick369f6a52019-10-03 11:22:08 -040045 uintptr_t /* const char** */ fontFamilies;
46 int numFontFamilies;
47};
48
49para::TextStyle toTextStyle(const SimpleTextStyle& s) {
50 para::TextStyle ts;
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040051
52 // textstyle.color doesn't support a 4f color, however the foreground and background fields below do.
53 ts.setColor(s.color.toSkColor());
54
55 // Emscripten will not allow a value_object to have an unset field, however
56 // It is functionally important that these paints be unset when no value was provided.
57 // paragraph.js defaults these colors to transparent in that case and we use that signal here.
58 if (s.foregroundColor.a > 0) {
59 SkPaint p1;
60 p1.setColor4f(s.foregroundColor.toSkColor4f());
61 ts.setForegroundColor(p1);
Robert Phillipscb77eab2020-03-24 14:19:40 +000062 }
Kevin Lubick369f6a52019-10-03 11:22:08 -040063
Nathaniel Nifonge5d32542020-03-26 09:27:48 -040064 if (s.backgroundColor.a > 0) {
65 SkPaint p2;
66 p2.setColor4f(s.backgroundColor.toSkColor4f());
67 ts.setBackgroundColor(p2);
Robert Phillipscb77eab2020-03-24 14:19:40 +000068 }
Kevin Lubick369f6a52019-10-03 11:22:08 -040069
70 if (s.fontSize != 0) {
71 ts.setFontSize(s.fontSize);
72 }
73
74 ts.setDecoration(para::TextDecoration(s.decoration));
75 if (s.decorationThickness != 0) {
76 ts.setDecorationThicknessMultiplier(s.decorationThickness);
77 }
78
79 const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamilies);
80 if (s.numFontFamilies > 0 && fontFamilies != nullptr) {
81 std::vector<SkString> ff;
82 for (int i = 0; i< s.numFontFamilies; i++) {
83 ff.emplace_back(fontFamilies[i]);
84 }
85 ts.setFontFamilies(ff);
86 }
87
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040088 SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
89 ts.setFontStyle(fs);
90
Kevin Lubick369f6a52019-10-03 11:22:08 -040091 return ts;
92}
93
94struct SimpleParagraphStyle {
Kevin Lubickd3b1fe62019-10-21 10:50:26 -040095 bool disableHinting;
96 uintptr_t /* const char* */ ellipsisPtr;
97 size_t ellipsisLen;
Kevin Lubick369f6a52019-10-03 11:22:08 -040098 SkScalar heightMultiplier;
Kevin Lubick369f6a52019-10-03 11:22:08 -040099 size_t maxLines;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400100 para::TextAlign textAlign;
101 para::TextDirection textDirection;
102 SimpleTextStyle textStyle;
Kevin Lubick369f6a52019-10-03 11:22:08 -0400103};
104
105para::ParagraphStyle toParagraphStyle(const SimpleParagraphStyle& s) {
106 para::ParagraphStyle ps;
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400107 if (s.disableHinting) {
108 ps.turnHintingOff();
109 }
110
111 if (s.ellipsisLen > 0) {
112 const char* ellipsisPtr = reinterpret_cast<const char*>(s.ellipsisPtr);
113 SkString eStr(ellipsisPtr, s.ellipsisLen);
114 ps.setEllipsis(eStr);
115 }
116 ps.setTextAlign(s.textAlign);
117 ps.setTextDirection(s.textDirection);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400118 auto ts = toTextStyle(s.textStyle);
119 ps.setTextStyle(ts);
120 if (s.heightMultiplier != 0) {
121 ps.setHeight(s.heightMultiplier);
122 }
Kevin Lubick369f6a52019-10-03 11:22:08 -0400123 if (s.maxLines != 0) {
124 ps.setMaxLines(s.maxLines);
125 }
126 return ps;
127}
128
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500129struct SimpleTextBox {
130 SkRect rect;
131 // This isn't the most efficient way to represent this, but it is much easier to keep
132 // everything as floats when unpacking on the JS side.
133 // 0.0 = RTL, 1.0 = LTr
134 SkScalar direction;
135};
136
Kevin Lubick369f6a52019-10-03 11:22:08 -0400137Float32Array GetRectsForRange(para::ParagraphImpl& self, unsigned start, unsigned end,
138 para::RectHeightStyle heightStyle, para::RectWidthStyle widthStyle) {
139 std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500140 // Pack these text boxes into an array of n groups of 5 SkScalar (floats)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400141 if (!boxes.size()) {
142 return emscripten::val::null();
143 }
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500144 SimpleTextBox* rects = new SimpleTextBox[boxes.size()];
Kevin Lubick369f6a52019-10-03 11:22:08 -0400145 for (int i = 0; i< boxes.size(); i++) {
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500146 rects[i].rect = boxes[i].rect;
147 if (boxes[i].direction == para::TextDirection::kRtl) {
148 rects[i].direction = 0;
149 } else {
150 rects[i].direction = 1;
151 }
Kevin Lubick369f6a52019-10-03 11:22:08 -0400152 }
153 float* fPtr = reinterpret_cast<float*>(rects);
154 // Of note: now that we have cast rects to float*, emscripten is smart enough to wrap this
155 // into a Float32Array for us.
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500156 return Float32Array(typed_memory_view(boxes.size()*5, fPtr));
Kevin Lubick369f6a52019-10-03 11:22:08 -0400157}
158
159EMSCRIPTEN_BINDINGS(Paragraph) {
160
161 class_<para::Paragraph>("Paragraph");
162
163 // This "base<>" tells Emscripten that ParagraphImpl is a Paragraph and can get substituted
164 // in properly in drawParagraph. However, Emscripten will not let us bind pure virtual methods
Kevin Lubick04912672019-11-15 14:48:55 -0500165 // so we have to "expose" the ParagraphImpl in those cases.
Kevin Lubick369f6a52019-10-03 11:22:08 -0400166 class_<para::ParagraphImpl, base<para::Paragraph>>("ParagraphImpl")
Kevin Lubick04912672019-11-15 14:48:55 -0500167 .function("didExceedMaxLines", &para::Paragraph::didExceedMaxLines)
168 .function("getAlphabeticBaseline", &para::Paragraph::getAlphabeticBaseline)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400169 .function("getGlyphPositionAtCoordinate", &para::ParagraphImpl::getGlyphPositionAtCoordinate)
Kevin Lubick04912672019-11-15 14:48:55 -0500170 .function("getHeight", &para::Paragraph::getHeight)
171 .function("getIdeographicBaseline", &para::Paragraph::getIdeographicBaseline)
172 .function("getLongestLine", &para::Paragraph::getLongestLine)
173 .function("getMaxIntrinsicWidth", &para::Paragraph::getMaxIntrinsicWidth)
174 .function("getMaxWidth", &para::Paragraph::getMaxWidth)
175 .function("getMinIntrinsicWidth", &para::Paragraph::getMinIntrinsicWidth)
176 .function("_getRectsForRange", &GetRectsForRange)
177 .function("getWordBoundary", &para::ParagraphImpl::getWordBoundary)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400178 .function("layout", &para::ParagraphImpl::layout);
179
180 class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
181 .class_function("Make", optional_override([](SimpleParagraphStyle style,
182 sk_sp<SkFontMgr> fontMgr)-> para::ParagraphBuilderImpl {
183 auto fc = sk_make_sp<para::FontCollection>();
184 fc->setDefaultFontManager(fontMgr);
185 auto ps = toParagraphStyle(style);
186 para::ParagraphBuilderImpl pbi(ps, fc);
187 return pbi;
188 }), allow_raw_pointers())
189 .function("addText", optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
190 return self.addText(text.c_str(), text.length());
191 }))
192 .function("build", &para::ParagraphBuilderImpl::Build, allow_raw_pointers())
193 .function("pop", &para::ParagraphBuilderImpl::pop)
194 .function("pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
195 SimpleTextStyle textStyle) {
196 auto ts = toTextStyle(textStyle);
197 self.pushStyle(ts);
198 }));
199
200
201 enum_<para::Affinity>("Affinity")
202 .value("Upstream", para::Affinity::kUpstream)
203 .value("Downstream", para::Affinity::kDownstream);
204
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400205 enum_<SkFontStyle::Slant>("FontSlant")
206 .value("Upright", SkFontStyle::Slant::kUpright_Slant)
207 .value("Italic", SkFontStyle::Slant::kItalic_Slant)
208 .value("Oblique", SkFontStyle::Slant::kOblique_Slant);
209
210 enum_<SkFontStyle::Weight>("FontWeight")
211 .value("Invisible", SkFontStyle::Weight::kInvisible_Weight)
212 .value("Thin", SkFontStyle::Weight::kThin_Weight)
213 .value("ExtraLight", SkFontStyle::Weight::kExtraLight_Weight)
214 .value("Light", SkFontStyle::Weight::kLight_Weight)
215 .value("Normal", SkFontStyle::Weight::kNormal_Weight)
216 .value("Medium", SkFontStyle::Weight::kMedium_Weight)
217 .value("SemiBold", SkFontStyle::Weight::kSemiBold_Weight)
218 .value("Bold", SkFontStyle::Weight::kBold_Weight)
219 .value("ExtraBold", SkFontStyle::Weight::kExtraBold_Weight)
220 .value("Black" , SkFontStyle::Weight::kBlack_Weight)
221 .value("ExtraBlack", SkFontStyle::Weight::kExtraBlack_Weight);
222
223 enum_<SkFontStyle::Width>("FontWidth")
224 .value("UltraCondensed", SkFontStyle::Width::kUltraCondensed_Width)
225 .value("ExtraCondensed", SkFontStyle::Width::kExtraCondensed_Width)
226 .value("Condensed", SkFontStyle::Width::kCondensed_Width)
227 .value("SemiCondensed", SkFontStyle::Width::kSemiCondensed_Width)
228 .value("Normal", SkFontStyle::Width::kNormal_Width)
229 .value("SemiExpanded", SkFontStyle::Width::kSemiExpanded_Width)
230 .value("Expanded", SkFontStyle::Width::kExpanded_Width)
231 .value("ExtraExpanded", SkFontStyle::Width::kExtraExpanded_Width)
232 .value("UltraExpanded", SkFontStyle::Width::kUltraExpanded_Width);
233
Kevin Lubick369f6a52019-10-03 11:22:08 -0400234 enum_<para::RectHeightStyle>("RectHeightStyle")
Kevin Lubick4a5f4f22019-11-20 08:27:10 -0500235 .value("Tight", para::RectHeightStyle::kTight)
236 .value("Max", para::RectHeightStyle::kMax)
237 .value("IncludeLineSpacingMiddle", para::RectHeightStyle::kIncludeLineSpacingMiddle)
238 .value("IncludeLineSpacingTop", para::RectHeightStyle::kIncludeLineSpacingTop)
239 .value("IncludeLineSpacingBottom", para::RectHeightStyle::kIncludeLineSpacingBottom);
Kevin Lubick369f6a52019-10-03 11:22:08 -0400240
241 enum_<para::RectWidthStyle>("RectWidthStyle")
242 .value("Tight", para::RectWidthStyle::kTight)
243 .value("Max", para::RectWidthStyle::kMax);
244
245 enum_<para::TextAlign>("TextAlign")
246 .value("Left", para::TextAlign::kLeft)
247 .value("Right", para::TextAlign::kRight)
248 .value("Center", para::TextAlign::kCenter)
249 .value("Justify", para::TextAlign::kJustify)
250 .value("Start", para::TextAlign::kStart)
251 .value("End", para::TextAlign::kEnd);
252
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400253 enum_<para::TextDirection>("TextDirection")
254 .value("LTR", para::TextDirection::kLtr)
255 .value("RTL", para::TextDirection::kRtl);
256
Kevin Lubick369f6a52019-10-03 11:22:08 -0400257
258 value_object<para::PositionWithAffinity>("PositionWithAffinity")
259 .field("pos", &para::PositionWithAffinity::position)
260 .field("affinity", &para::PositionWithAffinity::affinity);
261
Kevin Lubick04912672019-11-15 14:48:55 -0500262 value_object<SimpleFontStyle>("FontStyle")
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400263 .field("slant", &SimpleFontStyle::slant)
264 .field("weight", &SimpleFontStyle::weight)
265 .field("width", &SimpleFontStyle::width);
266
Kevin Lubick369f6a52019-10-03 11:22:08 -0400267 value_object<SimpleParagraphStyle>("ParagraphStyle")
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400268 .field("disableHinting", &SimpleParagraphStyle::disableHinting)
269 .field("_ellipsisPtr", &SimpleParagraphStyle::ellipsisPtr)
270 .field("_ellipsisLen", &SimpleParagraphStyle::ellipsisLen)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400271 .field("heightMultiplier", &SimpleParagraphStyle::heightMultiplier)
272 .field("maxLines", &SimpleParagraphStyle::maxLines)
273 .field("textAlign", &SimpleParagraphStyle::textAlign)
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400274 .field("textDirection", &SimpleParagraphStyle::textDirection)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400275 .field("textStyle", &SimpleParagraphStyle::textStyle);
276
277 value_object<SimpleTextStyle>("TextStyle")
Kevin Lubick369f6a52019-10-03 11:22:08 -0400278 .field("color", &SimpleTextStyle::color)
Nathaniel Nifonge5d32542020-03-26 09:27:48 -0400279 .field("foregroundColor", &SimpleTextStyle::foregroundColor)
280 .field("backgroundColor", &SimpleTextStyle::backgroundColor)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400281 .field("decoration", &SimpleTextStyle::decoration)
282 .field("decorationThickness", &SimpleTextStyle::decorationThickness)
283 .field("_fontFamilies", &SimpleTextStyle::fontFamilies)
284 .field("fontSize", &SimpleTextStyle::fontSize)
Kevin Lubickd3b1fe62019-10-21 10:50:26 -0400285 .field("fontStyle", &SimpleTextStyle::fontStyle)
Kevin Lubick369f6a52019-10-03 11:22:08 -0400286 .field("_numFontFamilies", &SimpleTextStyle::numFontFamilies);
287
Kevin Lubick04912672019-11-15 14:48:55 -0500288 // The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
289 // with the type we are using.
290 value_object<para::SkRange<size_t>>("URange")
291 .field("start", &para::SkRange<size_t>::start)
292 .field("end", &para::SkRange<size_t>::end);
293
Kevin Lubick369f6a52019-10-03 11:22:08 -0400294 // TextDecoration should be a const because they can be combined
295 constant("NoDecoration", int(para::TextDecoration::kNoDecoration));
296 constant("UnderlineDecoration", int(para::TextDecoration::kUnderline));
297 constant("OverlineDecoration", int(para::TextDecoration::kOverline));
298 constant("LineThroughDecoration", int(para::TextDecoration::kLineThrough));
299}