Add more paragraph bindings to CanvasKit
Change-Id: Ib02b6504724e4d7cfa197a3508f8c0b84b4135bf Reviewed-on: https://skia-review.googlesource.com/c/skia/+/313146 Commit-Queue: Kevin Lubick <kjlubick@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com> Reviewed-by: Yegor Jbanov <yjbanov@google.com> Auto-Submit: Harry Terkelsen <het@google.com>
This commit is contained in:
parent
0f7242f28f
commit
223ffcdff9
@ -58,6 +58,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
already have their own representation of Rect. This is experimental because we don't know
|
||||
if it's faster/better under real-world use and because we don't want to commit to having these
|
||||
for all Rect APIs (and for similar types) until it has baked in a bit.
|
||||
- Added the following to `TextStyle`:
|
||||
- `decorationStyle`
|
||||
- `textBaseline`
|
||||
- `letterSpacing`
|
||||
- `wordSpacing`
|
||||
- `heightMultiplier`
|
||||
- `locale`
|
||||
- `shadows`
|
||||
- `fontFeatures`
|
||||
- Added `strutStyle` to `ParagraphStyle`.
|
||||
- Added `addPlaceholder` to `ParagraphBuilder`.
|
||||
- Added `getRectsForPlaceholders` to `Paragraph`.
|
||||
- `SkFont.getGlyphIDs`, `SkFont.getGlyphBounds`, `SkFont.getGlyphWidths` for turning code points
|
||||
into GlyphIDs and getting the associated metrics with those glyphs. Note: glyph ids are only
|
||||
valid for the font of which they were requested.
|
||||
|
@ -139,4 +139,4 @@ sdk and verified/fixed any build issues that have arisen.
|
||||
11. Upload a CL with all the changes. Run all Test.+CanvasKit, Perf.+CanvasKit,
|
||||
Test.+PathKit, Perf.+PathKit jobs to make sure the new builds pass all
|
||||
tests and don't crash the perf harnesses.
|
||||
12. Send out CL for review. Feel free to point the reviewer at these steps.
|
||||
12. Send out CL for review. Feel free to point the reviewer at these steps.
|
||||
|
@ -144,6 +144,7 @@ var CanvasKit = {
|
||||
// private API
|
||||
/** @return {Float32Array} */
|
||||
_getRectsForRange: function() {},
|
||||
_getRectsForPlaceholders: function() {},
|
||||
},
|
||||
|
||||
ParagraphBuilder: {
|
||||
@ -156,6 +157,7 @@ var CanvasKit = {
|
||||
prototype: {
|
||||
pushStyle: function() {},
|
||||
pushPaintStyle: function() {},
|
||||
addPlaceholder: function() {},
|
||||
},
|
||||
|
||||
// private API
|
||||
@ -163,6 +165,7 @@ var CanvasKit = {
|
||||
_MakeFromFontProvider: function() {},
|
||||
_pushStyle: function() {},
|
||||
_pushPaintStyle: function() {},
|
||||
_addPlaceholder: function() {},
|
||||
},
|
||||
|
||||
SkRuntimeEffect: {
|
||||
@ -903,6 +906,28 @@ var CanvasKit = {
|
||||
RTL: {},
|
||||
},
|
||||
|
||||
DecorationStyle: {
|
||||
Solid: {},
|
||||
Double: {},
|
||||
Dotted: {},
|
||||
Dashed: {},
|
||||
Wavy: {},
|
||||
},
|
||||
|
||||
PlaceholderAlignment: {
|
||||
Baseline: {},
|
||||
AboveBaseline: {},
|
||||
BelowBaseline: {},
|
||||
Top: {},
|
||||
Bottom: {},
|
||||
Middle: {},
|
||||
},
|
||||
|
||||
TextBaseline: {
|
||||
Alphabetic: {},
|
||||
Ideographic: {},
|
||||
},
|
||||
|
||||
TileMode: {
|
||||
Clamp: {},
|
||||
Repeat: {},
|
||||
@ -960,6 +985,7 @@ var CanvasKit = {
|
||||
// It's not enough to declare them above, because closure can still erase them
|
||||
// unless they go on the prototype.
|
||||
CanvasKit.Paragraph.prototype.getRectsForRange = function() {};
|
||||
CanvasKit.Paragraph.prototype.getRectsForPlaceholders = function() {};
|
||||
|
||||
CanvasKit.SkPicture.prototype.saveAsFile = function() {};
|
||||
|
||||
|
@ -8,22 +8,34 @@
|
||||
* @type {Float32Array}
|
||||
*/
|
||||
var floatArray = this._getRectsForRange(start, end, hStyle, wStyle);
|
||||
return floatArrayToRects(floatArray);
|
||||
}
|
||||
|
||||
if (!floatArray || !floatArray.length) {
|
||||
return [];
|
||||
}
|
||||
var ret = [];
|
||||
for (var i = 0; i < floatArray.length; i+=5) {
|
||||
var r = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
|
||||
if (floatArray[i+4] === 0) {
|
||||
r['direction'] = CanvasKit.TextDirection.RTL;
|
||||
} else {
|
||||
r['direction'] = CanvasKit.TextDirection.LTR;
|
||||
CanvasKit.Paragraph.prototype.getRectsForPlaceholders = function() {
|
||||
/**
|
||||
* This is bytes, but we'll want to think about them as float32s
|
||||
* @type {Float32Array}
|
||||
*/
|
||||
var floatArray = this._getRectsForPlaceholders();
|
||||
return floatArrayToRects(floatArray);
|
||||
}
|
||||
|
||||
function floatArrayToRects(floatArray) {
|
||||
if (!floatArray || !floatArray.length) {
|
||||
return [];
|
||||
}
|
||||
ret.push(r);
|
||||
}
|
||||
CanvasKit._free(floatArray.byteOffset);
|
||||
return ret;
|
||||
var ret = [];
|
||||
for (var i = 0; i < floatArray.length; i+=5) {
|
||||
var r = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
|
||||
if (floatArray[i+4] === 0) {
|
||||
r['direction'] = CanvasKit.TextDirection.RTL;
|
||||
} else {
|
||||
r['direction'] = CanvasKit.TextDirection.LTR;
|
||||
}
|
||||
ret.push(r);
|
||||
}
|
||||
CanvasKit._free(floatArray.byteOffset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Registers the font (provided as an arrayBuffer) with the alias `family`.
|
||||
@ -43,7 +55,6 @@
|
||||
// have undefined and it expects, for example, a float.
|
||||
CanvasKit.ParagraphStyle = function(s) {
|
||||
// Use [''] to tell closure not to minify the names
|
||||
// TODO(kjlubick): strutStyle
|
||||
s['disableHinting'] = s['disableHinting'] || false;
|
||||
if (s['ellipsis']) {
|
||||
var str = s['ellipsis'];
|
||||
@ -59,6 +70,7 @@
|
||||
s['textAlign'] = s['textAlign'] || CanvasKit.TextAlign.Start;
|
||||
s['textDirection'] = s['textDirection'] || CanvasKit.TextDirection.LTR;
|
||||
s['textStyle'] = CanvasKit.TextStyle(s['textStyle']);
|
||||
s['strutStyle'] = strutStyle(s['strutStyle']);
|
||||
return s;
|
||||
};
|
||||
|
||||
@ -73,6 +85,34 @@
|
||||
return s;
|
||||
}
|
||||
|
||||
function strutStyle(s) {
|
||||
s = s || {};
|
||||
s['strutEnabled'] = s['strutEnabled'] || false;
|
||||
|
||||
if (s['strutEnabled'] && Array.isArray(s['fontFamilies']) && s['fontFamilies'].length) {
|
||||
s['_fontFamiliesPtr'] = naiveCopyStrArray(s['fontFamilies']);
|
||||
s['_fontFamiliesLen'] = s['fontFamilies'].length;
|
||||
} else {
|
||||
s['_fontFamiliesPtr'] = nullptr;
|
||||
s['_fontFamiliesLen'] = 0;
|
||||
}
|
||||
s['fontStyle'] = fontStyle(s['fontStyle']);
|
||||
s['fontSize'] = s['fontSize'] || 0;
|
||||
s['heightMultiplier'] = s['heightMultiplier'] || 0;
|
||||
s['leading'] = s['leading'] || 0;
|
||||
s['forceStrutHeight'] = s['forceStrutHeight'] || false;
|
||||
return s;
|
||||
}
|
||||
|
||||
function placeholderStyle(s) {
|
||||
s['width'] = s['width'] || 0;
|
||||
s['height'] = s['height'] || 0;
|
||||
s['alignment'] = s['alignment'] || CanvasKit.PlaceholderAlignment.Baseline;
|
||||
s['baseline'] = s['baseline'] || CanvasKit.TextBaseline.Alphabetic;
|
||||
s['offset'] = s['offset'] || 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
CanvasKit.TextStyle = function(s) {
|
||||
// Use [''] to tell closure not to minify the names
|
||||
if (!s['color']) {
|
||||
@ -81,8 +121,49 @@
|
||||
|
||||
s['decoration'] = s['decoration'] || 0;
|
||||
s['decorationThickness'] = s['decorationThickness'] || 0;
|
||||
s['decorationStyle'] = s['decorationStyle'] || CanvasKit.DecorationStyle.Solid;
|
||||
s['textBaseline'] = s['textBaseline'] || CanvasKit.TextBaseline.Alphabetic;
|
||||
s['fontSize'] = s['fontSize'] || 0;
|
||||
s['letterSpacing'] = s['letterSpacing'] || 0;
|
||||
s['wordSpacing'] = s['wordSpacing'] || 0;
|
||||
s['heightMultiplier'] = s['heightMultiplier'] || 0;
|
||||
if (s['locale']) {
|
||||
var str = s['locale'];
|
||||
s['_localePtr'] = cacheOrCopyString(str);
|
||||
s['_localeLen'] = lengthBytesUTF8(str) + 1; // add 1 for the null terminator.
|
||||
} else {
|
||||
s['_localePtr'] = nullptr;
|
||||
s['_localeLen'] = 0;
|
||||
}
|
||||
s['fontStyle'] = fontStyle(s['fontStyle']);
|
||||
if (s['shadows']) {
|
||||
var shadows = s['shadows'];
|
||||
var shadowColors = shadows.map(function(s) { return s['color'] || CanvasKit.BLACK; });
|
||||
var shadowOffsets = shadows.map(function(s) { return s['offset'] || [0, 0]; });
|
||||
var shadowBlurRadii = shadows.map(function(s) { return s['blurRadius'] || 0.0; });
|
||||
s['_shadowLen'] = shadows.length;
|
||||
s['_shadowColorsPtr'] = copyFlexibleColorArray(shadowColors).colorPtr;
|
||||
s['_shadowOffsetsPtr'] = copy2dArray(shadowOffsets, 'HEAPF32');
|
||||
s['_shadowBlurRadiiPtr'] = copy1dArray(shadowBlurRadii, 'HEAPF32');
|
||||
} else {
|
||||
s['_shadowLen'] = 0;
|
||||
s['_shadowColorsPtr'] = nullptr;
|
||||
s['_shadowOffsetsPtr'] = nullptr;
|
||||
s['_shadowBlurRadiiPtr'] = nullptr;
|
||||
}
|
||||
if (s['fontFeatures']) {
|
||||
var fontFeatures = s['fontFeatures'];
|
||||
var fontFeatureNames = fontFeatures.map(function(s) { return s['name']; });
|
||||
var fontFeatureValues = fontFeatures.map(function(s) { return s['value']; });
|
||||
s['_fontFeatureLen'] = fontFeatures.length;
|
||||
s['_fontFeatureNamesPtr'] = naiveCopyStrArray(fontFeatureNames);
|
||||
s['_fontFeatureValuesPtr'] = copy1dArray(fontFeatureValues, 'HEAPU32');
|
||||
} else {
|
||||
s['_fontFeatureLen'] = 0;
|
||||
s['_fontFeatureNamesPtr'] = nullptr;
|
||||
s['_fontFeatureValuesPtr'] = nullptr;
|
||||
}
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
@ -102,7 +183,7 @@
|
||||
var strPtr = cacheOrCopyString(strings[i]);
|
||||
sPtrs.push(strPtr);
|
||||
}
|
||||
return copy1dArray(sPtrs, "HEAPU32");
|
||||
return copy1dArray(sPtrs, 'HEAPU32');
|
||||
}
|
||||
|
||||
// maps string -> malloc'd pointer
|
||||
@ -129,6 +210,7 @@
|
||||
// having to free them after every invocation.
|
||||
var scratchForegroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
|
||||
var scratchBackgroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
|
||||
var scratchDecorationColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
|
||||
|
||||
function copyArrays(textStyle) {
|
||||
// These color fields were arrays, but will set to WASM pointers before we pass this
|
||||
@ -136,12 +218,16 @@
|
||||
textStyle['_colorPtr'] = copyColorToWasm(textStyle['color']);
|
||||
textStyle['_foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js
|
||||
textStyle['_backgroundColorPtr'] = nullptr;
|
||||
textStyle['_decorationColorPtr'] = nullptr;
|
||||
if (textStyle['foregroundColor']) {
|
||||
textStyle['_foregroundColorPtr'] = copyColorToWasm(textStyle['foregroundColor'], scratchForegroundColorPtr);
|
||||
}
|
||||
if (textStyle['backgroundColor']) {
|
||||
textStyle['_backgroundColorPtr'] = copyColorToWasm(textStyle['backgroundColor'], scratchBackgroundColorPtr);
|
||||
}
|
||||
if (textStyle['decorationColor']) {
|
||||
textStyle['_decorationColorPtr'] = copyColorToWasm(textStyle['decorationColor'], scratchDecorationColorPtr);
|
||||
}
|
||||
|
||||
if (Array.isArray(textStyle['fontFamilies']) && textStyle['fontFamilies'].length) {
|
||||
textStyle['_fontFamiliesPtr'] = naiveCopyStrArray(textStyle['fontFamilies']);
|
||||
@ -179,12 +265,22 @@
|
||||
copyArrays(textStyle);
|
||||
this._pushStyle(textStyle);
|
||||
freeArrays(textStyle);
|
||||
}
|
||||
};
|
||||
|
||||
CanvasKit.ParagraphBuilder.prototype.pushPaintStyle = function(textStyle, fg, bg) {
|
||||
copyArrays(textStyle);
|
||||
this._pushPaintStyle(textStyle, fg, bg);
|
||||
freeArrays(textStyle);
|
||||
}
|
||||
};
|
||||
|
||||
CanvasKit.ParagraphBuilder.prototype.addPlaceholder =
|
||||
function(width, height, alignment, baseline, offset) {
|
||||
width = width || 0;
|
||||
height = height || 0;
|
||||
alignment = alignment || CanvasKit.PlaceholderAlignment.Baseline;
|
||||
baseline = baseline || CanvasKit.TextBaseline.Alphabetic;
|
||||
offset = offset || 0;
|
||||
this._addPlaceholder(width, height, alignment, baseline, offset);
|
||||
};
|
||||
});
|
||||
}(Module)); // When this file is loaded in, the high level object is "Module";
|
||||
|
@ -30,14 +30,14 @@ namespace para = skia::textlayout;
|
||||
|
||||
SkColor4f toSkColor4f(uintptr_t /* float* */ cPtr) {
|
||||
float* fourFloats = reinterpret_cast<float*>(cPtr);
|
||||
SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
|
||||
SkColor4f color = {fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3]};
|
||||
return color;
|
||||
}
|
||||
|
||||
struct SimpleFontStyle {
|
||||
SkFontStyle::Slant slant;
|
||||
SkFontStyle::Slant slant;
|
||||
SkFontStyle::Weight weight;
|
||||
SkFontStyle::Width width;
|
||||
SkFontStyle::Width width;
|
||||
};
|
||||
|
||||
struct SimpleTextStyle {
|
||||
@ -46,17 +46,78 @@ struct SimpleTextStyle {
|
||||
uintptr_t /* float* */ backgroundColorPtr;
|
||||
uint8_t decoration;
|
||||
SkScalar decorationThickness;
|
||||
uintptr_t /* float* */ decorationColorPtr;
|
||||
para::TextDecorationStyle decorationStyle;
|
||||
para::TextBaseline textBaseline;
|
||||
SkScalar fontSize;
|
||||
SkScalar letterSpacing;
|
||||
SkScalar wordSpacing;
|
||||
SkScalar heightMultiplier;
|
||||
uintptr_t /* const char* */ localePtr;
|
||||
int localeLen;
|
||||
SimpleFontStyle fontStyle;
|
||||
|
||||
uintptr_t /* const char** */ fontFamiliesPtr;
|
||||
int fontFamiliesLen;
|
||||
|
||||
int shadowLen;
|
||||
uintptr_t /* SkColor4f* */ shadowColorsPtr;
|
||||
uintptr_t /* SkPoint* */ shadowOffsetsPtr;
|
||||
uintptr_t /* float* */ shadowBlurRadiiPtr;
|
||||
|
||||
int fontFeatureLen;
|
||||
uintptr_t /* float* */ fontFeatureNamesPtr;
|
||||
uintptr_t /* float* */ fontFeatureValuesPtr;
|
||||
};
|
||||
|
||||
struct SimpleStrutStyle {
|
||||
uintptr_t /* const char** */ fontFamiliesPtr;
|
||||
int fontFamiliesLen;
|
||||
SimpleFontStyle fontStyle;
|
||||
SkScalar fontSize;
|
||||
SkScalar heightMultiplier;
|
||||
SkScalar leading;
|
||||
bool strutEnabled;
|
||||
bool forceStrutHeight;
|
||||
};
|
||||
|
||||
para::StrutStyle toStrutStyle(const SimpleStrutStyle& s) {
|
||||
para::StrutStyle ss;
|
||||
|
||||
const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
|
||||
if (fontFamilies != nullptr) {
|
||||
std::vector<SkString> ff;
|
||||
for (int i = 0; i < s.fontFamiliesLen; i++) {
|
||||
ff.emplace_back(fontFamilies[i]);
|
||||
}
|
||||
ss.setFontFamilies(ff);
|
||||
}
|
||||
|
||||
SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
|
||||
ss.setFontStyle(fs);
|
||||
|
||||
if (s.fontSize != 0) {
|
||||
ss.setFontSize(s.fontSize);
|
||||
}
|
||||
if (s.heightMultiplier != 0) {
|
||||
ss.setHeight(s.heightMultiplier);
|
||||
ss.setHeightOverride(true);
|
||||
}
|
||||
if (s.leading != 0) {
|
||||
ss.setLeading(s.leading);
|
||||
}
|
||||
|
||||
ss.setStrutEnabled(s.strutEnabled);
|
||||
ss.setForceStrutHeight(s.forceStrutHeight);
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
para::TextStyle toTextStyle(const SimpleTextStyle& s) {
|
||||
para::TextStyle ts;
|
||||
|
||||
// textstyle.color doesn't support a 4f color, however the foreground and background fields below do.
|
||||
// textstyle.color doesn't support a 4f color, however the foreground and background fields
|
||||
// below do.
|
||||
ts.setColor(toSkColor4f(s.colorPtr).toSkColor());
|
||||
|
||||
// It is functionally important that these paints be unset when no value was provided.
|
||||
@ -75,14 +136,35 @@ para::TextStyle toTextStyle(const SimpleTextStyle& s) {
|
||||
if (s.fontSize != 0) {
|
||||
ts.setFontSize(s.fontSize);
|
||||
}
|
||||
if (s.letterSpacing != 0) {
|
||||
ts.setLetterSpacing(s.letterSpacing);
|
||||
}
|
||||
if (s.wordSpacing != 0) {
|
||||
ts.setWordSpacing(s.wordSpacing);
|
||||
}
|
||||
|
||||
if (s.heightMultiplier != 0) {
|
||||
ts.setHeight(s.heightMultiplier);
|
||||
ts.setHeightOverride(true);
|
||||
}
|
||||
|
||||
ts.setDecoration(para::TextDecoration(s.decoration));
|
||||
ts.setDecorationStyle(s.decorationStyle);
|
||||
if (s.decorationThickness != 0) {
|
||||
ts.setDecorationThicknessMultiplier(s.decorationThickness);
|
||||
}
|
||||
if (s.decorationColorPtr) {
|
||||
ts.setDecorationColor(toSkColor4f(s.decorationColorPtr).toSkColor());
|
||||
}
|
||||
|
||||
if (s.localeLen > 0) {
|
||||
const char* localePtr = reinterpret_cast<const char*>(s.localePtr);
|
||||
SkString lStr(localePtr, s.localeLen);
|
||||
ts.setLocale(lStr);
|
||||
}
|
||||
|
||||
const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
|
||||
if (s.fontFamiliesLen > 0 && fontFamilies != nullptr) {
|
||||
if (fontFamilies != nullptr) {
|
||||
std::vector<SkString> ff;
|
||||
for (int i = 0; i < s.fontFamiliesLen; i++) {
|
||||
ff.emplace_back(fontFamilies[i]);
|
||||
@ -90,9 +172,32 @@ para::TextStyle toTextStyle(const SimpleTextStyle& s) {
|
||||
ts.setFontFamilies(ff);
|
||||
}
|
||||
|
||||
ts.setTextBaseline(s.textBaseline);
|
||||
|
||||
SkFontStyle fs(s.fontStyle.weight, s.fontStyle.width, s.fontStyle.slant);
|
||||
ts.setFontStyle(fs);
|
||||
|
||||
if (s.shadowLen > 0) {
|
||||
const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(s.shadowColorsPtr);
|
||||
const SkPoint* offsets = reinterpret_cast<const SkPoint*>(s.shadowOffsetsPtr);
|
||||
const float* blurRadii = reinterpret_cast<const float*>(s.shadowBlurRadiiPtr);
|
||||
for (int i = 0; i < s.shadowLen; i++) {
|
||||
para::TextShadow shadow(colors[i].toSkColor(), offsets[i], blurRadii[i]);
|
||||
ts.addShadow(shadow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (s.fontFeatureLen > 0) {
|
||||
const char** fontFeatureNames = reinterpret_cast<const char**>(s.fontFeatureNamesPtr);
|
||||
const int* fontFeatureValues = reinterpret_cast<const int*>(s.fontFeatureValuesPtr);
|
||||
for (int i = 0; i < s.fontFeatureLen; i++) {
|
||||
// Font features names are 4-character simple strings.
|
||||
SkString name(fontFeatureNames[i], 4);
|
||||
ts.addFontFeature(name, fontFeatureValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
@ -105,6 +210,7 @@ struct SimpleParagraphStyle {
|
||||
para::TextAlign textAlign;
|
||||
para::TextDirection textDirection;
|
||||
SimpleTextStyle textStyle;
|
||||
SimpleStrutStyle strutStyle;
|
||||
};
|
||||
|
||||
para::ParagraphStyle toParagraphStyle(const SimpleParagraphStyle& s) {
|
||||
@ -122,6 +228,8 @@ para::ParagraphStyle toParagraphStyle(const SimpleParagraphStyle& s) {
|
||||
ps.setTextDirection(s.textDirection);
|
||||
auto ts = toTextStyle(s.textStyle);
|
||||
ps.setTextStyle(ts);
|
||||
auto ss = toStrutStyle(s.strutStyle);
|
||||
ps.setStrutStyle(ss);
|
||||
if (s.heightMultiplier != 0) {
|
||||
ps.setHeight(s.heightMultiplier);
|
||||
}
|
||||
@ -139,15 +247,13 @@ struct SimpleTextBox {
|
||||
SkScalar direction;
|
||||
};
|
||||
|
||||
Float32Array GetRectsForRange(para::ParagraphImpl& self, unsigned start, unsigned end,
|
||||
para::RectHeightStyle heightStyle, para::RectWidthStyle widthStyle) {
|
||||
std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
|
||||
Float32Array TextBoxesToFloat32Array(std::vector<para::TextBox> boxes) {
|
||||
// Pack these text boxes into an array of n groups of 5 SkScalar (floats)
|
||||
if (!boxes.size()) {
|
||||
return emscripten::val::null();
|
||||
}
|
||||
SimpleTextBox* rects = new SimpleTextBox[boxes.size()];
|
||||
for (int i = 0; i< boxes.size(); i++) {
|
||||
for (int i = 0; i < boxes.size(); i++) {
|
||||
rects[i].rect = boxes[i].rect;
|
||||
if (boxes[i].direction == para::TextDirection::kRtl) {
|
||||
rects[i].direction = 0;
|
||||
@ -158,7 +264,21 @@ Float32Array GetRectsForRange(para::ParagraphImpl& self, unsigned start, unsigne
|
||||
float* fPtr = reinterpret_cast<float*>(rects);
|
||||
// Of note: now that we have cast rects to float*, emscripten is smart enough to wrap this
|
||||
// into a Float32Array for us.
|
||||
return Float32Array(typed_memory_view(boxes.size()*5, fPtr));
|
||||
return Float32Array(typed_memory_view(boxes.size() * 5, fPtr));
|
||||
}
|
||||
|
||||
Float32Array GetRectsForRange(para::ParagraphImpl& self,
|
||||
unsigned start,
|
||||
unsigned end,
|
||||
para::RectHeightStyle heightStyle,
|
||||
para::RectWidthStyle widthStyle) {
|
||||
std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
|
||||
return TextBoxesToFloat32Array(boxes);
|
||||
}
|
||||
|
||||
Float32Array GetRectsForPlaceholders(para::ParagraphImpl& self) {
|
||||
std::vector<para::TextBox> boxes = self.getRectsForPlaceholders();
|
||||
return TextBoxesToFloat32Array(boxes);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
@ -179,45 +299,72 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
.function("getMaxWidth", ¶::Paragraph::getMaxWidth)
|
||||
.function("getMinIntrinsicWidth", ¶::Paragraph::getMinIntrinsicWidth)
|
||||
.function("_getRectsForRange", &GetRectsForRange)
|
||||
.function("_getRectsForPlaceholders", &GetRectsForPlaceholders)
|
||||
.function("getWordBoundary", ¶::ParagraphImpl::getWordBoundary)
|
||||
.function("layout", ¶::ParagraphImpl::layout);
|
||||
.function("layout", ¶::ParagraphImpl::layout)
|
||||
.function("getLineMetrics", ¶::ParagraphImpl::getLineMetrics);
|
||||
|
||||
class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
|
||||
.class_function("_Make", optional_override([](SimpleParagraphStyle style, sk_sp<SkFontMgr> fontMgr)
|
||||
-> std::unique_ptr<para::ParagraphBuilderImpl> {
|
||||
auto fc = sk_make_sp<para::FontCollection>();
|
||||
fc->setDefaultFontManager(fontMgr);
|
||||
auto ps = toParagraphStyle(style);
|
||||
auto pb = para::ParagraphBuilderImpl::make(ps, fc);
|
||||
return std::unique_ptr<para::ParagraphBuilderImpl>(static_cast<para::ParagraphBuilderImpl*>(pb.release()));
|
||||
}), allow_raw_pointers())
|
||||
.class_function("_MakeFromFontProvider", optional_override([](SimpleParagraphStyle style,
|
||||
sk_sp<para::TypefaceFontProvider> fontProvider)-> std::unique_ptr<para::ParagraphBuilderImpl> {
|
||||
auto fc = sk_make_sp<para::FontCollection>();
|
||||
fc->setDefaultFontManager(fontProvider);
|
||||
auto ps = toParagraphStyle(style);
|
||||
auto pb = para::ParagraphBuilderImpl::make(ps, fc);
|
||||
return std::unique_ptr<para::ParagraphBuilderImpl>(static_cast<para::ParagraphBuilderImpl*>(pb.release()));
|
||||
}), allow_raw_pointers())
|
||||
.function("addText", optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
|
||||
return self.addText(text.c_str(), text.length());
|
||||
}))
|
||||
.function("build", ¶::ParagraphBuilderImpl::Build, allow_raw_pointers())
|
||||
.function("pop", ¶::ParagraphBuilderImpl::pop)
|
||||
.function("_pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
|
||||
SimpleTextStyle textStyle) {
|
||||
auto ts = toTextStyle(textStyle);
|
||||
self.pushStyle(ts);
|
||||
}))
|
||||
// A method of pushing a textStyle with paints instead of colors for foreground and
|
||||
// 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.
|
||||
.function("_pushPaintStyle", optional_override([](para::ParagraphBuilderImpl& self,
|
||||
SimpleTextStyle textStyle, SkPaint foreground, SkPaint background) {
|
||||
auto ts = toTextStyle(textStyle);
|
||||
ts.setForegroundColor(foreground);
|
||||
ts.setBackgroundColor(background);
|
||||
self.pushStyle(ts);
|
||||
}));
|
||||
.class_function(
|
||||
"_Make",
|
||||
optional_override([](SimpleParagraphStyle style, sk_sp<SkFontMgr> fontMgr)
|
||||
-> std::unique_ptr<para::ParagraphBuilderImpl> {
|
||||
auto fc = sk_make_sp<para::FontCollection>();
|
||||
fc->setDefaultFontManager(fontMgr);
|
||||
fc->enableFontFallback();
|
||||
auto ps = toParagraphStyle(style);
|
||||
auto pb = para::ParagraphBuilderImpl::make(ps, fc);
|
||||
return std::unique_ptr<para::ParagraphBuilderImpl>(
|
||||
static_cast<para::ParagraphBuilderImpl*>(pb.release()));
|
||||
}),
|
||||
allow_raw_pointers())
|
||||
.class_function(
|
||||
"_MakeFromFontProvider",
|
||||
optional_override([](SimpleParagraphStyle style,
|
||||
sk_sp<para::TypefaceFontProvider> fontProvider)
|
||||
-> std::unique_ptr<para::ParagraphBuilderImpl> {
|
||||
auto fc = sk_make_sp<para::FontCollection>();
|
||||
fc->setDefaultFontManager(fontProvider);
|
||||
fc->enableFontFallback();
|
||||
auto ps = toParagraphStyle(style);
|
||||
auto pb = para::ParagraphBuilderImpl::make(ps, fc);
|
||||
return std::unique_ptr<para::ParagraphBuilderImpl>(
|
||||
static_cast<para::ParagraphBuilderImpl*>(pb.release()));
|
||||
}),
|
||||
allow_raw_pointers())
|
||||
.function("addText",
|
||||
optional_override([](para::ParagraphBuilderImpl& self, std::string text) {
|
||||
return self.addText(text.c_str(), text.length());
|
||||
}))
|
||||
.function("build", ¶::ParagraphBuilderImpl::Build, allow_raw_pointers())
|
||||
.function("pop", ¶::ParagraphBuilderImpl::pop)
|
||||
.function("_pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
|
||||
SimpleTextStyle textStyle) {
|
||||
auto ts = toTextStyle(textStyle);
|
||||
self.pushStyle(ts);
|
||||
}))
|
||||
// A method of pushing a textStyle with paints instead of colors for foreground and
|
||||
// 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.
|
||||
.function("_pushPaintStyle",
|
||||
optional_override([](para::ParagraphBuilderImpl& self,
|
||||
SimpleTextStyle textStyle, SkPaint foreground,
|
||||
SkPaint background) {
|
||||
auto ts = toTextStyle(textStyle);
|
||||
ts.setForegroundColor(foreground);
|
||||
ts.setBackgroundColor(background);
|
||||
self.pushStyle(ts);
|
||||
}))
|
||||
.function("_addPlaceholder", optional_override([](para::ParagraphBuilderImpl& self,
|
||||
SkScalar width,
|
||||
SkScalar height,
|
||||
para::PlaceholderAlignment alignment,
|
||||
para::TextBaseline baseline,
|
||||
SkScalar offset) {
|
||||
para::PlaceholderStyle ps(width, height, alignment, baseline, offset);
|
||||
self.addPlaceholder(ps);
|
||||
}));
|
||||
|
||||
class_<para::TypefaceFontProvider, base<SkFontMgr>>("TypefaceFontProvider")
|
||||
.smart_ptr<sk_sp<para::TypefaceFontProvider>>("sk_sp<TypefaceFontProvider>")
|
||||
@ -289,6 +436,24 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
.value("LTR", para::TextDirection::kLtr)
|
||||
.value("RTL", para::TextDirection::kRtl);
|
||||
|
||||
enum_<para::TextDecorationStyle>("DecorationStyle")
|
||||
.value("Solid", para::TextDecorationStyle::kSolid)
|
||||
.value("Double", para::TextDecorationStyle::kDouble)
|
||||
.value("Dotted", para::TextDecorationStyle::kDotted)
|
||||
.value("Dashed", para::TextDecorationStyle::kDashed)
|
||||
.value("Wavy", para::TextDecorationStyle::kWavy);
|
||||
|
||||
enum_<para::PlaceholderAlignment>("PlaceholderAlignment")
|
||||
.value("Baseline", para::PlaceholderAlignment::kBaseline)
|
||||
.value("AboveBaseline", para::PlaceholderAlignment::kAboveBaseline)
|
||||
.value("BelowBaseline", para::PlaceholderAlignment::kBelowBaseline)
|
||||
.value("Top", para::PlaceholderAlignment::kTop)
|
||||
.value("Bottom", para::PlaceholderAlignment::kBottom)
|
||||
.value("Middle", para::PlaceholderAlignment::kMiddle);
|
||||
|
||||
enum_<para::TextBaseline>("TextBaseline")
|
||||
.value("Alphabetic", para::TextBaseline::kAlphabetic)
|
||||
.value("Ideographic", para::TextBaseline::kIdeographic);
|
||||
|
||||
value_object<para::PositionWithAffinity>("PositionWithAffinity")
|
||||
.field("pos", ¶::PositionWithAffinity::position)
|
||||
@ -307,18 +472,43 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
|
||||
.field("maxLines", &SimpleParagraphStyle::maxLines)
|
||||
.field("textAlign", &SimpleParagraphStyle::textAlign)
|
||||
.field("textDirection", &SimpleParagraphStyle::textDirection)
|
||||
.field("textStyle", &SimpleParagraphStyle::textStyle);
|
||||
.field("textStyle", &SimpleParagraphStyle::textStyle)
|
||||
.field("strutStyle", &SimpleParagraphStyle::strutStyle);
|
||||
|
||||
value_object<SimpleStrutStyle>("StrutStyle")
|
||||
.field("_fontFamiliesPtr", &SimpleStrutStyle::fontFamiliesPtr)
|
||||
.field("_fontFamiliesLen", &SimpleStrutStyle::fontFamiliesLen)
|
||||
.field("strutEnabled", &SimpleStrutStyle::strutEnabled)
|
||||
.field("fontSize", &SimpleStrutStyle::fontSize)
|
||||
.field("fontStyle", &SimpleStrutStyle::fontStyle)
|
||||
.field("heightMultiplier", &SimpleStrutStyle::heightMultiplier)
|
||||
.field("leading", &SimpleStrutStyle::leading)
|
||||
.field("forceStrutHeight", &SimpleStrutStyle::forceStrutHeight);
|
||||
|
||||
value_object<SimpleTextStyle>("TextStyle")
|
||||
.field("_colorPtr", &SimpleTextStyle::colorPtr)
|
||||
.field("_foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
|
||||
.field("_backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
|
||||
.field("decoration", &SimpleTextStyle::decoration)
|
||||
.field("decorationThickness", &SimpleTextStyle::decorationThickness)
|
||||
.field("_fontFamiliesPtr", &SimpleTextStyle::fontFamiliesPtr)
|
||||
.field("_fontFamiliesLen", &SimpleTextStyle::fontFamiliesLen)
|
||||
.field("fontSize", &SimpleTextStyle::fontSize)
|
||||
.field("fontStyle", &SimpleTextStyle::fontStyle);
|
||||
.field("_colorPtr", &SimpleTextStyle::colorPtr)
|
||||
.field("_foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
|
||||
.field("_backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
|
||||
.field("decoration", &SimpleTextStyle::decoration)
|
||||
.field("decorationThickness", &SimpleTextStyle::decorationThickness)
|
||||
.field("_decorationColorPtr", &SimpleTextStyle::decorationColorPtr)
|
||||
.field("decorationStyle", &SimpleTextStyle::decorationStyle)
|
||||
.field("_fontFamiliesPtr", &SimpleTextStyle::fontFamiliesPtr)
|
||||
.field("_fontFamiliesLen", &SimpleTextStyle::fontFamiliesLen)
|
||||
.field("fontSize", &SimpleTextStyle::fontSize)
|
||||
.field("letterSpacing", &SimpleTextStyle::letterSpacing)
|
||||
.field("wordSpacing", &SimpleTextStyle::wordSpacing)
|
||||
.field("heightMultiplier", &SimpleTextStyle::heightMultiplier)
|
||||
.field("_localePtr", &SimpleTextStyle::localePtr)
|
||||
.field("_localeLen", &SimpleTextStyle::localeLen)
|
||||
.field("fontStyle", &SimpleTextStyle::fontStyle)
|
||||
.field("_shadowLen", &SimpleTextStyle::shadowLen)
|
||||
.field("_shadowColorsPtr", &SimpleTextStyle::shadowColorsPtr)
|
||||
.field("_shadowOffsetsPtr", &SimpleTextStyle::shadowOffsetsPtr)
|
||||
.field("_shadowBlurRadiiPtr", &SimpleTextStyle::shadowBlurRadiiPtr)
|
||||
.field("_fontFeatureLen", &SimpleTextStyle::fontFeatureLen)
|
||||
.field("_fontFeatureNamesPtr", &SimpleTextStyle::fontFeatureNamesPtr)
|
||||
.field("_fontFeatureValuesPtr", &SimpleTextStyle::fontFeatureValuesPtr);
|
||||
|
||||
// The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
|
||||
// with the type we are using.
|
||||
|
@ -23,6 +23,13 @@ describe('Paragraph Behavior', function() {
|
||||
emojiFontBuffer = buffer;
|
||||
});
|
||||
|
||||
let robotoFontBuffer = null;
|
||||
const robotoFontLoaded = fetch('/assets/Roboto-Regular.otf').then(
|
||||
(response) => response.arrayBuffer()).then(
|
||||
(buffer) => {
|
||||
robotoFontBuffer = buffer;
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await LoadCanvasKit;
|
||||
await notoSerifFontLoaded;
|
||||
@ -99,6 +106,7 @@ describe('Paragraph Behavior', function() {
|
||||
end: 26,
|
||||
});
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
@ -132,6 +140,7 @@ describe('Paragraph Behavior', function() {
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
fontMgr.delete();
|
||||
@ -169,6 +178,7 @@ describe('Paragraph Behavior', function() {
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
// Again 5px to the right so you can tell the fill is transparent
|
||||
canvas.drawParagraph(paragraph, 15, 10);
|
||||
@ -180,6 +190,222 @@ describe('Paragraph Behavior', function() {
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
gm('paragraph_letter_word_spacing', (canvas) => {
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(notoSerifFontBuffer);
|
||||
expect(fontMgr.countFamilies()).toEqual(1);
|
||||
expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
|
||||
|
||||
const wrapTo = 200;
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
// color should default to black
|
||||
fontFamilies: ['Noto Serif'],
|
||||
fontSize: 20,
|
||||
letterSpacing: 5,
|
||||
wordSpacing: 10,
|
||||
},
|
||||
|
||||
textAlign: CanvasKit.TextAlign.Center,
|
||||
});
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText(
|
||||
'This text should have a lot of space between the letters and words.');
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
fontMgr.delete();
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
gm('paragraph_shadows', (canvas) => {
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(notoSerifFontBuffer);
|
||||
expect(fontMgr.countFamilies()).toEqual(1);
|
||||
expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
|
||||
|
||||
const wrapTo = 200;
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.WHITE,
|
||||
fontFamilies: ['Noto Serif'],
|
||||
fontSize: 20,
|
||||
shadows: [{color: CanvasKit.BLACK, blurRadius: 15},
|
||||
{color: CanvasKit.RED, blurRadius: 5, offset: [10, 10]}],
|
||||
},
|
||||
|
||||
textAlign: CanvasKit.TextAlign.Center,
|
||||
});
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText('This text should have a shadow behind it.');
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
fontMgr.delete();
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
gm('paragraph_strut_style', (canvas) => {
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(robotoFontBuffer);
|
||||
expect(fontMgr.countFamilies()).toEqual(1);
|
||||
expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
|
||||
|
||||
// The lines in this paragraph should have the same height despite the third
|
||||
// line having a larger font size.
|
||||
const paraStrutStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
fontFamilies: ['Roboto'],
|
||||
color: CanvasKit.BLACK,
|
||||
},
|
||||
strutStyle: {
|
||||
strutEnabled: true,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 28,
|
||||
heightMultiplier: 1.5,
|
||||
forceStrutHeight: true,
|
||||
},
|
||||
});
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
fontFamilies: ['Roboto'],
|
||||
color: CanvasKit.BLACK,
|
||||
},
|
||||
});
|
||||
const roboto28Style = new CanvasKit.TextStyle({
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 28,
|
||||
});
|
||||
const roboto32Style = new CanvasKit.TextStyle({
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 32,
|
||||
});
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStrutStyle, fontMgr);
|
||||
builder.pushStyle(roboto28Style);
|
||||
builder.addText('This paragraph\n');
|
||||
builder.pushStyle(roboto32Style);
|
||||
builder.addText('is using\n');
|
||||
builder.pop();
|
||||
builder.pushStyle(roboto28Style);
|
||||
builder.addText('a strut style!\n');
|
||||
builder.pop();
|
||||
builder.pop();
|
||||
|
||||
const builder2 = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder2.pushStyle(roboto28Style);
|
||||
builder2.addText('This paragraph\n');
|
||||
builder2.pushStyle(roboto32Style);
|
||||
builder2.addText('is not using\n');
|
||||
builder2.pop();
|
||||
builder2.pushStyle(roboto28Style);
|
||||
builder2.addText('a strut style!\n');
|
||||
builder2.pop();
|
||||
builder2.pop();
|
||||
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
const paragraph2 = builder2.build();
|
||||
paragraph2.layout(300);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
canvas.drawParagraph(paragraph2, 220, 10);
|
||||
|
||||
fontMgr.delete();
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
gm('paragraph_font_features', (canvas) => {
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(robotoFontBuffer);
|
||||
expect(fontMgr.countFamilies()).toEqual(1);
|
||||
expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
|
||||
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 30,
|
||||
fontFeatures: [{name: 'smcp', value: 1}]
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Center,
|
||||
});
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText('This Text Should Be In Small Caps');
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
fontMgr.delete();
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
gm('paragraph_placeholders', (canvas) => {
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(robotoFontBuffer);
|
||||
expect(fontMgr.countFamilies()).toEqual(1);
|
||||
expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
|
||||
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 20,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Center,
|
||||
});
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText('There should be ');
|
||||
builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.AboveBaseline,
|
||||
CanvasKit.TextBaseline.Ideographic);
|
||||
builder.addText('a space in this sentence.\n');
|
||||
|
||||
builder.addText('There should be ');
|
||||
builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.BelowBaseline,
|
||||
CanvasKit.TextBaseline.Ideographic);
|
||||
builder.addText('a dropped space in this sentence.\n');
|
||||
|
||||
builder.addText('There should be ');
|
||||
builder.addPlaceholder(10, 10, null, null, 20);
|
||||
builder.addText('an offset space in this sentence.\n');
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(300);
|
||||
|
||||
let rects = paragraph.getRectsForPlaceholders();
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
for (const rect of rects) {
|
||||
const p = new CanvasKit.SkPaint();
|
||||
p.setColor(CanvasKit.Color(0, 0, 255));
|
||||
p.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
// Account for the (10, 10) offset when we painted the paragraph.
|
||||
const placeholder =
|
||||
CanvasKit.LTRBRect(rect[0]+10,rect[1]+10,rect[2]+10,rect[3]+10);
|
||||
canvas.drawRect(placeholder, p);
|
||||
p.delete();
|
||||
}
|
||||
|
||||
fontMgr.delete();
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
// loosely based on SkParagraph_GetRectsForRangeParagraph test in c++ code.
|
||||
gm('paragraph_rects', (canvas) => {
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(notoSerifFontBuffer);
|
||||
@ -244,6 +470,7 @@ describe('Paragraph Behavior', function() {
|
||||
color: CanvasKit.Color(0, 200, 200),
|
||||
}
|
||||
];
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
// Move it down a bit so we can see the rects that go above 0,0
|
||||
canvas.translate(10, 10);
|
||||
canvas.drawParagraph(paragraph, 0, 0);
|
||||
@ -306,6 +533,7 @@ describe('Paragraph Behavior', function() {
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
const paint = new CanvasKit.SkPaint();
|
||||
@ -339,6 +567,7 @@ describe('Paragraph Behavior', function() {
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.translate(10, 10);
|
||||
canvas.drawParagraph(paragraph, 0, 0);
|
||||
|
||||
@ -419,7 +648,7 @@ describe('Paragraph Behavior', function() {
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
canvas.clear(CanvasKit.Color(250, 250, 250));
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
|
||||
canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
@ -476,7 +705,7 @@ describe('Paragraph Behavior', function() {
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
canvas.clear(CanvasKit.Color(250, 250, 250));
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
|
||||
canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
@ -487,6 +716,77 @@ describe('Paragraph Behavior', function() {
|
||||
fontMgr.delete();
|
||||
});
|
||||
|
||||
gm('paragraph_text_styles', (canvas) => {
|
||||
const paint = new CanvasKit.SkPaint();
|
||||
|
||||
paint.setColor(CanvasKit.GREEN);
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
|
||||
const fontMgr = CanvasKit.SkFontMgr.FromData(notoSerifFontBuffer);
|
||||
expect(fontMgr.countFamilies()).toEqual(1);
|
||||
expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
|
||||
|
||||
const wrapTo = 200;
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Noto Serif'],
|
||||
fontSize: 20,
|
||||
decoration: CanvasKit.UnderlineDecoration,
|
||||
decorationThickness: 1.5, // multiplier based on font size
|
||||
decorationStyle: CanvasKit.DecorationStyle.Wavy,
|
||||
},
|
||||
});
|
||||
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
|
||||
|
||||
const blueText = new CanvasKit.TextStyle({
|
||||
backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
|
||||
color: CanvasKit.Color(48, 37, 199),
|
||||
fontFamilies: ['Noto Serif'],
|
||||
textBaseline: CanvasKit.TextBaseline.Ideographic,
|
||||
decoration: CanvasKit.LineThroughDecoration,
|
||||
decorationThickness: 1.5, // multiplier based on font size
|
||||
});
|
||||
builder.pushStyle(blueText);
|
||||
builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`);
|
||||
builder.pop();
|
||||
builder.addText(` I'm done with the blue now. `);
|
||||
builder.addText(`Now I hope we should stop before we get 8 lines tall. `);
|
||||
const paragraph = builder.build();
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3);
|
||||
expect(paragraph.getHeight()).toEqual(227);
|
||||
expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3);
|
||||
expect(paragraph.getLongestLine()).toBeCloseTo(195.664, 3);
|
||||
expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1167.140, 3);
|
||||
expect(paragraph.getMaxWidth()).toEqual(200);
|
||||
expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3);
|
||||
// Check "VAVAVAVAVAVAVA"
|
||||
expect(paragraph.getWordBoundary(8)).toEqual({
|
||||
start: 0,
|
||||
end: 14,
|
||||
});
|
||||
// Check "I"
|
||||
expect(paragraph.getWordBoundary(25)).toEqual({
|
||||
start: 25,
|
||||
end: 26,
|
||||
});
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
paint.delete();
|
||||
fontMgr.delete();
|
||||
paragraph.delete();
|
||||
builder.delete();
|
||||
});
|
||||
|
||||
it('should not crash if we omit font family on pushed textStyle', () => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('test');
|
||||
expect(surface).toBeTruthy('Could not make surface');
|
||||
@ -527,7 +827,7 @@ describe('Paragraph Behavior', function() {
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
canvas.clear(CanvasKit.Color(250, 250, 250));
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
@ -578,7 +878,7 @@ describe('Paragraph Behavior', function() {
|
||||
|
||||
paragraph.layout(wrapTo);
|
||||
|
||||
canvas.clear(CanvasKit.Color(250, 250, 250));
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
|
||||
|
@ -90,7 +90,7 @@ function _report(data, outputType, testname) {
|
||||
|
||||
function reportError(done) {
|
||||
return (e) => {
|
||||
console.log("Error with fetching. Likely could not connect to aggegator server", e.message);
|
||||
console.log("Error with fetching. Likely could not connect to aggregator server", e.message);
|
||||
if (fail_on_no_gold) {
|
||||
expect(e).toBeUndefined();
|
||||
}
|
||||
@ -126,4 +126,4 @@ function catchException(done, fn) {
|
||||
// that would make the break the asynchronous nature
|
||||
// of fn().
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -580,7 +580,7 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
|
||||
return results;
|
||||
}
|
||||
|
||||
ensureUTF16Mapping();
|
||||
ensureUTF16Mapping();
|
||||
|
||||
if (start >= end || start > fUTF8IndexForUTF16Index.size() || end == 0) {
|
||||
return results;
|
||||
|
Loading…
Reference in New Issue
Block a user