Update HarfBuzz to 13.1.1 and optionally use new hb-raster API to render glyphs.

This commit is contained in:
Pāvels Nadtočajevs
2026-03-06 17:20:04 +02:00
parent 734f5473c1
commit f5d278fe90
134 changed files with 23901 additions and 6546 deletions
+22
View File
@@ -125,6 +125,8 @@
<param index="6" name="spacing_space" type="int" default="0" />
<param index="7" name="spacing_glyph" type="int" default="0" />
<param index="8" name="baseline_offset" type="float" default="0.0" />
<param index="9" name="palette_index" type="int" default="0" />
<param index="10" name="custom_colors" type="PackedColorArray" default="PackedColorArray()" />
<description>
Returns [TextServer] RID of the font cache for specific variation.
</description>
@@ -226,6 +228,26 @@
Returns [Dictionary] with OpenType font name strings (localized font names, version, description, license information, sample text, etc.).
</description>
</method>
<method name="get_palette_colors" qualifiers="const">
<return type="PackedColorArray" />
<param index="0" name="index" type="int" />
<description>
Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [FontVariation].
</description>
</method>
<method name="get_palette_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
</description>
</method>
<method name="get_palette_name" qualifiers="const">
<return type="String" />
<param index="0" name="index" type="int" />
<description>
Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
</description>
</method>
<method name="get_rids" qualifiers="const">
<return type="RID[]" />
<description>
+6
View File
@@ -52,6 +52,12 @@
<member name="opentype_features" type="Dictionary" setter="set_opentype_features" getter="get_opentype_features" default="{}">
A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url].
</member>
<member name="palette_custom_colors" type="PackedColorArray" setter="set_palette_custom_colors" getter="get_palette_custom_colors" default="PackedColorArray()">
An array of colors to override predefined palette. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position.
</member>
<member name="palette_index" type="int" setter="set_palette_index" getter="get_palette_index" default="0">
A palette index.
</member>
<member name="spacing_bottom" type="int" setter="set_spacing" getter="get_spacing" default="0">
Extra spacing at the bottom of the line in pixels.
</member>
+54 -1
View File
@@ -406,6 +406,36 @@
Returns oversampling factor override. If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
</description>
</method>
<method name="font_get_palette_colors" qualifiers="const">
<return type="PackedColorArray" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [method font_set_palette_custom_colors].
</description>
</method>
<method name="font_get_palette_count" qualifiers="const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
</description>
</method>
<method name="font_get_palette_custom_colors" qualifiers="const">
<return type="PackedColorArray" />
<param index="0" name="font_rid" type="RID" />
<description>
Returns array of custom colors to override predefined palette.
</description>
</method>
<method name="font_get_palette_name" qualifiers="const">
<return type="String" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
</description>
</method>
<method name="font_get_scale" qualifiers="const">
<return type="float" />
<param index="0" name="font_rid" type="RID" />
@@ -542,6 +572,13 @@
Returns thickness of the underline in pixels.
</description>
</method>
<method name="font_get_used_palette" qualifiers="const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
Returns used palette index.
</description>
</method>
<method name="font_get_variation_coordinates" qualifiers="const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
@@ -882,7 +919,7 @@
<method name="font_set_modulate_color_glyphs">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="force_autohinter" type="bool" />
<param index="1" name="modulate" type="bool" />
<description>
If set to [code]true[/code], color modulation is applied when drawing colored glyphs, otherwise it's applied to the monochrome glyphs only.
</description>
@@ -936,6 +973,14 @@
If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
</description>
</method>
<method name="font_set_palette_custom_colors">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="colors" type="PackedColorArray" />
<description>
Sets array of custom colors to override predefined palette. Set to empty array to reset overrides. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position.
</description>
</method>
<method name="font_set_scale">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
@@ -1044,6 +1089,14 @@
Sets thickness of the underline in pixels.
</description>
</method>
<method name="font_set_used_palette">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="index" type="int" />
<description>
Sets used palette index.
</description>
</method>
<method name="font_set_variation_coordinates">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
+53
View File
@@ -384,6 +384,36 @@
Returns oversampling factor override. If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
</description>
</method>
<method name="_font_get_palette_colors" qualifiers="virtual const">
<return type="PackedColorArray" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [method _font_set_palette_custom_colors].
</description>
</method>
<method name="_font_get_palette_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
</description>
</method>
<method name="_font_get_palette_custom_colors" qualifiers="virtual const">
<return type="PackedColorArray" />
<param index="0" name="font_rid" type="RID" />
<description>
Returns array of custom colors to override predefined palette.
</description>
</method>
<method name="_font_get_palette_name" qualifiers="virtual const">
<return type="String" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="index" type="int" />
<description>
Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors.
</description>
</method>
<method name="_font_get_scale" qualifiers="virtual required const">
<return type="float" />
<param index="0" name="font_rid" type="RID" />
@@ -520,6 +550,13 @@
Returns thickness of the underline in pixels.
</description>
</method>
<method name="_font_get_used_palette" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
Returns used palette index.
</description>
</method>
<method name="_font_get_variation_coordinates" qualifiers="virtual const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
@@ -919,6 +956,14 @@
If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only.
</description>
</method>
<method name="_font_set_palette_custom_colors" qualifiers="virtual">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="colors" type="PackedColorArray" />
<description>
Sets array of custom colors to override predefined palette. Set to empty array to reset overrides. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position.
</description>
</method>
<method name="_font_set_scale" qualifiers="virtual required">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
@@ -1024,6 +1069,14 @@
Sets thickness of the underline in pixels.
</description>
</method>
<method name="_font_set_used_palette" qualifiers="virtual">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
<param index="1" name="index" type="int" />
<description>
Sets used palette index.
</description>
</method>
<method name="_font_set_variation_coordinates" qualifiers="virtual">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
@@ -167,6 +167,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["bptc"] = "BPTC";
capitalize_string_remaps["bvh"] = "BVH";
capitalize_string_remaps["ca"] = "CA";
capitalize_string_remaps["colr"] = "COLR";
capitalize_string_remaps["ccdik"] = "CCDIK";
capitalize_string_remaps["cd"] = "CD";
capitalize_string_remaps["cpu"] = "CPU";
@@ -1,5 +1,5 @@
GH-80954
--------
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 9.
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 11.
Added optional arguments. Compatibility method registered.
@@ -1,5 +1,5 @@
GH-87668
--------
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 8 to 9.
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 8 to 11.
Added optional "baseline_offset" argument. Compatibility method registered.
@@ -0,0 +1,5 @@
GH-117149
---------
Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 9 to 11.
Optional "palette_index" and "custom_colors" arguments added. Compatibility method registered.
+23 -1
View File
@@ -99,6 +99,7 @@ if env["builtin_harfbuzz"]:
# "src/hb-subset-cff-common.cc",
# "src/hb-subset-cff1.cc",
# "src/hb-subset-cff2.cc",
# "src/hb-subset-cff2-to-cff1.cc",
# "src/hb-subset-input.cc",
# "src/hb-subset-instancer-iup.cc",
# "src/hb-subset-instancer-solver.cc",
@@ -109,9 +110,30 @@ if env["builtin_harfbuzz"]:
# "src/hb-subset-table-layout.cc",
# "src/hb-subset-table-other.cc",
# "src/hb-subset-table-var.cc",
"src/hb-raster.cc",
"src/hb-raster-draw.cc",
"src/hb-raster-image.cc",
"src/hb-raster-paint.cc",
"src/hb-raster-svg-base.cc",
"src/hb-raster-svg-bbox.cc",
"src/hb-raster-svg-color.cc",
"src/hb-raster-svg-clip.cc",
"src/hb-raster-svg-defs.cc",
"src/hb-raster-svg-defs-scan.cc",
"src/hb-raster-svg-gradient.cc",
"src/hb-raster-svg-fill.cc",
"src/hb-raster-svg-render.cc",
"src/hb-raster-svg-parse.cc",
"src/hb-raster-svg-use.cc",
"src/hb-vector-svg-draw.cc",
"src/hb-vector-svg-paint.cc",
"src/hb-vector-svg-path.cc",
"src/hb-vector-svg-subset.cc",
"src/hb-vector-svg-utils.cc",
"src/hb-subset.cc",
"src/hb-ucd.cc",
"src/hb-unicode.cc",
"src/hb-zlib.cc",
# "src/hb-uniscribe.cc",
"src/OT/Var/VARC/VARC.cc",
]
@@ -128,7 +150,7 @@ if env["builtin_harfbuzz"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU"])
env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU", "HAVE_ZLIB", "HAVE_PNG"])
if env["builtin_icu4c"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
env_harfbuzz.Append(
+287 -18
View File
@@ -1085,6 +1085,72 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
#if HB_VERSION_ATLEAST(13, 0, 0)
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_hb_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, hb_raster_image_t *p_image, const hb_raster_extents_t &p_ext, const Vector2 &p_advance, bool p_bgra) const {
FontGlyph chr;
chr.advance = p_advance * p_data->scale;
chr.found = true;
int w = p_ext.width;
int h = p_ext.height;
if (w == 0 || h == 0 || p_image == nullptr) {
chr.texture_idx = -1;
chr.uv_rect = Rect2();
chr.rect = Rect2();
return chr;
}
int color_size = p_bgra ? 4 : 2;
int mw = w + p_rect_margin * 4;
int mh = h + p_rect_margin * 4;
ERR_FAIL_COND_V(mw > 4096, FontGlyph());
ERR_FAIL_COND_V(mh > 4096, FontGlyph());
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
const uint8_t *img_src = hb_raster_image_get_buffer(p_image);
// Fit character in char texture.
ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
{
uint8_t *wr = tex.image->ptrw();
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size;
ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph());
if (p_bgra) {
int ofs_color = i * p_ext.stride + (j << 2);
wr[ofs + 2] = img_src[ofs_color + 0];
wr[ofs + 1] = img_src[ofs_color + 1];
wr[ofs + 0] = img_src[ofs_color + 2];
wr[ofs + 3] = img_src[ofs_color + 3];
} else {
wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = img_src[i * p_ext.stride + j];
}
}
}
}
tex.dirty = true;
chr.texture_idx = tex_pos.index;
chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);
chr.rect.position = Vector2(p_ext.x_origin - p_rect_margin, p_ext.y_origin - p_rect_margin) * p_data->scale;
chr.rect.size = chr.uv_rect.size * p_data->scale;
return chr;
}
#endif
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const {
FontGlyph chr;
chr.advance = p_advance * p_data->scale;
@@ -1093,7 +1159,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
int w = p_bitmap.width;
int h = p_bitmap.rows;
if (w == 0 || h == 0) {
if (w == 0 || h == 0 || p_bitmap.buffer == nullptr) {
chr.texture_idx = -1;
chr.uv_rect = Rect2();
chr.rect = Rect2();
@@ -1335,21 +1401,85 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
}
FT_GlyphSlot slot = p_font_data->face->glyph;
bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.
bool fix_edge = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.
bool from_bitmap = (slot->format == FT_GLYPH_FORMAT_BITMAP);
if (!outline) {
if (!p_font_data->msdf) {
error = FT_Render_Glyph(slot, aa_mode);
}
if (!error) {
if (p_font_data->msdf) {
if (p_font_data->msdf) {
#ifdef MODULE_MSDFGEN_ENABLED
gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
#else
fd->glyph_map[p_glyph] = FontGlyph();
ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
fd->glyph_map[p_glyph] = FontGlyph();
ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
#endif
} else {
#if HB_VERSION_ATLEAST(13, 0, 0)
if (hb_rdr && hb_mono && p_font_data->antialiasing == FONT_ANTIALIASING_GRAY && !from_bitmap) {
bool is_rasterized = false;
float xshift = 0.0;
if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) {
xshift = float((int)((p_glyph >> 27) & 3) << 4);
} else if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) {
xshift = float((int)((p_glyph >> 27) & 3) << 5);
}
xshift += (p_font_data->embolden * double(p_size.x) / 64.0);
fix_edge = false;
if (fd->color_paint) {
hb_raster_paint_reset(hb_rdr);
if (p_font_data->transform != Transform2D()) {
hb_raster_paint_set_transform(hb_rdr, p_font_data->transform[0][0], p_font_data->transform[1][0], p_font_data->transform[0][1], -p_font_data->transform[1][1], 0.f, 0.f);
} else {
hb_raster_paint_set_transform(hb_rdr, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f);
}
hb_raster_paint_set_scale_factor(hb_rdr, 64.0, 64.0);
hb_raster_paint_clear_custom_palette_colors(hb_rdr);
if (!p_font_data->palette_custom_colors_hb.is_empty()) {
for (int col = 0; col < p_font_data->palette_custom_colors_hb.size(); col++) {
if (p_font_data->palette_custom_colors_hb[col] != 0) {
hb_raster_paint_set_custom_palette_color(hb_rdr, col, p_font_data->palette_custom_colors_hb[col]);
}
}
}
bool ok = hb_raster_paint_glyph(hb_rdr, fd->hb_handle, (hb_codepoint_t)glyph_index, xshift, 0, p_font_data->palette_index, (hb_color_t)0xFFFFFFFF);
if (ok) {
is_rasterized = true;
hb_raster_image_t *img = hb_raster_paint_render(hb_rdr);
hb_raster_extents_t ext = { 0, 0, 0, 0, 0 };
if (img) {
hb_raster_image_get_extents(img, &ext);
gl = rasterize_hb_bitmap(fd, rect_range, img, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, true);
hb_raster_paint_recycle_image(hb_rdr, img);
} else {
gl = rasterize_hb_bitmap(fd, rect_range, nullptr, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, true);
}
}
}
if (!is_rasterized) {
hb_raster_draw_reset(hb_mono);
if (p_font_data->transform != Transform2D()) {
hb_raster_draw_set_transform(hb_mono, p_font_data->transform[0][0], p_font_data->transform[1][0], p_font_data->transform[0][1], -p_font_data->transform[1][1], 0.f, 0.f);
} else {
hb_raster_draw_set_transform(hb_mono, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f);
}
hb_raster_draw_set_scale_factor(hb_mono, 64.0, 64.0);
hb_raster_draw_glyph(hb_mono, fd->hb_handle, (hb_codepoint_t)glyph_index, xshift, 0);
hb_raster_image_t *img = hb_raster_draw_render(hb_mono);
hb_raster_extents_t ext = { 0, 0, 0, 0, 0 };
if (img) {
hb_raster_image_get_extents(img, &ext);
gl = rasterize_hb_bitmap(fd, rect_range, img, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, false);
hb_raster_draw_recycle_image(hb_mono, img);
} else {
gl = rasterize_hb_bitmap(fd, rect_range, nullptr, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, false);
}
}
} else {
gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);
#else
{
#endif
error = FT_Render_Glyph(slot, aa_mode);
if (!error) {
gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);
}
}
}
} else {
@@ -1380,7 +1510,7 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i
cleanup_stroker:
FT_Stroker_Done(stroker);
}
gl.from_svg = from_svg;
gl.fix_edge = fix_edge;
E = fd->glyph_map.insert(p_glyph, gl);
r_glyph = E->value;
return gl.found;
@@ -1501,6 +1631,10 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
fd->hb_handle = hb_ft_font_create(p_font_data->face, nullptr);
if (p_font_data->embolden != 0.0) {
hb_font_set_synthetic_bold(fd->hb_handle, p_font_data->embolden / 16.0, p_font_data->embolden / 16.0, true);
}
fd->ascent = (p_font_data->face->size->metrics.ascender / 64.0) * fd->scale;
fd->descent = (-p_font_data->face->size->metrics.descender / 64.0) * fd->scale;
fd->underline_position = (-FT_MulFix(p_font_data->face->underline_position, p_font_data->face->size->metrics.y_scale) / 64.0) * fd->scale;
@@ -1512,16 +1646,56 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const
#ifndef _MSC_VER
#warning Building with HarfBuzz < 3.3.0, synthetic slant offset correction disabled.
#endif
#endif
#if HB_VERSION_ATLEAST(13, 0, 0)
fd->hb_face = hb_font_get_face(fd->hb_handle);
fd->color_paint = (hb_ot_color_has_paint(fd->hb_face) || hb_ot_color_has_layers(fd->hb_face));
#endif
if (!p_font_data->face_init) {
const static hb_language_t english = hb_language_from_string("en", -1);
#if HB_VERSION_ATLEAST(13, 0, 0)
hb_face_t *hb_face = fd->hb_face;
p_font_data->palette_names.clear();
p_font_data->palette_colors.clear();
unsigned int pal_count = hb_ot_color_palette_get_count(hb_face);
for (unsigned int pal = 0; pal < pal_count; pal++) {
String pal_name;
Vector<Color> pal_colors;
// Get name.
hb_ot_name_id_t name_id = hb_ot_color_palette_get_name_id(hb_face, pal);
if (name_id == HB_OT_NAME_ID_INVALID) {
pal_name = vformat("Palette %d", pal + 1);
} else {
unsigned int text_size = hb_ot_name_get_utf32(hb_face, name_id, english, nullptr, nullptr) + 1;
pal_name.resize_uninitialized(text_size);
hb_ot_name_get_utf32(hb_face, name_id, english, &text_size, (uint32_t *)pal_name.ptrw());
}
// Get colors.
unsigned int color_count = 0;
color_count = hb_ot_color_palette_get_colors(hb_face, pal, 0, &color_count, nullptr);
Vector<hb_color_t> pal_colors_hb;
pal_colors_hb.resize_uninitialized(color_count);
hb_ot_color_palette_get_colors(hb_face, pal, 0, &color_count, pal_colors_hb.ptrw());
for (unsigned int col = 0; col < color_count; col++) {
Color c = Color::from_rgba8(hb_color_get_red(pal_colors_hb[col]), hb_color_get_green(pal_colors_hb[col]), hb_color_get_blue(pal_colors_hb[col]), hb_color_get_alpha(pal_colors_hb[col]));
pal_colors.push_back(c);
}
p_font_data->palette_names.push_back(pal_name);
p_font_data->palette_colors.push_back(pal_colors);
}
#else
hb_face_t *hb_face = hb_font_get_face(fd->hb_handle);
#endif
// When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names.
// FreeType automatically converts non-ASCII characters to "?" in the synthesized name.
// To avoid that behavior, use the format-specific name directly if available.
hb_face_t *hb_face = hb_font_get_face(fd->hb_handle);
unsigned int num_entries = 0;
const hb_ot_name_entry_t *names = hb_ot_name_list_names(hb_face, &num_entries);
const hb_language_t english = hb_language_from_string("en", -1);
for (unsigned int i = 0; i < num_entries; i++) {
if (names[i].name_id != HB_OT_NAME_ID_FONT_FAMILY) {
continue;
@@ -2603,6 +2777,90 @@ bool TextServerAdvanced::_font_is_modulate_color_glyphs(const RID &p_font_rid) c
return fd->modulate_color_glyphs;
}
int64_t TextServerAdvanced::_font_get_palette_count(const RID &p_font_rid) const {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, 0);
#if HB_VERSION_ATLEAST(13, 0, 0)
return fd->palette_names.size();
#else
return 0;
#endif
}
String TextServerAdvanced::_font_get_palette_name(const RID &p_font_rid, int64_t p_index) const {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, String());
#if HB_VERSION_ATLEAST(13, 0, 0)
ERR_FAIL_INDEX_V(p_index, fd->palette_names.size(), String());
return fd->palette_names[p_index];
#else
return String();
#endif
}
Vector<Color> TextServerAdvanced::_font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, Vector<Color>());
#if HB_VERSION_ATLEAST(13, 0, 0)
ERR_FAIL_INDEX_V(p_index, fd->palette_names.size(), Vector<Color>());
return fd->palette_colors[p_index];
#else
return Vector<Color>();
#endif
}
void TextServerAdvanced::_font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
#if HB_VERSION_ATLEAST(13, 0, 0)
if (fd->palette_custom_colors != p_colors) {
fd->palette_custom_colors = p_colors;
fd->palette_custom_colors_hb.resize_uninitialized(p_colors.size());
for (int col = 0; col < fd->palette_custom_colors.size(); col++) {
const Color &c = fd->palette_custom_colors[col];
fd->palette_custom_colors_hb.write[col] = HB_COLOR(c.get_b8(), c.get_g8(), c.get_r8(), c.get_a8());
}
_font_clear_cache(fd);
}
#endif
}
Vector<Color> TextServerAdvanced::_font_get_palette_custom_colors(const RID &p_font_rid) const {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, Vector<Color>());
#if HB_VERSION_ATLEAST(13, 0, 0)
return fd->palette_custom_colors;
#else
return Vector<Color>();
#endif
}
int64_t TextServerAdvanced::_font_get_used_palette(const RID &p_font_rid) const {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, 0);
#if HB_VERSION_ATLEAST(13, 0, 0)
return fd->palette_index;
#else
return 0;
#endif
}
void TextServerAdvanced::_font_set_used_palette(const RID &p_font_rid, int64_t p_index) {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
#if HB_VERSION_ATLEAST(13, 0, 0)
if (fd->palette_index != p_index) {
fd->palette_index = p_index;
_font_clear_cache(fd);
}
#endif
}
void TextServerAdvanced::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
@@ -3508,7 +3766,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const
if (ffsd->textures[fgl.texture_idx].dirty) {
ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
Ref<Image> img = tex.image;
if (fgl.from_svg) {
if (fgl.fix_edge) {
// Same as the "fix alpha border" process option when importing SVGs
img->fix_alpha_edges();
}
@@ -3558,7 +3816,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co
if (ffsd->textures[fgl.texture_idx].dirty) {
ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
Ref<Image> img = tex.image;
if (fgl.from_svg) {
if (fgl.fix_edge) {
// Same as the "fix alpha border" process option when importing SVGs
img->fix_alpha_edges();
}
@@ -4034,7 +4292,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
if (ffsd->textures[fgl.texture_idx].dirty) {
ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
Ref<Image> img = tex.image;
if (fgl.from_svg) {
if (fgl.fix_edge) {
// Same as the "fix alpha border" process option when importing SVGs
img->fix_alpha_edges();
}
@@ -8119,7 +8377,10 @@ void TextServerAdvanced::_update_settings() {
TextServerAdvanced::TextServerAdvanced() {
os_locale = OS::get_singleton()->get_locale();
#if HB_VERSION_ATLEAST(13, 0, 0)
hb_rdr = hb_raster_paint_create_or_fail();
hb_mono = hb_raster_draw_create_or_fail();
#endif
_insert_feature_sets();
_bmp_create_font_funcs();
_update_settings();
@@ -8144,6 +8405,14 @@ void TextServerAdvanced::_cleanup() {
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
#if HB_VERSION_ATLEAST(13, 0, 0)
if (hb_rdr != nullptr) {
hb_raster_paint_destroy(hb_rdr);
}
if (hb_mono != nullptr) {
hb_raster_draw_destroy(hb_mono);
}
#endif
#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
FT_Done_FreeType(ft_library);
+32 -1
View File
@@ -91,6 +91,9 @@ GODOT_CLANG_WARNING_POP
#include <hb-icu.h>
#include <hb.h>
#if HB_VERSION_ATLEAST(13, 0, 0)
#include <hb-raster.h>
#endif
/*************************************************************************/
@@ -228,7 +231,7 @@ class TextServerAdvanced : public TextServerExtension {
Rect2 rect;
Rect2 uv_rect;
Vector2 advance;
bool from_svg = false;
bool fix_edge = false;
};
struct FontAdvanced;
@@ -249,6 +252,10 @@ class TextServerAdvanced : public TextServerExtension {
HashMap<int32_t, FontGlyph> glyph_map;
HashMap<Vector2i, Vector2> kerning_map;
hb_font_t *hb_handle = nullptr;
#if HB_VERSION_ATLEAST(13, 0, 0)
hb_face_t *hb_face = nullptr;
bool color_paint = false;
#endif
#ifdef MODULE_FREETYPE_ENABLED
FT_Size fsize = nullptr;
@@ -311,6 +318,14 @@ class TextServerAdvanced : public TextServerExtension {
HashMap<Vector2i, FontForSizeAdvanced *> cache;
#if HB_VERSION_ATLEAST(13, 0, 0)
Vector<String> palette_names;
Vector<PackedColorArray> palette_colors;
PackedColorArray palette_custom_colors;
Vector<hb_color_t> palette_custom_colors_hb;
unsigned int palette_index = 0;
#endif
bool face_init = false;
HashSet<uint32_t> supported_scripts;
Dictionary supported_features;
@@ -350,6 +365,9 @@ class TextServerAdvanced : public TextServerExtension {
#endif
#ifdef MODULE_FREETYPE_ENABLED
_FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const;
#if HB_VERSION_ATLEAST(13, 0, 0)
_FORCE_INLINE_ FontGlyph rasterize_hb_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, hb_raster_image_t *p_image, const hb_raster_extents_t &p_ext, const Vector2 &p_advance, bool p_bgra) const;
#endif
#endif
bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph, uint32_t p_oversampling = 0) const;
bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size, FontForSizeAdvanced *&r_cache_for_size, bool p_silent = false, uint32_t p_oversampling = 0) const;
@@ -555,6 +573,11 @@ class TextServerAdvanced : public TextServerExtension {
mutable RID_PtrOwner<FontAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
#if HB_VERSION_ATLEAST(13, 0, 0)
hb_raster_paint_t *hb_rdr = nullptr;
hb_raster_draw_t *hb_mono = nullptr;
#endif
_FORCE_INLINE_ FontAdvanced *_get_font_data(const RID &p_font_rid) const {
RID rid = p_font_rid;
FontAdvancedLinkedVariation *fdv = font_var_owner.get_or_null(rid);
@@ -870,6 +893,14 @@ public:
MODBIND2(font_set_modulate_color_glyphs, const RID &, bool);
MODBIND1RC(bool, font_is_modulate_color_glyphs, const RID &);
MODBIND1RC(int64_t, font_get_palette_count, const RID &);
MODBIND2RC(String, font_get_palette_name, const RID &, int64_t);
MODBIND2RC(Vector<Color>, font_get_palette_colors, const RID &, int64_t);
MODBIND2(font_set_palette_custom_colors, const RID &, const Vector<Color> &);
MODBIND1RC(Vector<Color>, font_get_palette_custom_colors, const RID &);
MODBIND1RC(int64_t, font_get_used_palette, const RID &);
MODBIND2(font_set_used_palette, const RID &, int64_t);
MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning);
MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &);
+45
View File
@@ -1549,6 +1549,51 @@ bool TextServerFallback::_font_is_modulate_color_glyphs(const RID &p_font_rid) c
return fd->modulate_color_glyphs;
}
int64_t TextServerFallback::_font_get_palette_count(const RID &p_font_rid) const {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, 0);
return 0;
}
String TextServerFallback::_font_get_palette_name(const RID &p_font_rid, int64_t p_index) const {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, String());
return String();
}
Vector<Color> TextServerFallback::_font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, Vector<Color>());
return Vector<Color>();
}
void TextServerFallback::_font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
}
Vector<Color> TextServerFallback::_font_get_palette_custom_colors(const RID &p_font_rid) const {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, Vector<Color>());
return Vector<Color>();
}
int64_t TextServerFallback::_font_get_used_palette(const RID &p_font_rid) const {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL_V(fd, 0);
return 0;
}
void TextServerFallback::_font_set_used_palette(const RID &p_font_rid, int64_t p_index) {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
}
void TextServerFallback::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
+8
View File
@@ -649,6 +649,14 @@ public:
MODBIND2(font_set_modulate_color_glyphs, const RID &, bool);
MODBIND1RC(bool, font_is_modulate_color_glyphs, const RID &);
MODBIND1RC(int64_t, font_get_palette_count, const RID &);
MODBIND2RC(String, font_get_palette_name, const RID &, int64_t);
MODBIND2RC(Vector<Color>, font_get_palette_colors, const RID &, int64_t);
MODBIND2(font_set_palette_custom_colors, const RID &, const Vector<Color> &);
MODBIND1RC(Vector<Color>, font_get_palette_custom_colors, const RID &);
MODBIND1RC(int64_t, font_get_used_palette, const RID &);
MODBIND2(font_set_used_palette, const RID &, int64_t);
MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning);
MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &);
+7 -2
View File
@@ -59,11 +59,15 @@ real_t Font::_draw_char_outline_bind_compat_104872(RID p_canvas_item, const Poin
}
RID Font::_find_variation_bind_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const {
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0, 0.0);
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0, 0.0, 0, Vector<Color>());
}
RID Font::_find_variation_bind_compat_87668(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph) const {
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, 0.0);
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, 0.0, 0, Vector<Color>());
}
RID Font::_find_variation_bind_compat_117149(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, 0, Vector<Color>());
}
void Font::_bind_compatibility_methods() {
@@ -78,6 +82,7 @@ void Font::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::_find_variation_bind_compat_80954, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()));
ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph"), &Font::_find_variation_bind_compat_87668, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0));
ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset"), &Font::_find_variation_bind_compat_117149, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0));
}
#endif // DISABLE_DEPRECATED
+100 -8
View File
@@ -53,7 +53,7 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks);
// Output.
ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset", "palette_index", "custom_colors"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0), DEFVAL(0), DEFVAL(Vector<Color>()));
ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids);
// Font metrics.
@@ -70,6 +70,10 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_font_weight"), &Font::get_font_weight);
ClassDB::bind_method(D_METHOD("get_font_stretch"), &Font::get_font_stretch);
ClassDB::bind_method(D_METHOD("get_palette_count"), &Font::get_palette_count);
ClassDB::bind_method(D_METHOD("get_palette_name", "index"), &Font::get_palette_name);
ClassDB::bind_method(D_METHOD("get_palette_colors", "index"), &Font::get_palette_colors);
ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing);
ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features);
@@ -270,6 +274,18 @@ String Font::get_font_name() const {
return TS->font_get_name(_get_rid());
}
int64_t Font::get_palette_count() const {
return TS->font_get_palette_count(_get_rid());
}
String Font::get_palette_name(int64_t p_index) const {
return TS->font_get_palette_name(_get_rid(), p_index);
}
Vector<Color> Font::get_palette_colors(int64_t p_index) const {
return TS->font_get_palette_colors(_get_rid(), p_index);
}
Dictionary Font::get_ot_name_strings() const {
return TS->font_get_ot_name_strings(_get_rid());
}
@@ -2366,7 +2382,7 @@ real_t FontFile::get_oversampling() const {
return oversampling_override;
}
RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector<Color> &p_custom_colors) const {
// Find existing variation cache.
const Dictionary &supported_coords = get_supported_variation_list();
int make_linked_from = -1;
@@ -2378,6 +2394,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
match = match && (TS->font_get_face_index(cache[i]) == p_face_index);
match = match && (TS->font_get_embolden(cache[i]) == p_strength);
match = match && (TS->font_get_transform(cache[i]) == p_transform);
match = match && (TS->font_get_used_palette(cache[i]) == p_palette_index);
match = match && (TS->font_get_palette_custom_colors(cache[i]) == p_custom_colors);
match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_TOP) == p_spacing_top);
match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_BOTTOM) == p_spacing_bottom);
match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_SPACE) == p_spacing_space);
@@ -2427,6 +2445,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space);
TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph);
TS->font_set_baseline_offset(cache[idx], p_baseline_offset);
TS->font_set_used_palette(cache[idx], p_palette_index);
TS->font_set_palette_custom_colors(cache[idx], p_custom_colors);
} else {
_ensure_rid(idx);
TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates);
@@ -2438,6 +2458,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa
TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space);
TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph);
TS->font_set_baseline_offset(cache[idx], p_baseline_offset);
TS->font_set_used_palette(cache[idx], p_palette_index);
TS->font_set_palette_custom_colors(cache[idx], p_custom_colors);
}
return cache[idx];
}
@@ -2905,6 +2927,12 @@ void FontVariation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_baseline_offset", "baseline_offset"), &FontVariation::set_baseline_offset);
ClassDB::bind_method(D_METHOD("get_baseline_offset"), &FontVariation::get_baseline_offset);
ClassDB::bind_method(D_METHOD("get_palette_index"), &FontVariation::get_palette_index);
ClassDB::bind_method(D_METHOD("set_palette_index", "palette_index"), &FontVariation::set_palette_index);
ClassDB::bind_method(D_METHOD("get_palette_custom_colors"), &FontVariation::get_palette_custom_colors);
ClassDB::bind_method(D_METHOD("set_palette_custom_colors", "colors"), &FontVariation::set_palette_custom_colors);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, Font::get_class_static()), "set_base_font", "get_base_font");
ADD_GROUP("Variation", "variation_");
@@ -2924,6 +2952,46 @@ void FontVariation::_bind_methods() {
ADD_GROUP("Baseline", "baseline_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "baseline_offset", PROPERTY_HINT_RANGE, "-2,2,0.005"), "set_baseline_offset", "get_baseline_offset");
ADD_GROUP("Palette", "palette_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "palette_index", PROPERTY_HINT_NONE, ""), "set_palette_index", "get_palette_index");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "palette_custom_colors", PROPERTY_HINT_NONE, ""), "set_palette_custom_colors", "get_palette_custom_colors");
}
void FontVariation::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "palette_custom_colors") {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
int64_t pal_count = f->get_palette_count();
if (pal_count == 0 || f->get_palette_colors(0).is_empty()) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
} else if (p_property.name == "palette_index") {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
p_property.hint = PROPERTY_HINT_ENUM;
p_property.hint_string = String();
int64_t pal_count = f->get_palette_count();
if (pal_count == 0) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
} else {
for (int64_t i = 0; i < pal_count; i++) {
if (!p_property.hint_string.is_empty()) {
p_property.hint_string += ",";
}
p_property.hint_string += f->get_palette_name(i);
}
}
} else {
p_property.hint = PROPERTY_HINT_NONE;
p_property.hint_string = String();
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
}
void FontVariation::_update_rids() const {
@@ -2965,6 +3033,8 @@ void FontVariation::reset_state() {
extra_spacing[i] = 0;
}
baseline_offset = 0.0;
palette_index = 0;
custom_colors.clear();
Font::reset_state();
}
@@ -3120,10 +3190,32 @@ float FontVariation::get_baseline_offset() const {
return baseline_offset;
}
RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
void FontVariation::set_palette_index(int64_t p_palette_index) {
if (palette_index != p_palette_index) {
palette_index = p_palette_index;
_invalidate_rids();
}
}
int64_t FontVariation::get_palette_index() const {
return palette_index;
}
void FontVariation::set_palette_custom_colors(const Vector<Color> &p_colors) {
if (custom_colors != p_colors) {
custom_colors = p_colors;
_invalidate_rids();
}
}
Vector<Color> FontVariation::get_palette_custom_colors() const {
return custom_colors;
}
RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector<Color> &p_custom_colors) const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors);
}
return RID();
}
@@ -3131,7 +3223,7 @@ RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int
RID FontVariation::_get_rid() const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH], baseline_offset);
return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH], baseline_offset, palette_index, custom_colors);
}
return RID();
}
@@ -3651,7 +3743,7 @@ int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const {
}
}
RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const {
RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector<Color> &p_custom_colors) const {
Ref<Font> f = _get_base_font_or_default();
if (f.is_valid()) {
Dictionary var = p_variation_coordinates;
@@ -3667,9 +3759,9 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_
if (!face_indices.is_empty()) {
int face_index = CLAMP(p_face_index, 0, face_indices.size() - 1);
return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors);
} else {
return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset);
return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors);
}
}
return RID();
+18 -4
View File
@@ -108,6 +108,7 @@ protected:
real_t _draw_char_outline_bind_compat_104872(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
RID _find_variation_bind_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const;
RID _find_variation_bind_compat_87668(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const;
RID _find_variation_bind_compat_117149(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const;
static void _bind_compatibility_methods();
#endif
@@ -123,7 +124,7 @@ public:
virtual TypedArray<Font> get_fallbacks() const;
// Output.
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const { return RID(); }
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const { return RID(); }
virtual RID _get_rid() const { return RID(); }
virtual TypedArray<RID> get_rids() const;
@@ -141,6 +142,10 @@ public:
virtual int get_font_weight() const;
virtual int get_font_stretch() const;
virtual int64_t get_palette_count() const;
virtual String get_palette_name(int64_t p_index) const;
virtual Vector<Color> get_palette_colors(int64_t p_index) const;
virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; }
virtual Dictionary get_opentype_features() const;
@@ -300,7 +305,7 @@ public:
virtual real_t get_oversampling() const;
// Cache.
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override;
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const override;
virtual RID _get_rid() const override;
virtual int get_cache_count() const;
@@ -427,9 +432,12 @@ class FontVariation : public Font {
Dictionary opentype_features;
int extra_spacing[TextServer::SPACING_MAX];
float baseline_offset = 0.0;
int64_t palette_index = 0;
Vector<Color> custom_colors;
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
virtual void _update_rids() const override;
@@ -461,8 +469,14 @@ public:
virtual float get_baseline_offset() const;
virtual void set_baseline_offset(float p_baseline_offset);
virtual int64_t get_palette_index() const;
virtual void set_palette_index(int64_t p_palette_index);
virtual Vector<Color> get_palette_custom_colors() const;
virtual void set_palette_custom_colors(const Vector<Color> &p_colors);
// Output.
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override;
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const override;
virtual RID _get_rid() const override;
FontVariation();
@@ -568,7 +582,7 @@ public:
virtual int get_spacing(TextServer::SpacingType p_spacing) const override;
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override;
virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector<Color> &p_custom_colors = Vector<Color>()) const override;
virtual RID _get_rid() const override;
int64_t get_face_count() const override;
+9 -1
View File
@@ -276,9 +276,17 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter);
ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter);
ClassDB::bind_method(D_METHOD("font_set_modulate_color_glyphs", "font_rid", "force_autohinter"), &TextServer::font_set_modulate_color_glyphs);
ClassDB::bind_method(D_METHOD("font_set_modulate_color_glyphs", "font_rid", "modulate"), &TextServer::font_set_modulate_color_glyphs);
ClassDB::bind_method(D_METHOD("font_is_modulate_color_glyphs", "font_rid"), &TextServer::font_is_modulate_color_glyphs);
ClassDB::bind_method(D_METHOD("font_get_palette_count", "font_rid"), &TextServer::font_get_palette_count);
ClassDB::bind_method(D_METHOD("font_get_palette_name", "font_rid", "index"), &TextServer::font_get_palette_name);
ClassDB::bind_method(D_METHOD("font_get_palette_colors", "font_rid", "index"), &TextServer::font_get_palette_colors);
ClassDB::bind_method(D_METHOD("font_set_palette_custom_colors", "font_rid", "colors"), &TextServer::font_set_palette_custom_colors);
ClassDB::bind_method(D_METHOD("font_get_palette_custom_colors", "font_rid"), &TextServer::font_get_palette_custom_colors);
ClassDB::bind_method(D_METHOD("font_get_used_palette", "font_rid"), &TextServer::font_get_used_palette);
ClassDB::bind_method(D_METHOD("font_set_used_palette", "font_rid", "index"), &TextServer::font_set_used_palette);
ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "hinting"), &TextServer::font_set_hinting);
ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting);
+8
View File
@@ -332,6 +332,14 @@ public:
virtual void font_set_modulate_color_glyphs(const RID &p_font_rid, bool p_modulate) = 0;
virtual bool font_is_modulate_color_glyphs(const RID &p_font_rid) const = 0;
virtual int64_t font_get_palette_count(const RID &p_font_rid) const = 0;
virtual String font_get_palette_name(const RID &p_font_rid, int64_t p_index) const = 0;
virtual Vector<Color> font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const = 0;
virtual void font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) = 0;
virtual Vector<Color> font_get_palette_custom_colors(const RID &p_font_rid) const = 0;
virtual int64_t font_get_used_palette(const RID &p_font_rid) const = 0;
virtual void font_set_used_palette(const RID &p_font_rid, int64_t p_index) = 0;
virtual void font_set_hinting(const RID &p_font_rid, Hinting p_hinting) = 0;
virtual Hinting font_get_hinting(const RID &p_font_rid) const = 0;
+45
View File
@@ -115,6 +115,14 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_font_set_modulate_color_glyphs, "font_rid", "modulate");
GDVIRTUAL_BIND(_font_is_modulate_color_glyphs, "font_rid");
GDVIRTUAL_BIND(_font_get_palette_count, "font_rid");
GDVIRTUAL_BIND(_font_get_palette_name, "font_rid", "index");
GDVIRTUAL_BIND(_font_get_palette_colors, "font_rid", "index");
GDVIRTUAL_BIND(_font_set_palette_custom_colors, "font_rid", "colors");
GDVIRTUAL_BIND(_font_get_palette_custom_colors, "font_rid");
GDVIRTUAL_BIND(_font_get_used_palette, "font_rid");
GDVIRTUAL_BIND(_font_set_used_palette, "font_rid", "index");
GDVIRTUAL_BIND(_font_set_hinting, "font_rid", "hinting");
GDVIRTUAL_BIND(_font_get_hinting, "font_rid");
@@ -690,6 +698,43 @@ bool TextServerExtension::font_is_modulate_color_glyphs(const RID &p_font_rid) c
return ret;
}
int64_t TextServerExtension::font_get_palette_count(const RID &p_font_rid) const {
int64_t ret = 0;
GDVIRTUAL_CALL(_font_get_palette_count, p_font_rid, ret);
return ret;
}
String TextServerExtension::font_get_palette_name(const RID &p_font_rid, int64_t p_index) const {
String ret;
GDVIRTUAL_CALL(_font_get_palette_name, p_font_rid, p_index, ret);
return ret;
}
Vector<Color> TextServerExtension::font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const {
Vector<Color> ret;
GDVIRTUAL_CALL(_font_get_palette_colors, p_font_rid, p_index, ret);
return ret;
}
void TextServerExtension::font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) {
GDVIRTUAL_CALL(_font_set_palette_custom_colors, p_font_rid, p_colors);
}
Vector<Color> TextServerExtension::font_get_palette_custom_colors(const RID &p_font_rid) const {
Vector<Color> ret;
GDVIRTUAL_CALL(_font_get_palette_custom_colors, p_font_rid, ret);
return ret;
}
int64_t TextServerExtension::font_get_used_palette(const RID &p_font_rid) const {
int64_t ret = 0;
GDVIRTUAL_CALL(_font_get_used_palette, p_font_rid, ret);
return ret;
}
void TextServerExtension::font_set_used_palette(const RID &p_font_rid, int64_t p_index) {
GDVIRTUAL_CALL(_font_set_used_palette, p_font_rid, p_index);
}
void TextServerExtension::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
GDVIRTUAL_CALL(_font_set_hinting, p_font_rid, p_hinting);
}
+15
View File
@@ -213,6 +213,21 @@ public:
GDVIRTUAL2(_font_set_modulate_color_glyphs, RID, bool);
GDVIRTUAL1RC(bool, _font_is_modulate_color_glyphs, RID);
virtual int64_t font_get_palette_count(const RID &p_font_rid) const override;
GDVIRTUAL1RC(int64_t, _font_get_palette_count, const RID &);
virtual String font_get_palette_name(const RID &p_font_rid, int64_t p_index) const override;
GDVIRTUAL2RC(String, _font_get_palette_name, const RID &, int64_t);
virtual Vector<Color> font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const override;
GDVIRTUAL2RC(Vector<Color>, _font_get_palette_colors, const RID &, int64_t);
virtual void font_set_palette_custom_colors(const RID &p_font_rid, const Vector<Color> &p_colors) override;
GDVIRTUAL2(_font_set_palette_custom_colors, const RID &, const Vector<Color> &);
virtual Vector<Color> font_get_palette_custom_colors(const RID &p_font_rid) const override;
GDVIRTUAL1RC(Vector<Color>, _font_get_palette_custom_colors, const RID &);
virtual int64_t font_get_used_palette(const RID &p_font_rid) const override;
GDVIRTUAL1RC(int64_t, _font_get_used_palette, const RID &);
virtual void font_set_used_palette(const RID &p_font_rid, int64_t p_index) override;
GDVIRTUAL2(_font_set_used_palette, const RID &, int64_t);
virtual void font_set_hinting(const RID &p_font_rid, Hinting p_hinting) override;
virtual Hinting font_get_hinting(const RID &p_font_rid) const override;
GDVIRTUAL2(_font_set_hinting, RID, Hinting);
+1 -1
View File
@@ -468,7 +468,7 @@ Patches:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
- Version: 12.3.2 (b42511e071162fe76102f613a6ccc009726c99af, 2026)
- Version: 13.1.1 (70b812d8d2fb4a2b6386c04bfd8565a0fcc05f9b, 2026)
- License: MIT
Files extracted from upstream source:
+1 -4
View File
@@ -1814,10 +1814,7 @@ struct ClipList
{
auto *rec = clips.as_array ().bsearch (gid);
if (rec)
{
rec->get_extents (extents, this, instancer);
return true;
}
return rec->get_extents (extents, this, instancer);
return false;
}
+682 -4
View File
@@ -27,7 +27,12 @@
#include "../../../hb-open-type.hh"
#include "../../../hb-blob.hh"
#include "../../../hb-limits.hh"
#include "../../../hb-map.hh"
#include "../../../hb-paint.hh"
#include "../../../hb-zlib.hh"
#include <ctype.h>
#include <string.h>
/*
* SVG -- SVG (Scalable Vector Graphics)
@@ -39,12 +44,62 @@
namespace OT {
static inline hb_blob_t *
hb_ot_svg_reference_normalized_blob (hb_blob_t *image,
const char **svg,
unsigned *len)
{
hb_blob_t *blob = hb_blob_reference (image);
unsigned data_len = 0;
const char *data = hb_blob_get_data (blob, &data_len);
if (!data || !data_len)
goto fail;
if (hb_blob_is_gzip (data, data_len))
{
uint32_t expected_size = 0;
if (hb_gzip_get_uncompressed_size (data, data_len, &expected_size) &&
unlikely ((size_t) expected_size > (size_t) HB_SVG_MAX_DOCUMENT_SIZE))
goto fail;
hb_blob_t *uncompressed = hb_blob_decompress_gzip (blob,
HB_SVG_MAX_DOCUMENT_SIZE);
if (!uncompressed)
goto fail;
hb_blob_destroy (blob);
blob = uncompressed;
data = hb_blob_get_data (blob, &data_len);
if (!data || !data_len)
goto fail;
}
if (unlikely ((size_t) data_len > (size_t) HB_SVG_MAX_DOCUMENT_SIZE))
goto fail;
if (svg) *svg = data;
if (len) *len = data_len;
return blob;
fail:
hb_blob_destroy (blob);
if (svg) *svg = nullptr;
if (len) *len = 0;
return nullptr;
}
struct SVGDocumentIndexEntry
{
int cmp (hb_codepoint_t g) const
{ return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; }
hb_codepoint_t get_start_glyph () const
{ return startGlyphID; }
hb_codepoint_t get_end_glyph () const
{ return endGlyphID; }
hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const
{
return hb_blob_create_sub_blob (svg_blob,
@@ -78,13 +133,50 @@ struct SVG
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG;
struct svg_id_span_t
{
const char *p;
unsigned len;
bool operator == (const svg_id_span_t &o) const
{
return len == o.len && !memcmp (p, o.p, len);
}
uint32_t hash () const
{
uint32_t h = hb_hash (len);
for (unsigned i = 0; i < len; i++)
h = h * 33u + (unsigned char) p[i];
return h;
}
};
struct svg_defs_entry_t
{
svg_id_span_t id;
unsigned start;
unsigned end;
};
struct svg_doc_cache_t
{
hb_blob_t *blob = nullptr;
const char *svg = nullptr;
unsigned len = 0;
hb_vector_t<svg_defs_entry_t> defs_entries;
hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID;
hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID;
hb_vector_t<hb_pair_t<uint32_t, uint32_t>> glyph_spans;
hb_hashmap_t<svg_id_span_t, hb_pair_t<uint32_t, uint32_t>> id_spans;
};
bool has_data () const { return svgDocEntries; }
struct accelerator_t
{
accelerator_t (hb_face_t *face)
{ table = hb_sanitize_context_t ().reference_table<SVG> (face); }
~accelerator_t () { table.destroy (); }
accelerator_t (hb_face_t *face);
~accelerator_t ();
hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const
{
@@ -92,8 +184,52 @@ struct SVG
table->svgDocEntries);
}
unsigned get_document_count () const
{ return table->get_document_count (); }
bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const
{ return table->get_glyph_document_index (glyph_id, index); }
bool get_document_glyph_range (unsigned index,
hb_codepoint_t *start_glyph,
hb_codepoint_t *end_glyph) const
{ return table->get_document_glyph_range (index, start_glyph, end_glyph); }
bool has_data () const { return table->has_data (); }
const svg_doc_cache_t *
get_or_create_doc_cache (hb_blob_t *image,
const char *svg,
unsigned len,
unsigned doc_index,
hb_codepoint_t start_glyph,
hb_codepoint_t end_glyph) const;
const char *
doc_cache_get_svg (const svg_doc_cache_t *doc,
unsigned *len) const;
const hb_vector_t<svg_defs_entry_t> *
doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const;
bool
doc_cache_get_glyph_span (const svg_doc_cache_t *doc,
hb_codepoint_t glyph,
unsigned *start,
unsigned *end) const;
bool
doc_cache_find_id_span (const svg_doc_cache_t *doc,
svg_id_span_t id,
unsigned *start,
unsigned *end) const;
bool
doc_cache_find_id_cstr (const svg_doc_cache_t *doc,
const char *id,
unsigned *start,
unsigned *end) const;
bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
{
if (!has_data ())
@@ -117,14 +253,56 @@ struct SVG
}
private:
svg_doc_cache_t *
make_doc_cache (hb_blob_t *image,
const char *svg,
unsigned len,
hb_codepoint_t start_glyph,
hb_codepoint_t end_glyph) const;
static void destroy_doc_cache (svg_doc_cache_t *doc);
hb_blob_ptr_t<SVG> table;
mutable hb_vector_t<hb_atomic_t<svg_doc_cache_t *>> doc_caches;
public:
DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>));
DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>) +
sizeof (hb_vector_t<hb_atomic_t<svg_doc_cache_t *>>));
};
const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
{ return (this+svgDocEntries).bsearch (glyph_id); }
unsigned get_document_count () const
{
if (!has_data ())
return 0;
return (this + svgDocEntries).len;
}
bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const
{
if (!has_data ())
return false;
return (this + svgDocEntries).bfind (glyph_id, index);
}
bool get_document_glyph_range (unsigned index,
hb_codepoint_t *start_glyph,
hb_codepoint_t *end_glyph) const
{
if (!has_data ())
return false;
const auto &entries = this + svgDocEntries;
if (index >= entries.len)
return false;
const auto &entry = entries.arrayZ[index];
if (start_glyph) *start_glyph = entry.get_start_glyph ();
if (end_glyph) *end_glyph = entry.get_end_glyph ();
return true;
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@@ -143,6 +321,506 @@ struct SVG
DEFINE_SIZE_STATIC (10);
};
namespace _hb_svg_cache_impl {
struct glyph_entry_t
{
hb_codepoint_t glyph;
uint32_t start;
uint32_t end;
};
struct id_entry_t
{
SVG::svg_id_span_t id;
uint32_t start;
uint32_t end;
};
struct open_elem_t
{
unsigned start;
SVG::svg_id_span_t id;
bool in_defs_content;
bool is_defs;
};
static const unsigned MAX_DEPTH = 128;
static inline int
find_substr (const char *s,
unsigned n,
unsigned from,
const char *needle,
unsigned needle_len)
{
if (!needle_len || from >= n || needle_len > n)
return -1;
for (unsigned i = from; i + needle_len <= n; i++)
if (s[i] == needle[0] && !memcmp (s + i, needle, needle_len))
return (int) i;
return -1;
}
static inline bool
parse_id_in_start_tag (const char *svg,
unsigned tag_start,
unsigned tag_end,
SVG::svg_id_span_t *id)
{
unsigned p = tag_start;
while (p + 4 <= tag_end)
{
if (!memcmp (svg + p, "id=\"", 4))
{
unsigned b = p + 4;
unsigned e = b;
while (e < tag_end && svg[e] != '"') e++;
if (e <= tag_end && e > b)
{
*id = {svg + b, e - b};
return true;
}
}
if (!memcmp (svg + p, "id='", 4))
{
unsigned b = p + 4;
unsigned e = b;
while (e < tag_end && svg[e] != '\'') e++;
if (e <= tag_end && e > b)
{
*id = {svg + b, e - b};
return true;
}
}
p++;
}
return false;
}
static inline bool
parse_glyph_id_span (const SVG::svg_id_span_t &id,
hb_codepoint_t *glyph)
{
if (id.len <= 5 || memcmp (id.p, "glyph", 5))
return false;
hb_codepoint_t gid = 0;
for (unsigned i = 5; i < id.len; i++)
{
unsigned char c = (unsigned char) id.p[i];
if (c < '0' || c > '9')
return false;
hb_codepoint_t digit = (hb_codepoint_t) (c - '0');
if (unlikely (gid > HB_CODEPOINT_INVALID / 10 ||
(gid == HB_CODEPOINT_INVALID / 10 &&
digit > HB_CODEPOINT_INVALID % 10)))
return false;
gid = (hb_codepoint_t) (gid * 10 + digit);
}
*glyph = gid;
return true;
}
static inline bool
parse_cache_entries_linear (const char *svg,
unsigned len,
hb_vector_t<SVG::svg_defs_entry_t> *defs_entries,
hb_vector_t<glyph_entry_t> *glyph_spans,
hb_vector_t<id_entry_t> *id_entries)
{
open_elem_t stack[MAX_DEPTH];
unsigned depth = 0;
defs_entries->alloc (256);
id_entries->alloc (256);
unsigned defs_depth = 0;
unsigned i = 0;
while (i < len)
{
if (svg[i] != '<')
{
i++;
continue;
}
if (i + 4 <= len && !memcmp (svg + i, "<!--", 4))
{
int cend = find_substr (svg, len, i + 4, "-->", 3);
if (cend < 0) return false;
i = (unsigned) cend + 3;
continue;
}
if (i + 9 <= len && !memcmp (svg + i, "<![CDATA[", 9))
{
int cend = find_substr (svg, len, i + 9, "]]>", 3);
if (cend < 0) return false;
i = (unsigned) cend + 3;
continue;
}
bool closing = (i + 1 < len && svg[i + 1] == '/');
bool special = (i + 1 < len && (svg[i + 1] == '!' || svg[i + 1] == '?'));
unsigned gt = i + 1;
char quote = 0;
while (gt < len)
{
char c = svg[gt];
if (quote)
{
if (c == quote) quote = 0;
}
else
{
if (c == '"' || c == '\'')
quote = c;
else if (c == '>')
break;
}
gt++;
}
if (gt >= len)
return false;
if (special)
{
i = gt + 1;
continue;
}
unsigned p = i + (closing ? 2 : 1);
while (p < gt && isspace ((unsigned char) svg[p])) p++;
const char *name = svg + p;
unsigned name_len = 0;
while (p + name_len < gt)
{
unsigned char c = (unsigned char) name[name_len];
if (!(isalnum (c) || c == '_' || c == '-' || c == ':'))
break;
name_len++;
}
bool is_defs = (name_len == 4 && !memcmp (name, "defs", 4));
if (closing)
{
if (!depth)
{
i = gt + 1;
continue;
}
open_elem_t e = stack[--depth];
unsigned end = gt + 1;
if (e.id.len)
{
auto *id_slot = id_entries->push ();
if (unlikely (!id_slot))
return false;
*id_slot = {e.id, (uint32_t) e.start, (uint32_t) end};
if (e.in_defs_content)
{
auto *slot = defs_entries->push ();
if (unlikely (!slot))
return false;
slot->id = e.id;
slot->start = e.start;
slot->end = end;
}
hb_codepoint_t gid;
if (parse_glyph_id_span (e.id, &gid))
{
auto *span = glyph_spans->push ();
if (unlikely (!span))
return false;
span->glyph = gid;
span->start = (uint32_t) e.start;
span->end = (uint32_t) end;
}
}
if (e.is_defs && defs_depth)
defs_depth--;
i = end;
continue;
}
SVG::svg_id_span_t id = {nullptr, 0};
parse_id_in_start_tag (svg, i, gt, &id);
unsigned r = gt;
while (r > i && isspace ((unsigned char) svg[r - 1])) r--;
bool self_closing = (r > i && svg[r - 1] == '/');
open_elem_t e = {i, id, defs_depth > 0, is_defs};
if (self_closing)
{
unsigned end = gt + 1;
if (e.id.len)
{
auto *id_slot = id_entries->push ();
if (unlikely (!id_slot))
return false;
*id_slot = {e.id, (uint32_t) e.start, (uint32_t) end};
if (e.in_defs_content)
{
auto *slot = defs_entries->push ();
if (unlikely (!slot))
return false;
slot->id = e.id;
slot->start = e.start;
slot->end = end;
}
hb_codepoint_t gid;
if (parse_glyph_id_span (e.id, &gid))
{
auto *span = glyph_spans->push ();
if (unlikely (!span))
return false;
span->glyph = gid;
span->start = (uint32_t) e.start;
span->end = (uint32_t) end;
}
}
}
else
{
if (unlikely (depth >= MAX_DEPTH))
return false;
stack[depth++] = e;
if (is_defs)
defs_depth++;
}
i = gt + 1;
}
return true;
}
} /* namespace _hb_svg_cache_impl */
inline
SVG::accelerator_t::accelerator_t (hb_face_t *face)
{
table = hb_sanitize_context_t ().reference_table<SVG> (face);
doc_caches.init ();
unsigned doc_count = table->get_document_count ();
if (doc_count && unlikely (!doc_caches.resize (doc_count)))
doc_caches.resize (0);
for (unsigned i = 0; i < doc_caches.length; i++)
doc_caches.arrayZ[i].set_relaxed (nullptr);
}
inline
SVG::accelerator_t::~accelerator_t ()
{
for (unsigned i = 0; i < doc_caches.length; i++)
destroy_doc_cache (doc_caches.arrayZ[i].get_relaxed ());
doc_caches.fini ();
table.destroy ();
}
inline void
SVG::accelerator_t::destroy_doc_cache (svg_doc_cache_t *doc)
{
if (!doc)
return;
doc->glyph_spans.fini ();
doc->defs_entries.fini ();
doc->id_spans.fini ();
hb_blob_destroy (doc->blob);
hb_free (doc);
}
inline SVG::svg_doc_cache_t *
SVG::accelerator_t::make_doc_cache (hb_blob_t *image,
const char *svg,
unsigned len,
hb_codepoint_t start_glyph,
hb_codepoint_t end_glyph) const
{
static const uint32_t INVALID_SPAN = 0xFFFFFFFFu;
auto *doc = (svg_doc_cache_t *) hb_malloc (sizeof (svg_doc_cache_t));
if (!doc)
return nullptr;
doc->blob = nullptr;
doc->svg = nullptr;
doc->len = 0;
doc->defs_entries.init ();
doc->start_glyph = HB_CODEPOINT_INVALID;
doc->end_glyph = HB_CODEPOINT_INVALID;
doc->glyph_spans.init ();
doc->id_spans.init ();
doc->blob = hb_blob_reference (image);
doc->svg = svg;
doc->len = len;
doc->start_glyph = start_glyph;
doc->end_glyph = end_glyph;
if (unlikely (start_glyph == HB_CODEPOINT_INVALID || end_glyph < start_glyph))
{
destroy_doc_cache (doc);
return nullptr;
}
unsigned glyph_count = end_glyph - start_glyph + 1;
if (!doc->glyph_spans.resize ((int) glyph_count))
{
destroy_doc_cache (doc);
return nullptr;
}
for (unsigned i = 0; i < glyph_count; i++)
doc->glyph_spans.arrayZ[i] = hb_pair_t<uint32_t, uint32_t> (INVALID_SPAN, INVALID_SPAN);
hb_vector_t<_hb_svg_cache_impl::glyph_entry_t> glyph_spans;
glyph_spans.init ();
hb_vector_t<_hb_svg_cache_impl::id_entry_t> id_entries;
id_entries.init ();
if (!_hb_svg_cache_impl::parse_cache_entries_linear (svg, len,
&doc->defs_entries,
&glyph_spans,
&id_entries))
{
id_entries.fini ();
glyph_spans.fini ();
destroy_doc_cache (doc);
return nullptr;
}
for (unsigned i = 0; i < glyph_spans.length; i++)
{
const auto &span = glyph_spans.arrayZ[i];
if (unlikely (span.glyph < start_glyph || span.glyph > end_glyph))
continue;
doc->glyph_spans.arrayZ[span.glyph - start_glyph] = hb_pair_t<uint32_t, uint32_t> (span.start, span.end);
}
for (unsigned i = 0; i < id_entries.length; i++)
{
const auto &e = id_entries.arrayZ[i];
hb_pair_t<uint32_t, uint32_t> *out = nullptr;
if (doc->id_spans.has (e.id, &out))
continue;
if (unlikely (!doc->id_spans.set (e.id, hb_pair_t<uint32_t, uint32_t> (e.start, e.end))))
{
id_entries.fini ();
glyph_spans.fini ();
destroy_doc_cache (doc);
return nullptr;
}
}
id_entries.fini ();
glyph_spans.fini ();
return doc;
}
inline const SVG::svg_doc_cache_t *
SVG::accelerator_t::get_or_create_doc_cache (hb_blob_t *image,
const char *svg,
unsigned len,
unsigned doc_index,
hb_codepoint_t start_glyph,
hb_codepoint_t end_glyph) const
{
if (doc_index >= doc_caches.length)
return nullptr;
auto &slot = doc_caches.arrayZ[doc_index];
auto *doc = slot.get_acquire ();
if (doc)
return doc;
auto *fresh = make_doc_cache (image, svg, len, start_glyph, end_glyph);
if (!fresh)
return nullptr;
auto *expected = (svg_doc_cache_t *) nullptr;
if (slot.cmpexch (expected, fresh))
return fresh;
destroy_doc_cache (fresh);
return expected;
}
inline const char *
SVG::accelerator_t::doc_cache_get_svg (const svg_doc_cache_t *doc,
unsigned *len) const
{
if (!doc)
{
if (len) *len = 0;
return nullptr;
}
if (len) *len = doc->len;
return doc->svg;
}
inline const hb_vector_t<SVG::svg_defs_entry_t> *
SVG::accelerator_t::doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const
{
return doc ? &doc->defs_entries : nullptr;
}
inline bool
SVG::accelerator_t::doc_cache_get_glyph_span (const svg_doc_cache_t *doc,
hb_codepoint_t glyph,
unsigned *start,
unsigned *end) const
{
static const uint32_t INVALID_SPAN = 0xFFFFFFFFu;
if (!doc || doc->start_glyph == HB_CODEPOINT_INVALID ||
glyph < doc->start_glyph || glyph > doc->end_glyph)
return false;
const auto &span = doc->glyph_spans.arrayZ[glyph - doc->start_glyph];
if (span.first == INVALID_SPAN)
return false;
if (start) *start = span.first;
if (end) *end = span.second;
return true;
}
inline bool
SVG::accelerator_t::doc_cache_find_id_span (const svg_doc_cache_t *doc,
svg_id_span_t id,
unsigned *start,
unsigned *end) const
{
if (!doc || !id.p || !id.len)
return false;
hb_pair_t<uint32_t, uint32_t> *span = nullptr;
if (!doc->id_spans.has (id, &span))
return false;
if (start) *start = span->first;
if (end) *end = span->second;
return true;
}
inline bool
SVG::accelerator_t::doc_cache_find_id_cstr (const svg_doc_cache_t *doc,
const char *id,
unsigned *start,
unsigned *end) const
{
if (!id) return false;
svg_id_span_t key = {id, (unsigned) strlen (id)};
return doc_cache_find_id_span (doc, key, start, end);
}
struct SVG_accelerator_t : SVG::accelerator_t {
SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {}
};
+1 -1
View File
@@ -100,7 +100,7 @@ struct SinglePosFormat1 : ValueBase
if (likely (index == NOT_COVERED)) return false;
/* This is ugly... */
hb_buffer_t buffer;
hb_buffer_t buffer {};
buffer.props.direction = direction;
OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
+1 -1
View File
@@ -104,7 +104,7 @@ struct SinglePosFormat2 : ValueBase
if (unlikely (index >= valueCount)) return false;
/* This is ugly... */
hb_buffer_t buffer;
hb_buffer_t buffer {};
buffer.props.direction = direction;
OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
+34 -34
View File
@@ -228,28 +228,28 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
#define PROCESS_TRANSFORM_COMPONENTS \
HB_STMT_START { \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \
PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \
PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_X, translateX); \
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_Y, translateY); \
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_ROTATION, rotation); \
PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_X, scaleX); \
PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_Y, scaleY); \
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_X, skewX); \
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_Y, skewY); \
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_X, tCenterX); \
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_Y, tCenterY); \
} HB_STMT_END
hb_transform_decomposed_t<> transform;
// Read transform components
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
if (flags & (unsigned) flags_t::flag) \
{ \
static_assert (type::static_size == HBINT16::static_size, ""); \
if (unlikely (unsigned (end - record) < HBINT16::static_size)) \
return hb_ubytes_t (); \
hb_barrier (); \
transform.name = mult * * (const HBINT16 *) record; \
transform.name = * (const HBINT16 *) record; \
record += HBINT16::static_size; \
}
PROCESS_TRANSFORM_COMPONENTS;
@@ -270,9 +270,8 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
{
// Only use coord_setter if there's actually any axis overrides.
coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ());
// Go backwards, to reduce coord_setter vector reallocations.
for (unsigned i = axisIndices.length; i; i--)
coord_setter[axisIndices[i - 1]] = axisValues[i - 1];
for (unsigned i = 0; i < axisIndices.length; i++)
coord_setter[axisIndices[i]] = roundf (axisValues[i]);
if (axisIndices)
component_coords = coord_setter.get_coords ();
@@ -281,39 +280,35 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
{
float transformValues[9];
unsigned numTransformValues = 0;
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
if (flags & (unsigned) flags_t::flag) \
transformValues[numTransformValues++] = transform.name / mult;
transformValues[numTransformValues++] = transform.name;
PROCESS_TRANSFORM_COMPONENTS;
#undef PROCESS_TRANSFORM_COMPONENT
varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
numTransformValues = 0;
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
if (flags & (unsigned) flags_t::flag) \
transform.name = transformValues[numTransformValues++] * mult;
transform.name = transformValues[numTransformValues++];
PROCESS_TRANSFORM_COMPONENTS;
#undef PROCESS_TRANSFORM_COMPONENT
}
// Divide them by their divisors
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
if (flags & (unsigned) flags_t::flag) \
{ \
HBINT16 int_v; \
int_v = roundf (transform.name); \
type typed_v = * (const type *) &int_v; \
float float_v = (float) typed_v; \
transform.name = float_v; \
}
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
if (shift && (flags & (unsigned) flags_t::flag)) \
transform.name *= 1.f / (1 << shift);
PROCESS_TRANSFORM_COMPONENTS;
#undef PROCESS_TRANSFORM_COMPONENT
if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
transform.scaleY = transform.scaleX;
transform.rotation *= HB_PI;
transform.skewX *= HB_PI;
transform.skewY *= HB_PI;
total_transform.transform (transform.to_transform ());
total_transform.scale (c.font->x_mult ? 1.f / c.font->x_multf : 0.f,
c.font->y_mult ? 1.f / c.font->y_multf : 0.f);
bool same_coords = component_coords.length == coords.length &&
component_coords.arrayZ == coords.arrayZ;
@@ -348,14 +343,18 @@ VARC::get_path_at (const hb_varc_context_t &c,
{
if (c.draw_session)
{
hb_transform_t<> leaf_transform = transform;
leaf_transform.x0 *= c.font->x_multf;
leaf_transform.y0 *= c.font->y_multf;
// Build a transforming pen to apply the transform.
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
hb_transforming_pen_context_t context {transform,
hb_transforming_pen_context_t context {leaf_transform,
c.draw_session->funcs,
c.draw_session->draw_data,
&c.draw_session->st};
hb_draw_session_t transformer_session {transformer_funcs, &context};
hb_draw_session_t &shape_draw_session = transform.is_identity () ? *c.draw_session : transformer_session;
hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session;
if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true;
#ifndef HB_NO_CFF
@@ -375,7 +374,10 @@ VARC::get_path_at (const hb_varc_context_t &c,
return false;
hb_extents_t<> comp_extents (glyph_extents);
transform.transform_extents (comp_extents);
hb_transform_t<> leaf_transform = transform;
leaf_transform.x0 *= c.font->x_multf;
leaf_transform.y0 *= c.font->y_multf;
leaf_transform.transform_extents (comp_extents);
c.extents->union_ (comp_extents);
}
return true;
@@ -399,8 +401,6 @@ VARC::get_path_at (const hb_varc_context_t &c,
parent_cache :
(this+varStore).create_cache (&static_cache);
transform.scale (c.font->x_multf, c.font->y_multf);
VarCompositeGlyph::get_path_at (c,
glyph,
coords, transform,
+16 -12
View File
@@ -384,20 +384,24 @@ struct Glyph
{
#ifndef HB_NO_BEYOND_64K
if (glyf_accelerator.GVAR->has_data ())
glyf_accelerator.GVAR->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
scratch,
gvar_cache,
phantom_only && type == SIMPLE);
{
if (!glyf_accelerator.GVAR->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
scratch,
gvar_cache,
phantom_only && type == SIMPLE))
return false;
}
else
#endif
glyf_accelerator.gvar->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
scratch,
gvar_cache,
phantom_only && type == SIMPLE);
if (!glyf_accelerator.gvar->apply_deltas_to_points (gid,
coords,
points.as_array ().sub_array (old_length),
scratch,
gvar_cache,
phantom_only && type == SIMPLE))
return false;
}
#endif
+58 -26
View File
@@ -89,7 +89,16 @@ static inline constexpr uint32_t hb_uint32_swap (uint32_t v)
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
template <typename Type>
struct __attribute__((packed)) hb_packed_t { Type v; };
struct __attribute__((packed)) hb_packed_t
{
hb_packed_t () = default;
constexpr hb_packed_t (Type V) : v (V) {}
operator Type () const { return v; }
hb_packed_t & operator = (Type V) { v = V; return *this; }
private:
Type v;
};
#ifndef HB_FAST_NUM_ACCESS
@@ -134,9 +143,9 @@ struct HBInt<BE, Type, 2>
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
((hb_packed_t<uint16_t> *) v)->v = V;
*((hb_packed_t<uint16_t> *) v) = V;
else
((hb_packed_t<uint16_t> *) v)->v = __builtin_bswap16 (V);
*((hb_packed_t<uint16_t> *) v) = __builtin_bswap16 (V);
}
#else
: v {BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V ) & 0xFF),
@@ -147,9 +156,9 @@ struct HBInt<BE, Type, 2>
{
#if HB_FAST_NUM_ACCESS
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
((const hb_packed_t<uint16_t> *) v)->v
(uint16_t) *((const hb_packed_t<uint16_t> *) v)
:
__builtin_bswap16 (((const hb_packed_t<uint16_t> *) v)->v)
__builtin_bswap16 ((uint16_t) *((const hb_packed_t<uint16_t> *) v))
;
#else
return (BE ? (v[0] << 8) : (v[0] ))
@@ -186,9 +195,9 @@ struct HBInt<BE, Type, 4>
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
((hb_packed_t<uint32_t> *) v)->v = V;
*((hb_packed_t<uint32_t> *) v) = V;
else
((hb_packed_t<uint32_t> *) v)->v = __builtin_bswap32 (V);
*((hb_packed_t<uint32_t> *) v) = __builtin_bswap32 (V);
}
#else
: v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V ) & 0xFF),
@@ -200,9 +209,9 @@ struct HBInt<BE, Type, 4>
constexpr operator Type () const {
#if HB_FAST_NUM_ACCESS
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
((const hb_packed_t<uint32_t> *) v)->v
(uint32_t) *((const hb_packed_t<uint32_t> *) v)
:
__builtin_bswap32 (((const hb_packed_t<uint32_t> *) v)->v)
__builtin_bswap32 ((uint32_t) *((const hb_packed_t<uint32_t> *) v))
;
#else
return (BE ? (v[0] << 24) : (v[0] ))
@@ -226,9 +235,9 @@ struct HBInt<BE, Type, 8>
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
((hb_packed_t<uint64_t> *) v)->v = V;
*((hb_packed_t<uint64_t> *) v) = V;
else
((hb_packed_t<uint64_t> *) v)->v = __builtin_bswap64 (V);
*((hb_packed_t<uint64_t> *) v) = __builtin_bswap64 (V);
}
#else
: v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF),
@@ -244,9 +253,9 @@ struct HBInt<BE, Type, 8>
constexpr operator Type () const {
#if HB_FAST_NUM_ACCESS
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
((const hb_packed_t<uint64_t> *) v)->v
(uint64_t) *((const hb_packed_t<uint64_t> *) v)
:
__builtin_bswap64 (((const hb_packed_t<uint64_t> *) v)->v)
__builtin_bswap64 ((uint64_t) *((const hb_packed_t<uint64_t> *) v))
;
#else
return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) ))
@@ -276,12 +285,12 @@ struct HBFloat
{
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
{
((hb_packed_t<Type> *) v)->v = V;
return;
}
}
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
{
*((hb_packed_t<Type> *) v) = V;
return;
}
}
#endif
union {
@@ -289,7 +298,7 @@ struct HBFloat
hb_packed_t<IntType> i;
} u = {{V}};
const HBInt<BE, IntType> I = u.i.v;
const HBInt<BE, IntType> I = (IntType) u.i;
for (unsigned i = 0; i < Bytes; i++)
v[i] = I.v[i];
}
@@ -297,10 +306,10 @@ struct HBFloat
/* c++14 constexpr */ operator Type () const
{
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
return ((const hb_packed_t<Type> *) v)->v;
}
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
return (Type) *((const hb_packed_t<Type> *) v);
}
#endif
HBInt<BE, IntType> I;
@@ -312,7 +321,7 @@ struct HBFloat
hb_packed_t<Type> f;
} u = {{I}};
return u.f.v;
return (Type) u.f;
}
private: uint8_t v[Bytes];
};
@@ -1191,6 +1200,21 @@ hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *resu
return (size > 0) && (count >= ((unsigned int) -1) / size);
}
static inline bool
hb_unsigned_add_overflows (unsigned int a, unsigned int b, unsigned *result = nullptr)
{
#if hb_has_builtin(__builtin_add_overflow)
unsigned stack_result;
if (!result)
result = &stack_result;
return __builtin_add_overflow (a, b, result);
#endif
if (result)
*result = a + b;
return b > (unsigned int) -1 - a;
}
/*
* Sort and search.
@@ -1664,6 +1688,13 @@ double solve_itp (func_t f,
double min_y, double max_y,
double &ya, double &yb, double &y)
{
// Guard against degenerate interval
if (b - a <= 0.0)
{
y = ya;
return a;
}
unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0));
const unsigned n0 = 1; // Hardwired
const double k1 = 0.2 / (b - a); // Hardwired.
@@ -1674,7 +1705,8 @@ double solve_itp (func_t f,
{
double x1_2 = 0.5 * (a + b);
double r = scaled_epsilon - 0.5 * (b - a);
double xf = (yb * a - ya * b) / (yb - ya);
// Guard against yb == ya to prevent division by zero
double xf = (yb != ya) ? (yb * a - ya * b) / (yb - ya) : x1_2;
double sigma = x1_2 - xf;
double b_a = b - a;
// This has k2 = 2 hardwired for efficiency.
+3
View File
@@ -193,6 +193,9 @@ struct hb_atomic_t<T *>
T *get_acquire () const { return v.load (std::memory_order_acquire); }
bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); }
hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; }
hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; }
operator bool () const { return get_acquire () != nullptr; }
T *operator->() const { return get_acquire (); }
template <typename C>
+8 -1
View File
@@ -173,7 +173,14 @@ struct hb_bit_set_invertible_t
bool is_subset (const hb_bit_set_invertible_t &larger_set) const
{
if (unlikely (inverted != larger_set.inverted))
return hb_all (hb_iter (s) | hb_map (larger_set.s));
{
if (inverted)
return hb_all (iter (), larger_set.s);
else
// larger set is inverted so larger_set.s is the set of things that are not present
// in larger_set, therefore if s has any of those it can't be a subset.
return !s.intersects (larger_set.s);
}
else
return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s);
}
+13 -5
View File
@@ -438,23 +438,31 @@ struct hb_bit_set_t
return false;
uint32_t spi = 0;
for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
uint32_t lpi = 0;
while (spi < page_map.length && lpi < larger_set.page_map.length)
{
uint32_t spm = page_map.arrayZ[spi].major;
uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
auto sp = page_at (spi);
if (spm < lpm && !sp.is_empty ())
return false;
if (lpm < spm)
if (spm < lpm) {
if (!sp.is_empty ())
return false;
spi++;
continue;
}
if (lpm < spm) {
lpi++;
continue;
}
auto lp = larger_set.page_at (lpi);
if (!sp.is_subset (lp))
return false;
spi++;
lpi++;
}
while (spi < page_map.length)
+14
View File
@@ -50,6 +50,20 @@ struct hb_blob_t
}
}
void replace_buffer (const char *new_data,
unsigned new_length,
hb_memory_mode_t new_mode,
void *new_user_data,
hb_destroy_func_t new_destroy)
{
destroy_user_data ();
data = new_data;
length = new_length;
mode = new_mode;
user_data = new_user_data;
destroy = new_destroy;
}
HB_INTERNAL bool try_make_writable ();
HB_INTERNAL bool try_make_writable_inplace ();
HB_INTERNAL bool try_make_writable_inplace_unix ();
+4 -4
View File
@@ -32,7 +32,7 @@
#include "hb.hh"
#line 33 "hb-buffer-deserialize-json.hh"
#line 36 "hb-buffer-deserialize-json.hh"
static const unsigned char _deserialize_json_trans_keys[] = {
0u, 0u, 9u, 34u, 97u, 121u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
@@ -597,12 +597,12 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
hb_glyph_info_t info = {0};
hb_glyph_position_t pos = {0};
#line 594 "hb-buffer-deserialize-json.hh"
#line 601 "hb-buffer-deserialize-json.hh"
{
cs = deserialize_json_start;
}
#line 597 "hb-buffer-deserialize-json.hh"
#line 606 "hb-buffer-deserialize-json.hh"
{
int _slen;
int _trans;
@@ -712,7 +712,7 @@ _resume:
#line 56 "hb-buffer-deserialize-json.rl"
{ if (unlikely (!buffer->ensure_unicode ())) return false; }
break;
#line 689 "hb-buffer-deserialize-json.hh"
#line 716 "hb-buffer-deserialize-json.hh"
}
_again:
@@ -32,7 +32,7 @@
#include "hb.hh"
#line 33 "hb-buffer-deserialize-text-glyphs.hh"
#line 36 "hb-buffer-deserialize-text-glyphs.hh"
static const unsigned char _deserialize_text_glyphs_trans_keys[] = {
0u, 0u, 35u, 124u, 48u, 57u, 60u, 124u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u,
48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 62u, 62u,
@@ -389,12 +389,12 @@ _hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer,
hb_glyph_info_t info = {0};
hb_glyph_position_t pos = {0};
#line 386 "hb-buffer-deserialize-text-glyphs.hh"
#line 393 "hb-buffer-deserialize-text-glyphs.hh"
{
cs = deserialize_text_glyphs_start;
}
#line 389 "hb-buffer-deserialize-text-glyphs.hh"
#line 398 "hb-buffer-deserialize-text-glyphs.hh"
{
int _slen;
int _trans;
@@ -552,7 +552,7 @@ _resume:
return false;
}
break;
#line 523 "hb-buffer-deserialize-text-glyphs.hh"
#line 556 "hb-buffer-deserialize-text-glyphs.hh"
}
_again:
@@ -573,7 +573,7 @@ _again:
*end_ptr = p;
}
break;
#line 542 "hb-buffer-deserialize-text-glyphs.hh"
#line 577 "hb-buffer-deserialize-text-glyphs.hh"
}
}
@@ -32,7 +32,7 @@
#include "hb.hh"
#line 33 "hb-buffer-deserialize-text-unicode.hh"
#line 36 "hb-buffer-deserialize-text-unicode.hh"
static const unsigned char _deserialize_text_unicode_trans_keys[] = {
0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u,
85u, 117u, 85u, 117u, 0
@@ -150,12 +150,12 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer,
hb_glyph_info_t info = {0};
const hb_glyph_position_t pos = {0};
#line 147 "hb-buffer-deserialize-text-unicode.hh"
#line 154 "hb-buffer-deserialize-text-unicode.hh"
{
cs = deserialize_text_unicode_start;
}
#line 150 "hb-buffer-deserialize-text-unicode.hh"
#line 159 "hb-buffer-deserialize-text-unicode.hh"
{
int _slen;
int _trans;
@@ -215,7 +215,7 @@ _resume:
hb_memset (&info, 0, sizeof (info));
}
break;
#line 203 "hb-buffer-deserialize-text-unicode.hh"
#line 219 "hb-buffer-deserialize-text-unicode.hh"
}
_again:
@@ -238,7 +238,7 @@ _again:
*end_ptr = p;
}
break;
#line 224 "hb-buffer-deserialize-text-unicode.hh"
#line 242 "hb-buffer-deserialize-text-unicode.hh"
}
}
+33 -11
View File
@@ -114,6 +114,17 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
*buf_consumed = 0;
hb_position_t x = 0, y = 0;
/* Calculate the advance of the previous glyphs */
if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
{
for (unsigned int i = 0; i < start; i++)
{
x += pos[i].x_advance;
y += pos[i].y_advance;
}
}
for (unsigned int i = start; i < end; i++)
{
char b[1024];
@@ -151,7 +162,7 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
}
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
if (pos && !(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
{
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
x+pos[i].x_offset, y+pos[i].y_offset));
@@ -272,6 +283,17 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
*buf_consumed = 0;
hb_position_t x = 0, y = 0;
/* Calculate the advance of the previous glyphs */
if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
{
for (unsigned int i = 0; i < start; i++)
{
x += pos[i].x_advance;
y += pos[i].y_advance;
}
}
for (unsigned int i = start; i < end; i++)
{
char b[1024];
@@ -297,7 +319,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
}
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
if (pos && !(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
{
if (x+pos[i].x_offset || y+pos[i].y_offset)
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
@@ -417,9 +439,9 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
* A human-readable, plain text format.
* The serialized glyphs will look something like:
*
* ```
* |[<!-- language="plain" -->
* [uni0651=0@518,0+0|uni0628=0+1897]
* ```
* ]|
*
* - The serialized glyphs are delimited with `[` and `]`.
* - Glyphs are separated with `|`
@@ -435,10 +457,10 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
* A machine-readable, structured format.
* The serialized glyphs will look something like:
*
* ```
* |[<!-- language="plain" -->
* [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0},
* {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}]
* ```
* {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}]
* ]|
*
* Each glyph is a JSON object, with the following properties:
* - `g`: the glyph name or glyph index if
@@ -530,9 +552,9 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
* A human-readable, plain text format.
* The serialized codepoints will look something like:
*
* ```
* |[<!-- language="plain" -->
*  <U+0651=0|U+0628=1>
* ```
* ]|
*
* - Glyphs are separated with `|`
* - Unicode codepoints are expressed as zero-padded four (or more)
@@ -550,9 +572,9 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
*
* For example:
*
* ```
* |[<!-- language="plain" -->
* [{u:1617,cl:0},{u:1576,cl:1}]
* ```
* ]|
*
* Return value:
* The number of serialized items.
+40 -15
View File
@@ -547,12 +547,6 @@ void
hb_buffer_t::merge_clusters_impl (unsigned int start,
unsigned int end)
{
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
{
unsafe_to_break (start, end);
return;
}
max_ops -= end - start;
if (unlikely (max_ops < 0))
successful = false;
@@ -581,15 +575,9 @@ hb_buffer_t::merge_clusters_impl (unsigned int start,
set_cluster (info[i], cluster);
}
void
hb_buffer_t::merge_out_clusters (unsigned int start,
unsigned int end)
hb_buffer_t::merge_out_clusters_impl (unsigned int start,
unsigned int end)
{
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
return;
if (unlikely (end - start < 2))
return;
max_ops -= end - start;
if (unlikely (max_ops < 0))
successful = false;
@@ -972,6 +960,9 @@ void
hb_buffer_set_content_type (hb_buffer_t *buffer,
hb_buffer_content_type_t content_type)
{
if (unlikely (hb_object_is_immutable (buffer)))
return;
buffer->content_type = content_type;
}
@@ -2290,6 +2281,22 @@ hb_buffer_diff (hb_buffer_t *buffer,
* Debugging.
*/
void
hb_buffer_t::changed ()
{
#ifdef HB_NO_BUFFER_MESSAGE
return;
#else
if (!message_depth)
return;
if (changed_func)
changed_func (this, changed_data);
else
update_digest ();
#endif
}
#ifndef HB_NO_BUFFER_MESSAGE
/**
* hb_buffer_set_message_func:
@@ -2307,7 +2314,8 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
hb_buffer_message_func_t func,
void *user_data, hb_destroy_func_t destroy)
{
if (unlikely (hb_object_is_immutable (buffer)))
if (unlikely (hb_object_is_immutable (buffer)) ||
unlikely (buffer->message_depth))
{
if (destroy)
destroy (user_data);
@@ -2327,6 +2335,23 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
buffer->message_destroy = nullptr;
}
}
/**
* hb_buffer_changed:
* @buffer: An #hb_buffer_t
*
* Called by a message callback after modifying buffer glyph indices,
* to update internal caches.
*
* If not called from inside a message callback, does nothing.
*
* Since: 13.0.0
**/
void
hb_buffer_changed (hb_buffer_t *buffer)
{
buffer->changed ();
}
bool
hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap)
{
+11 -3
View File
@@ -495,7 +495,7 @@ typedef enum {
(1u << HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES))))
/**
* HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS
* HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS:
* @level: #hb_buffer_cluster_level_t to test
*
* Tests whether a cluster level does not group cluster values by graphemes.
@@ -505,7 +505,7 @@ typedef enum {
*/
#define HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS(level) \
((bool) ((1u << (unsigned) (level)) & \
((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARCATERS) | \
((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) | \
(1u << HB_BUFFER_CLUSTER_LEVEL_CHARACTERS))))
HB_EXTERN void
@@ -672,7 +672,12 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
* @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents.
* @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0
* @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances,
* glyph offsets will reflect absolute glyph positions. Since: 1.8.0
* glyph offsets will reflect absolute glyph positions. Since: 1.8.0.
* Note: when this flag is used with a partial range of the buffer (i.e.
* @start is not 0), calculating the absolute positions has a cost
* proportional to @start. If the buffer is serialized in many small
* chunks, this can lead to quadratic behavior. It is recommended to
* use a larger @buf_size to minimize this cost.
* @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0
*
* Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs().
@@ -865,6 +870,9 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
hb_buffer_message_func_t func,
void *user_data, hb_destroy_func_t destroy);
HB_EXTERN void
hb_buffer_changed (hb_buffer_t *buffer);
HB_END_DECLS
+38 -1
View File
@@ -130,9 +130,13 @@ struct hb_buffer_t
*/
#ifndef HB_NO_BUFFER_MESSAGE
typedef void (*changed_func_t) (hb_buffer_t *buffer, void *user_data);
hb_buffer_message_func_t message_func;
void *message_data;
hb_destroy_func_t message_destroy;
changed_func_t changed_func;
void *changed_data;
unsigned message_depth; /* How deeply are we inside a message callback? */
#else
static constexpr unsigned message_depth = 0u;
@@ -398,10 +402,42 @@ struct hb_buffer_t
{
if (end - start < 2)
return;
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
{
unsafe_to_break (start, end);
return;
}
merge_clusters_impl (start, end);
}
void merge_grapheme_clusters (unsigned int start, unsigned int end)
{
if (end - start < 2)
return;
if (!HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (cluster_level))
{
unsafe_to_break (start, end);
return;
}
merge_clusters_impl (start, end);
}
HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end);
HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end);
void merge_out_clusters (unsigned int start, unsigned int end)
{
if (end - start < 2)
return;
if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level))
return;
merge_out_clusters_impl (start, end);
}
void merge_out_grapheme_clusters (unsigned int start, unsigned int end)
{
if (end - start < 2)
return;
if (!HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (cluster_level))
return;
merge_out_clusters_impl (start, end);
}
HB_INTERNAL void merge_out_clusters_impl (unsigned int start, unsigned int end);
/* Merge clusters for deleting current glyph, and skip it. */
HB_INTERNAL void delete_glyph ();
HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info));
@@ -605,6 +641,7 @@ struct hb_buffer_t
#endif
}
HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
HB_INTERNAL void changed ();
static void
set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
+1 -2
View File
@@ -602,10 +602,10 @@ hb_cairo_render_glyph (cairo_scaled_font_t *scaled_font,
hb_position_t x_scale, y_scale;
hb_font_get_scale (font, &x_scale, &y_scale);
cairo_scale (cr,
+1. / (x_scale ? x_scale : 1),
-1. / (y_scale ? y_scale : 1));
if (hb_font_draw_glyph_or_fail (font, glyph, hb_cairo_draw_get_funcs (), cr))
cairo_fill (cr);
@@ -882,7 +882,6 @@ hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face)
&hb_cairo_scale_factor_user_data_key);
}
/**
* hb_cairo_glyphs_from_buffer:
* @buffer: a #hb_buffer_t containing glyphs
+20 -21
View File
@@ -344,25 +344,25 @@ struct cff_stack_t
{
ELEM& operator [] (unsigned int i)
{
if (unlikely (i >= count))
if (unlikely (i >= length))
{
set_error ();
return Crap (ELEM);
}
return elements[i];
return arrayZ[i];
}
void push (const ELEM &v)
{
if (likely (count < LIMIT))
elements[count++] = v;
if (likely (length < LIMIT))
arrayZ[length++] = v;
else
set_error ();
}
ELEM &push ()
{
if (likely (count < LIMIT))
return elements[count++];
if (likely (length < LIMIT))
return arrayZ[length++];
else
{
set_error ();
@@ -372,8 +372,8 @@ struct cff_stack_t
ELEM& pop ()
{
if (likely (count > 0))
return elements[--count];
if (likely (length > 0))
return arrayZ[--length];
else
{
set_error ();
@@ -382,45 +382,44 @@ struct cff_stack_t
}
void pop (unsigned int n)
{
if (likely (count >= n))
count -= n;
if (likely (length >= n))
length -= n;
else
set_error ();
}
const ELEM& peek ()
{
if (unlikely (count == 0))
if (unlikely (length == 0))
{
set_error ();
return Null (ELEM);
}
return elements[count - 1];
return arrayZ[length - 1];
}
void unpop ()
{
if (likely (count < LIMIT))
count++;
if (likely (length < LIMIT))
length++;
else
set_error ();
}
void clear () { count = 0; }
void clear () { length = 0; }
bool in_error () const { return (error); }
void set_error () { error = true; }
unsigned int get_count () const { return count; }
bool is_empty () const { return !count; }
unsigned int get_count () const { return length; }
bool is_empty () const { return !length; }
hb_array_t<const ELEM> sub_array (unsigned start, unsigned length) const
{ return hb_array_t<const ELEM> (elements).sub_array (start, length); }
{ return hb_array_t<const ELEM> (arrayZ).sub_array (start, length); }
private:
bool error = false;
unsigned int count = 0;
ELEM elements[LIMIT];
unsigned int length = 0;
ELEM arrayZ[LIMIT];
};
/* argument stack */
+273
View File
@@ -0,0 +1,273 @@
/*
* CFF CharString Specializer
*
* Optimizes CharString bytecode by using specialized operators
* (hlineto, vlineto, hhcurveto, etc.) to save bytes and respects
* CFF1 stack limit (48 values).
*
* Based on fontTools.cffLib.specializer
*/
#ifndef HB_CFF_SPECIALIZER_HH
#define HB_CFF_SPECIALIZER_HH
#include "hb.hh"
#include "hb-cff-interp-cs-common.hh"
namespace CFF {
/* CharString command representation - forward declared in hb-subset-cff-common.hh */
/* Check if a value is effectively zero */
static inline bool
is_zero (const number_t &n)
{
return n.to_int () == 0;
}
/* Generalize CharString commands to canonical form
*
* Converts all operators to their general forms and breaks down
* multi-segment operators into single segments. This ensures we
* start from a clean baseline before specialization.
*
* Based on fontTools.cffLib.specializer.generalizeCommands
*/
static void
generalize_commands (hb_vector_t<cs_command_t> &commands)
{
hb_vector_t<cs_command_t> result;
result.alloc (commands.length * 2); /* Estimate: might expand */
for (unsigned i = 0; i < commands.length; i++)
{
auto &cmd = commands[i];
switch (cmd.op)
{
case OpCode_hmoveto:
case OpCode_vmoveto:
{
/* Convert to rmoveto with explicit dx,dy */
cs_command_t gen (OpCode_rmoveto);
gen.args.alloc (2);
if (cmd.op == OpCode_hmoveto && cmd.args.length >= 1)
{
gen.args.push (cmd.args[0]); /* dx */
number_t zero; zero.set_int (0);
gen.args.push (zero); /* dy = 0 */
}
else if (cmd.op == OpCode_vmoveto && cmd.args.length >= 1)
{
number_t zero; zero.set_int (0);
gen.args.push (zero); /* dx = 0 */
gen.args.push (cmd.args[0]); /* dy */
}
result.push (gen);
break;
}
case OpCode_hlineto:
case OpCode_vlineto:
{
/* Convert h/v lineto to rlineto, breaking into single segments
* hlineto alternates: dx1 ( dx1,0) dy1 ( 0,dy1) dx2 ( dx2,0) ...
* vlineto alternates: dy1 ( 0,dy1) dx1 ( dx1,0) dy2 ( 0,dy2) ... */
bool is_h = (cmd.op == OpCode_hlineto);
number_t zero; zero.set_int (0);
for (unsigned j = 0; j < cmd.args.length; j++)
{
cs_command_t seg (OpCode_rlineto);
seg.args.alloc (2);
bool is_horizontal = is_h ? (j % 2 == 0) : (j % 2 == 1);
if (is_horizontal)
{
seg.args.push (cmd.args[j]); /* dx */
seg.args.push (zero); /* dy = 0 */
}
else
{
seg.args.push (zero); /* dx = 0 */
seg.args.push (cmd.args[j]); /* dy */
}
result.push (seg);
}
break;
}
case OpCode_rlineto:
{
/* Break into single segments (dx,dy pairs) */
for (unsigned j = 0; j + 1 < cmd.args.length; j += 2)
{
cs_command_t seg (OpCode_rlineto);
seg.args.alloc (2);
seg.args.push (cmd.args[j]);
seg.args.push (cmd.args[j + 1]);
result.push (seg);
}
break;
}
case OpCode_rrcurveto:
{
/* Break into single segments (6 args each) */
for (unsigned j = 0; j + 5 < cmd.args.length; j += 6)
{
cs_command_t seg (OpCode_rrcurveto);
seg.args.alloc (6);
for (unsigned k = 0; k < 6; k++)
seg.args.push (cmd.args[j + k]);
result.push (seg);
}
break;
}
default:
/* Keep other operators as-is */
result.push (cmd);
break;
}
}
/* Replace commands with generalized result */
commands.resize (0);
for (unsigned i = 0; i < result.length; i++)
commands.push (result[i]);
}
/* Specialize CharString commands to optimize bytecode size
*
* Follows fontTools approach:
* 0. Generalize: Break down to canonical single-segment form
* 1. Specialize: Convert rmoveto/rlineto to h/v variants when dx or dy is zero
* 2. Combine: Merge adjacent compatible operators
* 3. Enforce: Respect maxstack limit (default 48 for CFF1)
*
* This ensures we never exceed stack depth while optimizing bytecode.
*/
static void
specialize_commands (hb_vector_t<cs_command_t> &commands,
unsigned maxstack = 48)
{
if (commands.length == 0) return;
/* Pass 0: Generalize to canonical form (fontTools does this first) */
generalize_commands (commands);
/* Pass 1: Specialize rmoveto/rlineto into h/v variants */
for (unsigned i = 0; i < commands.length; i++)
{
auto &cmd = commands[i];
if ((cmd.op == OpCode_rmoveto || cmd.op == OpCode_rlineto) &&
cmd.args.length == 2)
{
bool dx_zero = is_zero (cmd.args[0]);
bool dy_zero = is_zero (cmd.args[1]);
if (dx_zero && !dy_zero)
{
/* Vertical movement (dx=0): keep only dy */
cmd.op = (cmd.op == OpCode_rmoveto) ? OpCode_vmoveto : OpCode_vlineto;
/* Shift dy to position 0 */
cmd.args[0] = cmd.args[1];
cmd.args.resize (1);
}
else if (!dx_zero && dy_zero)
{
/* Horizontal movement (dy=0): keep only dx */
cmd.op = (cmd.op == OpCode_rmoveto) ? OpCode_hmoveto : OpCode_hlineto;
cmd.args.resize (1); /* Keep only dx */
}
/* else: both zero or both non-zero, keep as rmoveto/rlineto */
}
}
/* Pass 2: Combine adjacent hlineto/vlineto operators
* hlineto can take multiple args alternating with vlineto
* This saves operator bytes */
for (int i = (int)commands.length - 1; i > 0; i--)
{
auto &cmd = commands[i];
auto &prev = commands[i-1];
/* Combine adjacent hlineto + vlineto or vlineto + hlineto */
if ((prev.op == OpCode_hlineto && cmd.op == OpCode_vlineto) ||
(prev.op == OpCode_vlineto && cmd.op == OpCode_hlineto))
{
/* Check stack depth */
unsigned combined_args = prev.args.length + cmd.args.length;
if (combined_args < maxstack)
{
/* Merge into first command, keep its operator */
for (unsigned j = 0; j < cmd.args.length; j++)
prev.args.push (cmd.args[j]);
commands.remove_ordered (i);
i++; /* Adjust for removed element */
}
}
}
/* Pass 3: Combine adjacent identical operators */
for (int i = (int)commands.length - 1; i > 0; i--)
{
auto &cmd = commands[i];
auto &prev = commands[i-1];
/* Combine same operators (e.g., rlineto + rlineto) */
if (prev.op == cmd.op &&
(cmd.op == OpCode_rlineto || cmd.op == OpCode_hlineto ||
cmd.op == OpCode_vlineto || cmd.op == OpCode_rrcurveto))
{
/* Check stack depth */
unsigned combined_args = prev.args.length + cmd.args.length;
if (combined_args < maxstack)
{
/* Merge args */
for (unsigned j = 0; j < cmd.args.length; j++)
prev.args.push (cmd.args[j]);
commands.remove_ordered (i);
i++; /* Adjust for removed element */
}
}
}
}
/* Encode commands back to binary CharString */
static bool
encode_commands (const hb_vector_t<cs_command_t> &commands,
str_buff_t &output)
{
for (const auto &cmd : commands)
{
str_encoder_t encoder (output);
/* Encode arguments */
for (const auto &arg : cmd.args)
encoder.encode_num_cs (arg);
/* Encode operator */
if (cmd.op != OpCode_Invalid)
encoder.encode_op (cmd.op);
/* hintmask/cntrmask are followed by raw mask bytes. */
if (cmd.op == OpCode_hintmask || cmd.op == OpCode_cntrmask)
{
for (const auto &byte : cmd.mask_bytes)
encoder.encode_byte (byte);
}
if (encoder.in_error ())
return false;
}
return true;
}
} /* namespace CFF */
#endif /* HB_CFF_SPECIALIZER_HH */
+207
View File
@@ -0,0 +1,207 @@
/*
* CFF Width Optimizer
*
* Determines optimal defaultWidthX and nominalWidthX values
* to minimize CharString byte cost.
*
* Based on fontTools.cffLib.width
*/
#ifndef HB_CFF_WIDTH_OPTIMIZER_HH
#define HB_CFF_WIDTH_OPTIMIZER_HH
#include "hb.hh"
namespace CFF {
/* Calculate byte cost for encoding a width delta */
static inline unsigned
width_delta_cost (int delta)
{
delta = abs (delta);
if (delta <= 107) return 1;
if (delta <= 1131) return 2;
return 5;
}
/* Cumulative sum forward */
static void
cumsum_forward (const hb_hashmap_t<unsigned, unsigned> &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned> &cumsum)
{
cumsum.resize (max_w - min_w + 1);
unsigned v = 0;
for (unsigned x = min_w; x <= max_w; x++)
{
v += freq.get (x);
cumsum[x - min_w] = v;
}
}
/* Cumulative max forward */
static void
cummax_forward (const hb_hashmap_t<unsigned, unsigned> &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned> &cummax)
{
cummax.resize (max_w - min_w + 1);
unsigned v = 0;
for (unsigned x = min_w; x <= max_w; x++)
{
v = hb_max (v, freq.get (x));
cummax[x - min_w] = v;
}
}
/* Cumulative sum backward */
static void
cumsum_backward (const hb_hashmap_t<unsigned, unsigned> &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned> &cumsum)
{
cumsum.resize (max_w - min_w + 1);
unsigned v = 0;
for (int x = (int) max_w; x >= (int) min_w; x--)
{
v += freq.get ((unsigned) x);
cumsum[x - min_w] = v;
}
}
/* Cumulative max backward */
static void
cummax_backward (const hb_hashmap_t<unsigned, unsigned> &freq,
unsigned min_w, unsigned max_w,
hb_vector_t<unsigned> &cummax)
{
cummax.resize (max_w - min_w + 1);
unsigned v = 0;
for (int x = (int) max_w; x >= (int) min_w; x--)
{
v = hb_max (v, freq.get ((unsigned) x));
cummax[x - min_w] = v;
}
}
/* Helper to safely get cumulative value with bounds checking */
static inline unsigned
safe_get (const hb_vector_t<unsigned> &vec, int x, unsigned min_w, unsigned max_w)
{
if (x < (int) min_w || x > (int) max_w) return 0;
return vec[x - min_w];
}
/* Optimize defaultWidthX and nominalWidthX from a list of widths
* O(UPEM+numGlyphs) algorithm from fontTools.cffLib.width */
static void
optimize_widths (const hb_vector_t<unsigned> &width_list,
unsigned &default_width,
unsigned &nominal_width)
{
if (width_list.length == 0)
{
default_width = nominal_width = 0;
return;
}
/* Build frequency map */
hb_hashmap_t<unsigned, unsigned> widths;
unsigned min_w = width_list[0];
unsigned max_w = width_list[0];
for (unsigned w : width_list)
{
widths.set (w, widths.get (w) + 1);
min_w = hb_min (min_w, w);
max_w = hb_max (max_w, w);
}
/* Cumulative sum/max forward/backward */
hb_vector_t<unsigned> cumFrqU, cumMaxU, cumFrqD, cumMaxD;
cumsum_forward (widths, min_w, max_w, cumFrqU);
cummax_forward (widths, min_w, max_w, cumMaxU);
cumsum_backward (widths, min_w, max_w, cumFrqD);
cummax_backward (widths, min_w, max_w, cumMaxD);
/* Cost per nominal choice, without default consideration */
auto nomnCost = [&] (unsigned x) -> unsigned {
return safe_get (cumFrqU, x, min_w, max_w) +
safe_get (cumFrqU, x - 108, min_w, max_w) +
safe_get (cumFrqU, x - 1132, min_w, max_w) * 3 +
safe_get (cumFrqD, x, min_w, max_w) +
safe_get (cumFrqD, x + 108, min_w, max_w) +
safe_get (cumFrqD, x + 1132, min_w, max_w) * 3 -
widths.get (x);
};
/* Cost-saving per nominal choice, by best default choice */
auto dfltCost = [&] (unsigned x) -> unsigned {
unsigned u = hb_max (hb_max (safe_get (cumMaxU, x, min_w, max_w),
safe_get (cumMaxU, x - 108, min_w, max_w) * 2),
safe_get (cumMaxU, x - 1132, min_w, max_w) * 5);
unsigned d = hb_max (hb_max (safe_get (cumMaxD, x, min_w, max_w),
safe_get (cumMaxD, x + 108, min_w, max_w) * 2),
safe_get (cumMaxD, x + 1132, min_w, max_w) * 5);
return hb_max (u, d);
};
/* Find best nominal */
unsigned best_nominal = min_w;
unsigned best_cost = nomnCost (min_w) - dfltCost (min_w);
for (unsigned x = min_w + 1; x <= max_w; x++)
{
unsigned cost = nomnCost (x) - dfltCost (x);
if (cost < best_cost)
{
best_cost = cost;
best_nominal = x;
}
}
/* Work back the best default */
unsigned best_default = best_nominal;
unsigned best_default_cost = (unsigned) -1;
/* Check candidates around best_nominal */
int candidates[] = {
(int) best_nominal,
(int) best_nominal - 108,
(int) best_nominal - 1132,
(int) best_nominal + 108,
(int) best_nominal + 1132
};
for (int candidate : candidates)
{
if (candidate < (int) min_w || candidate > (int) max_w)
continue;
/* Compute actual cost with this default */
unsigned cost = 0;
for (auto kv : widths.iter ())
{
unsigned w = kv.first;
unsigned freq = kv.second;
if (w == (unsigned) candidate)
continue;
cost += freq * width_delta_cost ((int) w - (int) best_nominal);
}
if (cost < best_default_cost)
{
best_default_cost = cost;
best_default = (unsigned) candidate;
}
}
default_width = best_default;
nominal_width = best_nominal;
}
} /* namespace CFF */
#endif /* HB_CFF_WIDTH_OPTIMIZER_HH */
+1 -1
View File
@@ -291,7 +291,7 @@ struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PAR
for (unsigned int i = 0; i < n; i++)
{
const hb_array_t<const ELEM> blends = env.argStack.sub_array (start + n + (i * k), k);
process_arg_blend (env, env.argStack[start + i], blends, n, i);
process_arg_blend (env, env.argStack.arrayZ[start + i], blends, n, i);
}
/* pop off blend values leaving default values now adorned with blend values */
+1
View File
@@ -43,6 +43,7 @@
#ifdef HB_TINY
#define HB_LEAN
#define HB_MINI
#define HB_NO_SVG
#define HB_OPTIMIZE_SIZE
#define HB_OPTIMIZE_SIZE_MORE
#define HB_MINIMIZE_MEMORY_USAGE
+1 -1
View File
@@ -199,7 +199,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
hb_glyph_info_t *info = buffer->info;
for (unsigned int i = 1; i < count; i++)
if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
buffer->merge_clusters (i - 1, i + 1);
buffer->merge_grapheme_clusters (i - 1, i + 1);
}
hb_vector_t<range_record_t> range_records;
+34 -30
View File
@@ -138,52 +138,56 @@ struct vtable_t
static constexpr auto get_user_data = _get_user_data;
};
#define HB_DEFINE_VTABLE(name) \
#define HB_DEFINE_VTABLE(name, empty) \
template<> \
struct vtable<hb_##name##_t> \
: vtable_t<hb_##name##_t, \
&hb_##name##_get_empty, \
empty, \
&hb_##name##_reference, \
&hb_##name##_destroy, \
&hb_##name##_set_user_data, \
&hb_##name##_get_user_data> {}
HB_DEFINE_VTABLE (buffer);
HB_DEFINE_VTABLE (blob);
HB_DEFINE_VTABLE (face);
HB_DEFINE_VTABLE (font);
HB_DEFINE_VTABLE (font_funcs);
HB_DEFINE_VTABLE (map);
HB_DEFINE_VTABLE (set);
HB_DEFINE_VTABLE (shape_plan);
HB_DEFINE_VTABLE (unicode_funcs);
HB_DEFINE_VTABLE (draw_funcs);
HB_DEFINE_VTABLE (paint_funcs);
#undef HB_DEFINE_VTABLE
HB_DEFINE_VTABLE (buffer, &hb_buffer_get_empty);
HB_DEFINE_VTABLE (blob, &hb_blob_get_empty);
HB_DEFINE_VTABLE (face, &hb_face_get_empty);
HB_DEFINE_VTABLE (font, &hb_font_get_empty);
HB_DEFINE_VTABLE (font_funcs, &hb_font_funcs_get_empty);
HB_DEFINE_VTABLE (map, &hb_map_get_empty);
HB_DEFINE_VTABLE (set, &hb_set_get_empty);
HB_DEFINE_VTABLE (shape_plan, &hb_shape_plan_get_empty);
HB_DEFINE_VTABLE (unicode_funcs, &hb_unicode_funcs_get_empty);
HB_DEFINE_VTABLE (draw_funcs, &hb_draw_funcs_get_empty);
HB_DEFINE_VTABLE (paint_funcs, &hb_paint_funcs_get_empty);
#ifdef HB_SUBSET_H
#define HB_DEFINE_VTABLE(name) \
template<> \
struct vtable<hb_##name##_t> \
: vtable_t<hb_##name##_t, \
nullptr, \
&hb_##name##_reference, \
&hb_##name##_destroy, \
&hb_##name##_set_user_data, \
&hb_##name##_get_user_data> {}
HB_DEFINE_VTABLE (subset_input);
HB_DEFINE_VTABLE (subset_plan);
#undef HB_DEFINE_VTABLE
HB_DEFINE_VTABLE (subset_input, nullptr);
HB_DEFINE_VTABLE (subset_plan, nullptr);
#endif
#ifdef HB_RASTER_H
HB_DEFINE_VTABLE (raster_image, nullptr);
HB_DEFINE_VTABLE (raster_draw, nullptr);
HB_DEFINE_VTABLE (raster_paint, nullptr);
#endif
#ifdef HB_VECTOR_H
HB_DEFINE_VTABLE (vector_draw, nullptr);
HB_DEFINE_VTABLE (vector_paint, nullptr);
#endif
#undef HB_DEFINE_VTABLE
} // namespace hb
/* Workaround for GCC < 7, see:
+3
View File
@@ -452,6 +452,7 @@ hb_directwrite_font_get_dw_font_face (hb_font_t *font)
return (IDWriteFontFace *) (const void *) font->data.directwrite;
}
#ifndef HB_DISABLE_DEPRECATED
/**
* hb_directwrite_font_get_dw_font:
@@ -471,3 +472,5 @@ hb_directwrite_font_get_dw_font (hb_font_t *font)
}
#endif
#endif
+12
View File
@@ -108,5 +108,17 @@
#define HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH 64
#endif
#ifndef HB_SVG_MAX_PATH_SEGMENTS
#define HB_SVG_MAX_PATH_SEGMENTS 262144
#endif
#ifndef HB_SVG_MAX_DOCUMENT_SIZE
#define HB_SVG_MAX_DOCUMENT_SIZE ((size_t) 16 << 20)
#endif
#ifndef HB_RASTER_MAX_BUFFER_SIZE
#define HB_RASTER_MAX_BUFFER_SIZE ((size_t) 1 << 24)
#endif
#endif /* HB_LIMITS_HH */
+4 -4
View File
@@ -31,7 +31,7 @@
#include "hb.hh"
#line 32 "hb-number-parser.hh"
#line 35 "hb-number-parser.hh"
static const unsigned char _double_parser_trans_keys[] = {
0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u,
46u, 101u, 0
@@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */)
int cs;
#line 132 "hb-number-parser.hh"
#line 139 "hb-number-parser.hh"
{
cs = double_parser_start;
}
#line 135 "hb-number-parser.hh"
#line 144 "hb-number-parser.hh"
{
int _slen;
int _trans;
@@ -198,7 +198,7 @@ _resume:
exp_overflow = true;
}
break;
#line 187 "hb-number-parser.hh"
#line 202 "hb-number-parser.hh"
}
_again:
+16 -1
View File
@@ -272,6 +272,8 @@ static inline void hb_object_make_immutable (const Type *obj)
obj->header.writable = false;
}
template <typename Type>
static inline void hb_object_fini (Type *obj);
template <typename Type>
static inline Type *hb_object_reference (Type *obj)
{
hb_object_trace (obj, HB_FUNC);
@@ -282,7 +284,7 @@ static inline Type *hb_object_reference (Type *obj)
return obj;
}
template <typename Type>
static inline bool hb_object_destroy (Type *obj)
static inline bool hb_object_should_destroy (Type *obj)
{
hb_object_trace (obj, HB_FUNC);
if (unlikely (!obj || obj->header.is_inert ()))
@@ -290,12 +292,25 @@ static inline bool hb_object_destroy (Type *obj)
assert (hb_object_is_valid (obj));
if (obj->header.ref_count.dec () != 1)
return false;
return true;
}
template <typename Type>
static inline void hb_object_actually_destroy (Type *obj)
{
hb_object_fini (obj);
if (!std::is_trivially_destructible<Type>::value)
obj->~Type ();
}
template <typename Type>
static inline bool hb_object_destroy (Type *obj)
{
if (!hb_object_should_destroy (obj))
return false;
hb_object_actually_destroy (obj);
return true;
}
template <typename Type>
+13 -8
View File
@@ -1275,11 +1275,22 @@ struct CFFIndex
if (unlikely (!serialize_header (c, +it, data_size, min_off_size))) return_trace (false);
unsigned char *ret = c->allocate_size<unsigned char> (data_size, false);
if (unlikely (!ret)) return_trace (false);
unsigned remaining = data_size;
for (const auto &_ : +it)
{
unsigned len = _.length;
if (!len)
continue;
if (unlikely (len > remaining)) {
// We have more bytes to write then the computed data size, so the size calculation
// must have encountered overflow.
return_trace (c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
remaining -= len;
if (len <= 1)
{
*ret++ = *_.arrayZ;
@@ -1948,6 +1959,7 @@ struct TupleValues
return true;
}
public:
void skip (unsigned n)
{
while (n)
@@ -1961,6 +1973,7 @@ struct TupleValues
}
}
private:
template <bool scaled>
void _add_to (hb_array_t<float> out, float scale = 1.0f)
{
@@ -2035,14 +2048,6 @@ struct TupleValues
public:
void add_to (hb_array_t<float> out, float scale = 1.0f)
{
unsigned n = out.length;
if (scale == 0.0f)
{
skip (n);
return;
}
#ifndef HB_OPTIMIZE_SIZE
// The following branch is supposed to speed things up by avoiding
// the multiplication in _add_to<> if scale is 1.0f.
+126 -44
View File
@@ -237,8 +237,6 @@ struct CmapSubtableFormat0
struct CmapSubtableFormat4
{
template<typename Iterator,
typename Writer,
hb_requires (hb_is_iterator (Iterator))>
@@ -521,9 +519,10 @@ struct CmapSubtableFormat4
struct accelerator_t
{
accelerator_t () {}
accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
accelerator_t (const CmapSubtableFormat4 *subtable) = delete;
void init (const CmapSubtableFormat4 *subtable)
void init (const CmapSubtableFormat4 *subtable,
unsigned int subtable_data_size)
{
segCount = subtable->segCountX2 / 2;
endCount = subtable->values.arrayZ;
@@ -531,7 +530,11 @@ struct CmapSubtableFormat4
idDelta = startCount + segCount;
idRangeOffset = idDelta + segCount;
glyphIdArray = idRangeOffset + segCount;
glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
unsigned int values_offset = 16 + 8 * segCount;
glyphIdArrayLength = subtable_data_size > values_offset
? (subtable_data_size - values_offset) / 2
: 0;
}
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
@@ -671,23 +674,36 @@ struct CmapSubtableFormat4
unsigned int glyphIdArrayLength;
};
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph,
unsigned int subtable_data_size) const
{
accelerator_t accel (this);
accelerator_t accel;
accel.init (this, subtable_data_size);
return accel.get_glyph_func (&accel, codepoint, glyph);
}
void collect_unicodes (hb_set_t *out) const
bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
{ return false; }
void collect_unicodes (hb_set_t *out, unsigned int subtable_data_size) const
{
accelerator_t accel (this);
accelerator_t accel;
accel.init (this, subtable_data_size);
accel.collect_unicodes (out);
}
void collect_unicodes (hb_set_t *out) const
{ collect_unicodes (out, length); }
void collect_mapping (hb_set_t *unicodes, /* OUT */
hb_map_t *mapping /* OUT */) const
hb_map_t *mapping, /* OUT */
unsigned int subtable_data_size) const
{
accelerator_t accel (this);
accelerator_t accel;
accel.init (this, subtable_data_size);
accel.collect_mapping (unicodes, mapping);
}
void collect_mapping (hb_set_t *unicodes, /* OUT */
hb_map_t *mapping /* OUT */) const
{ collect_mapping (unicodes, mapping, length); }
bool sanitize (hb_sanitize_context_t *c) const
{
@@ -696,14 +712,9 @@ struct CmapSubtableFormat4
return_trace (false);
hb_barrier ();
if (unlikely (!c->check_range (this, length)))
return_trace (false);
return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
return_trace (c->check_range (values, 2 + 4 * segCountX2));
}
protected:
HBUINT16 format; /* Format number is set to 4. */
HBUINT16 length; /* This is the length in bytes of the
@@ -1485,11 +1496,14 @@ struct CmapSubtable
/* Note: We intentionally do NOT implement subtable formats 2 and 8. */
bool get_glyph (hb_codepoint_t codepoint,
hb_codepoint_t *glyph) const
hb_codepoint_t *glyph,
unsigned int subtable_data_size = 0) const
{
switch (u.format.v) {
case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph);
case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph);
case 4: hb_barrier (); return subtable_data_size
? u.format4.get_glyph (codepoint, glyph, subtable_data_size)
: false;
case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph);
case 10: hb_barrier (); return u.format10.get_glyph (codepoint, glyph);
case 12: hb_barrier (); return u.format12.get_glyph (codepoint, glyph);
@@ -1498,11 +1512,17 @@ struct CmapSubtable
default: return false;
}
}
void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const
void collect_unicodes (hb_set_t *out,
unsigned int num_glyphs = UINT_MAX,
unsigned int subtable_data_size = 0) const
{
switch (u.format.v) {
case 0: hb_barrier (); u.format0 .collect_unicodes (out); return;
case 4: hb_barrier (); u.format4 .collect_unicodes (out); return;
case 4: hb_barrier (); if (subtable_data_size)
u.format4.collect_unicodes (out, subtable_data_size);
else
u.format4.collect_unicodes (out);
return;
case 6: hb_barrier (); u.format6 .collect_unicodes (out); return;
case 10: hb_barrier (); u.format10.collect_unicodes (out); return;
case 12: hb_barrier (); u.format12.collect_unicodes (out, num_glyphs); return;
@@ -1514,11 +1534,16 @@ struct CmapSubtable
void collect_mapping (hb_set_t *unicodes, /* OUT */
hb_map_t *mapping, /* OUT */
unsigned num_glyphs = UINT_MAX) const
unsigned num_glyphs = UINT_MAX,
unsigned int subtable_data_size = 0) const
{
switch (u.format.v) {
case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return;
case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return;
case 4: hb_barrier (); if (subtable_data_size)
u.format4.collect_mapping (unicodes, mapping, subtable_data_size);
else
u.format4.collect_mapping (unicodes, mapping);
return;
case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return;
case 10: hb_barrier (); u.format10.collect_mapping (unicodes, mapping); return;
case 12: hb_barrier (); u.format12.collect_mapping (unicodes, mapping, num_glyphs); return;
@@ -1633,7 +1658,7 @@ struct EncodingRecord
CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
unsigned origin_length = c->length ();
cmapsubtable->serialize (c, it, format, plan, &(base+subtable));
if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack ();
if (c->length () > origin_length && !c->in_error()) *objidx = c->pop_pack ();
else c->pop_discard ();
}
@@ -1662,6 +1687,7 @@ struct SubtableUnicodesCache {
private:
hb_blob_ptr_t<cmap> base_blob;
const char* base;
unsigned int table_length;
hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> cached_unicodes;
public:
@@ -1686,15 +1712,18 @@ struct SubtableUnicodesCache {
hb_free (cache);
}
SubtableUnicodesCache(const void* cmap_base)
SubtableUnicodesCache(const void* cmap_base,
unsigned int table_length_ = 0)
: base_blob(),
base ((const char*) cmap_base),
table_length (table_length_),
cached_unicodes ()
{}
SubtableUnicodesCache(hb_blob_ptr_t<cmap> base_blob_)
: base_blob(base_blob_),
base ((const char *) base_blob.get()),
table_length (base_blob.get_length ()),
cached_unicodes ()
{}
@@ -1725,7 +1754,10 @@ struct SubtableUnicodesCache {
if (unlikely (s->in_error ()))
return hb_set_get_empty ();
(base+record->subtable).collect_unicodes (s);
unsigned int subtable_data_size = record->subtable < table_length
? table_length - (unsigned int) record->subtable
: 0;
(base+record->subtable).collect_unicodes (s, UINT_MAX, subtable_data_size);
if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr<hb_set_t> {s})))
return hb_set_get_empty ();
@@ -1737,7 +1769,7 @@ struct SubtableUnicodesCache {
};
static inline uint_fast16_t
static inline uint16_t
_hb_symbol_pua_map (unsigned codepoint)
{
if (codepoint <= 0x00FFu)
@@ -1784,7 +1816,8 @@ struct cmap
EncodingRecIter encodingrec_iter,
const void *base,
hb_subset_plan_t *plan,
bool drop_format_4 = false)
bool drop_format_4 = false,
unsigned int source_table_length = 0)
{
if (unlikely (!c->extend_min ((*this)))) return false;
this->version = 0;
@@ -1792,7 +1825,7 @@ struct cmap
unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
auto snap = c->snapshot ();
SubtableUnicodesCache local_unicodes_cache (base);
SubtableUnicodesCache local_unicodes_cache (base, source_table_length);
const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache;
if (plan->accelerator &&
@@ -1821,7 +1854,8 @@ struct cmap
encodingrec_iter,
base,
plan,
true);
true,
source_table_length);
}
}
@@ -1838,9 +1872,11 @@ struct cmap
}
else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
}
c->check_assign(this->encodingRecord.len,
(c->length () - cmap::min_size)/EncodingRecord::static_size,
HB_SERIALIZE_ERROR_INT_OVERFLOW);
unsigned length = c->length ();
unsigned available = length > cmap::min_size ? length - cmap::min_size : 0;
c->check_assign(this->encodingRecord.len,
available / EncodingRecord::static_size,
HB_SERIALIZE_ERROR_INT_OVERFLOW);
// Fail if format 4 was dropped and there is no cmap12.
return !drop_format_4 || format12objidx;
@@ -1955,7 +1991,9 @@ struct cmap
it,
encodingrec_iter,
this,
c->plan));
c->plan,
false,
c->source_blob->length));
}
const CmapSubtable *find_best_subtable (bool *symbol = nullptr,
@@ -2034,32 +2072,60 @@ struct cmap
this->get_glyph_data = subtable;
#ifndef HB_NO_CMAP_LEGACY_SUBTABLES
bool is_format4 = subtable->u.format.v == 4;
auto set_format4_getter = [this] (bool (*func) (const void *,
hb_codepoint_t,
hb_codepoint_t *))
{
this->format4_accel.init (&this->subtable->u.format4,
get_subtable_data_size (this->subtable));
this->get_glyph_data = &this->format4_accel;
this->get_glyph_funcZ = func;
};
if (unlikely (symbol))
{
switch ((unsigned) face->table.OS2->get_font_page ()) {
case OS2::font_page_t::FONT_PAGE_NONE:
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_symbol_pua_map>;
if (is_format4)
set_format4_getter (get_glyph_from_symbol<CmapSubtableFormat4::accelerator_t, _hb_symbol_pua_map>);
else
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_symbol_pua_map>;
break;
#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK
case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC:
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_simp_map>;
if (is_format4)
set_format4_getter (get_glyph_from_symbol<CmapSubtableFormat4::accelerator_t, _hb_arabic_pua_simp_map>);
else
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_simp_map>;
break;
case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC:
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>;
if (is_format4)
set_format4_getter (get_glyph_from_symbol<CmapSubtableFormat4::accelerator_t, _hb_arabic_pua_trad_map>);
else
this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>;
break;
#endif
default:
this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
if (is_format4)
set_format4_getter (get_glyph_from<CmapSubtableFormat4::accelerator_t>);
else
this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
break;
}
}
else if (unlikely (macroman))
{
this->get_glyph_funcZ = get_glyph_from_macroman<CmapSubtable>;
if (is_format4)
set_format4_getter (get_glyph_from_macroman<CmapSubtableFormat4::accelerator_t>);
else
this->get_glyph_funcZ = get_glyph_from_macroman<CmapSubtable>;
}
else if (unlikely (mac))
{
this->get_glyph_funcZ = get_glyph_from_ascii<CmapSubtable>;
if (is_format4)
set_format4_getter (get_glyph_from_ascii<CmapSubtableFormat4::accelerator_t>);
else
this->get_glyph_funcZ = get_glyph_from_ascii<CmapSubtable>;
}
else
#endif
@@ -2074,7 +2140,8 @@ struct cmap
break;
case 4:
{
this->format4_accel.init (&subtable->u.format4);
this->format4_accel.init (&subtable->u.format4,
get_subtable_data_size (subtable));
this->get_glyph_data = &this->format4_accel;
this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
break;
@@ -2155,10 +2222,10 @@ struct cmap
}
void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
{ subtable->collect_unicodes (out, num_glyphs); }
{ subtable->collect_unicodes (out, num_glyphs, get_subtable_data_size (subtable)); }
void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping,
unsigned num_glyphs = UINT_MAX) const
{ subtable->collect_mapping (unicodes, mapping, num_glyphs); }
{ subtable->collect_mapping (unicodes, mapping, num_glyphs, get_subtable_data_size (subtable)); }
void collect_variation_selectors (hb_set_t *out) const
{ subtable_uvs->collect_variation_selectors (out); }
void collect_variation_unicodes (hb_codepoint_t variation_selector,
@@ -2169,7 +2236,7 @@ struct cmap
typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
hb_codepoint_t codepoint,
hb_codepoint_t *glyph);
typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned);
typedef uint16_t (*hb_pua_remap_func_t) (unsigned);
template <typename Type>
HB_INTERNAL static bool get_glyph_from (const void *obj,
@@ -2217,6 +2284,21 @@ struct cmap
return c && typed_obj->get_glyph (c, glyph);
}
unsigned int get_subtable_data_size (const CmapSubtable *subtable) const
{
unsigned int table_length = this->table.get_length ();
uintptr_t table_start = (uintptr_t) (const void *) this->table.get ();
uintptr_t subtable_addr = (uintptr_t) (const void *) subtable;
if (unlikely (subtable_addr < table_start))
return 0;
uintptr_t subtable_offset = subtable_addr - table_start;
if (unlikely (subtable_offset >= table_length))
return 0;
return table_length - (unsigned int) subtable_offset;
}
private:
hb_nonnull_ptr_t<const CmapSubtable> subtable;
hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs;
+68
View File
@@ -35,7 +35,9 @@
#include "OT/Color/COLR/COLR.hh"
#include "OT/Color/CPAL/CPAL.hh"
#include "OT/Color/sbix/sbix.hh"
#ifndef HB_NO_SVG
#include "OT/Color/svg/svg.hh"
#endif
/**
@@ -274,6 +276,7 @@ hb_ot_color_glyph_get_layers (hb_face_t *face,
* SVG
*/
#ifndef HB_NO_SVG
/**
* hb_ot_color_has_svg:
* @face: #hb_face_t to work upon.
@@ -290,6 +293,70 @@ hb_ot_color_has_svg (hb_face_t *face)
return face->table.SVG->has_data ();
}
/**
* hb_ot_color_get_svg_document_count:
* @face: #hb_face_t to work upon.
*
* Gets the number of SVG documents in the face `SVG` table.
*
* Return value: number of SVG documents in the face.
*
* Since: 12.1.0
*/
unsigned int
hb_ot_color_get_svg_document_count (hb_face_t *face)
{
return face->table.SVG->get_document_count ();
}
/**
* hb_ot_color_glyph_get_svg_document_index:
* @face: #hb_face_t to work upon.
* @glyph: glyph ID to query.
* @svg_document_index: (out) (nullable): output SVG document index.
*
* Gets the `SVG`-table document index associated with a glyph.
*
* Return value: `true` if @glyph maps to an SVG document, `false` otherwise.
*
* Since: 12.1.0
*/
hb_bool_t
hb_ot_color_glyph_get_svg_document_index (hb_face_t *face,
hb_codepoint_t glyph,
unsigned int *svg_document_index)
{
unsigned doc_index = 0;
hb_bool_t ret = face->table.SVG->get_glyph_document_index (glyph, &doc_index);
if (ret && svg_document_index)
*svg_document_index = doc_index;
return ret;
}
/**
* hb_ot_color_get_svg_document_glyph_range:
* @face: #hb_face_t to work upon.
* @svg_document_index: SVG document index.
* @start_glyph_id: (out) (nullable): output start glyph ID.
* @end_glyph_id: (out) (nullable): output end glyph ID.
*
* Gets the glyph range covered by an `SVG`-table document index.
*
* Return value: `true` if @svg_document_index is valid, `false` otherwise.
*
* Since: 13.0.0
*/
hb_bool_t
hb_ot_color_get_svg_document_glyph_range (hb_face_t *face,
unsigned int svg_document_index,
hb_codepoint_t *start_glyph_id,
hb_codepoint_t *end_glyph_id)
{
return face->table.SVG->get_document_glyph_range (svg_document_index,
start_glyph_id,
end_glyph_id);
}
/**
* hb_ot_color_glyph_reference_svg:
* @face: #hb_face_t to work upon
@@ -308,6 +375,7 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph)
{
return face->table.SVG->reference_blob_for_glyph (glyph);
}
#endif
/*
+14
View File
@@ -136,6 +136,20 @@ hb_ot_color_glyph_has_paint (hb_face_t *face,
HB_EXTERN hb_bool_t
hb_ot_color_has_svg (hb_face_t *face);
HB_EXTERN unsigned int
hb_ot_color_get_svg_document_count (hb_face_t *face);
HB_EXTERN hb_bool_t
hb_ot_color_glyph_get_svg_document_index (hb_face_t *face,
hb_codepoint_t glyph,
unsigned int *svg_document_index /* OUT */);
HB_EXTERN hb_bool_t
hb_ot_color_get_svg_document_glyph_range (hb_face_t *face,
unsigned int svg_document_index,
hb_codepoint_t *start_glyph_id, /* OUT */
hb_codepoint_t *end_glyph_id /* OUT */);
HB_EXTERN hb_blob_t *
hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph);
+2
View File
@@ -140,8 +140,10 @@ HB_OT_ACCELERATOR (OT, COLR)
HB_OT_CORE_TABLE (OT, CPAL)
HB_OT_ACCELERATOR (OT, CBDT)
HB_OT_ACCELERATOR (OT, sbix)
#ifndef HB_NO_SVG
HB_OT_ACCELERATOR (OT, SVG)
#endif
#endif
/* OpenType math. */
#ifndef HB_NO_MATH
+2
View File
@@ -937,7 +937,9 @@ hb_ot_paint_glyph_or_fail (hb_font_t *font,
{
#ifndef HB_NO_COLOR
if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return true;
#ifndef HB_NO_SVG
if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return true;
#endif
#ifndef HB_NO_OT_FONT_BITMAP
if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return true;
if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return true;
+21 -6
View File
@@ -2543,13 +2543,13 @@ struct SparseVarRegionAxis
struct hb_scalar_cache_t
{
private:
static constexpr unsigned STATIC_LENGTH = 16;
static constexpr unsigned STATIC_LENGTH = 128;
static constexpr int INVALID = INT_MIN;
static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2);
static constexpr float DIVISOR = 1.f / MULTIPLIER;
public:
hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); }
hb_scalar_cache_t () {}
hb_scalar_cache_t (const hb_scalar_cache_t&) = delete;
hb_scalar_cache_t (hb_scalar_cache_t&&) = delete;
@@ -2561,8 +2561,9 @@ struct hb_scalar_cache_t
{
if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t);
if (scratch_cache && count <= scratch_cache->length)
if (scratch_cache && count <= STATIC_LENGTH)
{
scratch_cache->length = count;
scratch_cache->clear ();
return scratch_cache;
}
@@ -2970,7 +2971,9 @@ struct VarData
}
if (unlikely (!c->extend_min (this))) return_trace (false);
itemCount = row_count;
if (unlikely (!c->check_assign (itemCount, row_count,
HB_SERIALIZE_ERROR_INT_OVERFLOW)))
return_trace (false);
int min_threshold = has_long ? -65536 : -128;
int max_threshold = has_long ? +65535 : +127;
@@ -3256,12 +3259,24 @@ struct MultiVarData
auto values_iter = deltaSets.fetcher (inner);
unsigned regionCount = regionIndices.len;
unsigned skip = 0;
for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex],
coords, coord_count,
cache);
values_iter.add_to (out, scalar);
// We skip lazily. Helps with the tail end.
if (scalar == 0.0f)
skip += out.length;
else
{
if (skip)
{
values_iter.skip (skip);
skip = 0;
}
values_iter.add_to (out, scalar);
}
}
}
@@ -4041,7 +4056,7 @@ struct ConditionValue
bool evaluate (const int *coords, unsigned int coord_len,
Instancer *instancer) const
{
signed value = defaultValue;
float value = defaultValue;
value += (*instancer)[varIdx];
return value > 0;
}
+54 -2
View File
@@ -743,6 +743,10 @@ struct hb_ot_apply_context_t :
hb_vector_t<uint32_t> match_positions;
uint32_t stack_match_positions[8];
#ifndef HB_NO_BUFFER_MESSAGE
hb_buffer_t::changed_func_t orig_changed_func = nullptr;
void *orig_changed_data = nullptr;
#endif
hb_ot_apply_context_t (unsigned int table_index_,
hb_font_t *font_,
@@ -773,6 +777,30 @@ struct hb_ot_apply_context_t :
{
init_iters ();
match_positions.set_storage (stack_match_positions);
#ifndef HB_NO_BUFFER_MESSAGE
if (buffer->messaging ())
{
assert (buffer->changed_func == nullptr ||
buffer->changed_func == buffer_changed_trampoline);
orig_changed_func = buffer->changed_func;
orig_changed_data = buffer->changed_data;
buffer->changed_func = buffer_changed_trampoline;
buffer->changed_data = this;
}
#endif
}
~hb_ot_apply_context_t ()
{
#ifndef HB_NO_BUFFER_MESSAGE
if (buffer->messaging ())
{
assert (buffer->changed_func == buffer_changed_trampoline);
assert (buffer->changed_data == this);
buffer->changed_func = orig_changed_func;
buffer->changed_data = orig_changed_data;
}
#endif
}
void init_iters ()
@@ -801,6 +829,18 @@ struct hb_ot_apply_context_t :
return buffer->random_state;
}
static void buffer_changed_trampoline (hb_buffer_t *buffer HB_UNUSED, void *user_data)
{
((hb_ot_apply_context_t *) user_data)->buffer_changed ();
}
void buffer_changed ()
{
buffer->update_digest ();
if (likely (has_glyph_classes))
for (unsigned i = 0; i < buffer->len; i++)
_set_glyph_class_props (buffer->info[i]);
}
HB_ALWAYS_INLINE
HB_HOT
bool match_properties_mark (const hb_glyph_info_t *info,
@@ -870,8 +910,7 @@ struct hb_ot_apply_context_t :
props |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
if (likely (has_glyph_classes))
{
props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
_hb_glyph_info_set_glyph_props (&buffer->cur(), props | gdef_accel.get_glyph_props (glyph_index));
_set_glyph_class_props (buffer->cur(), props, glyph_index);
}
else if (class_guess)
{
@@ -881,6 +920,19 @@ struct hb_ot_apply_context_t :
else
_hb_glyph_info_set_glyph_props (&buffer->cur(), props);
}
void _set_glyph_class_props (hb_glyph_info_t &info,
unsigned int props,
hb_codepoint_t glyph_index)
{
props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
_hb_glyph_info_set_glyph_props (&info, props | gdef_accel.get_glyph_props (glyph_index));
}
void _set_glyph_class_props (hb_glyph_info_t &info)
{
_set_glyph_class_props (info,
_hb_glyph_info_get_glyph_props (&info),
info.codepoint);
}
void replace_glyph (hb_codepoint_t glyph_index)
{
+2 -6
View File
@@ -580,12 +580,8 @@ hb_form_clusters (hb_buffer_t *buffer)
if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS))
return;
if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level))
foreach_grapheme (buffer, start, end)
buffer->merge_clusters (start, end);
else
foreach_grapheme (buffer, start, end)
buffer->unsafe_to_break (start, end);
foreach_grapheme (buffer, start, end)
buffer->merge_grapheme_clusters (start, end);
}
static void
+67 -71
View File
@@ -9,41 +9,43 @@
#ifndef HB_OT_SHAPER_ARABIC_PUA_HH
#define HB_OT_SHAPER_ARABIC_PUA_HH
static const uint8_t
_hb_arabic_u8[464] =
#include "hb.hh"
#include <stdint.h>
static const uint8_t _hb_arabic_pua_u8[453]=
{
84, 86, 85, 85, 85, 85, 85,213, 16, 34, 34, 34, 34, 34, 35, 34,
34, 34, 34, 34, 34, 34, 34, 34, 36, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 82, 16, 0, 0, 0, 0, 1, 2, 3, 4,
0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 7,
0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 0, 0, 0, 22, 0, 23, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 16, 34, 34, 34, 35, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 66, 16, 50, 68, 68, 68, 68, 68, 68,
68, 68, 68, 68,101, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
71, 68, 68, 68, 68, 68, 68, 68,152,186, 76, 77, 68,254, 16, 50,
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6,
0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 3, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 13, 0, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 23, 23, 29, 30, 31, 32, 33, 0, 0, 0, 0,
0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 40,
41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, 48, 49, 0, 0, 0,
0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, 57, 58, 0, 0, 0,
0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 66,
0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 6, 0, 7, 0, 0, 8, 0, 0, 0, 9, 0, 0,
10, 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 0,
22, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 33,
0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0,
0, 0, 0, 0, 0, 0, 0, 33, 67, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,101, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,152,
186, 12, 13, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4,
5, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0,
0, 0, 13, 0, 0, 0, 0, 0, 14, 0, 0, 15, 16, 17, 18, 19,
20, 21, 22, 23, 1, 24, 25, 26, 27, 28, 1, 1, 29, 30, 31, 32,
33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0,
0, 39, 0, 0, 40, 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47,
48, 49, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56,
57, 58, 0, 0, 0, 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 65, 0, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99,
};
static const uint16_t
_hb_arabic_u16[720] =
static const uint16_t _hb_arabic_pua_u16[717]=
{
0, 0, 0, 0, 0, 0, 0, 0,61728,61729,61730, 0, 0,61733, 0, 0,
61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879,
@@ -65,52 +67,46 @@ _hb_arabic_u16[720] =
61808,61813,61813,61811,61812,61816,61816,61814,61815,61818,61818,61817,61817,61820,61820,61819,
61819,61822,61822,61821,61821,61921,61921,61823,61823,61860,61859,61857,61858,61861,61861,61868,
61867,61864,61863,61862,61862,61888,61889,61886,61887,61890,61891,61885,61884, 0, 0, 0,
0, 0, 0, 0,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,61995,
62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,62043,
0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,61990,
0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,62015,
0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,62053,
62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,61952,61952,61952,61952,
62047,62130,62134,62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,
62186,62185,62182,61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007,
62008,62009, 0,62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0,
61954, 0,61981, 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0,
0, 0, 0,61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0,
61983, 0, 0, 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0,
0,62098, 0, 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0,
62100, 0, 0, 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0,
62106, 0,62107, 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088,
62109,61978,62089,62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096,
62097,62105, 0, 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0,
0,62021,62022,62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,
62027,62025,62026,62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,
62038,62048,62044,62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,
62060,62064,62063,62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,
62074,62114,62113,62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,
62124,62130,62129,62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,
62140,62146,62145,62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,
62163,62160,62159,62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0,
0,61952,61952,61952,61952,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,
61995,62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,
62043, 0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,
61990, 0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,
62015, 0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,
62053,62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,62047,62130,62134,
62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,62186,62185,62182,
61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007,62008,62009, 0,
62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0,61954, 0,61981,
0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, 0, 0, 0,
61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0,61983, 0, 0,
0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, 0,62098, 0,
0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0,62100, 0, 0,
0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0,62106, 0,62107,
0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088,62109,61978,62089,
62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096,62097,62105, 0,
0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, 0,62021,62022,
62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,62027,62025,62026,
62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,62038,62048,62044,
62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,62060,62064,62063,
62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,62074,62114,62113,
62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,62124,62130,62129,
62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,62140,62146,62145,
62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,62163,62160,62159,
62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0,
};
static inline unsigned
_hb_arabic_b2 (const uint8_t* a, unsigned i)
static inline uint8_t _hb_arabic_pua_b4 (const uint8_t* a, unsigned i)
{
return (a[i>>2]>>((i&3u)<<1))&3u;
return (a[i>>1]>>((i&1)<<2))&15;
}
static inline unsigned
_hb_arabic_b4 (const uint8_t* a, unsigned i)
static inline uint16_t _hb_arabic_pua_simp_map (unsigned u)
{
return (a[i>>1]>>((i&1u)<<2))&15u;
/* packtab: [2^2,2^4,2^4,2^3] */
return u<65277u ? (uint16_t)(_hb_arabic_pua_u16[((_hb_arabic_pua_u8[31u+((_hb_arabic_pua_b4(_hb_arabic_pua_u8,((((13835058055282164225ULL>>((((((((u)>>3))>>4))>>4))<<1))&3))<<4)+((((((u)>>3))>>4))&15)))<<4)+((((u)>>3))&15)])<<3)+((u)&7)]) : 0;
}
static inline uint_fast16_t
_hb_arabic_pua_simp_map (unsigned u)
static inline uint16_t _hb_arabic_pua_trad_map (unsigned u)
{
return u<65277u?_hb_arabic_u16[((_hb_arabic_u8[40+(((_hb_arabic_b4(8+_hb_arabic_u8,((_hb_arabic_b2(_hb_arabic_u8,u>>3>>4>>4))<<4)+((u>>3>>4)&15u)))<<4)+((u>>3)&15u))])<<3)+((u)&7u)]:0;
}
static inline uint_fast16_t
_hb_arabic_pua_trad_map (unsigned u)
{
return u<65277u?_hb_arabic_u16[320+(((_hb_arabic_u8[208+(((_hb_arabic_b4(168+_hb_arabic_u8,((_hb_arabic_b4(136+_hb_arabic_u8,u>>2>>4>>4))<<4)+((u>>2>>4)&15u)))<<4)+((u>>2)&15u))])<<2)+((u)&3u))]:0;
/* packtab: [2^4,2^4,2^4,2^2] */
return u<65277u ? (uint16_t)(_hb_arabic_pua_u16[317u+((_hb_arabic_pua_u8[197u+((_hb_arabic_pua_b4(_hb_arabic_pua_u8+159u,((_hb_arabic_pua_b4(_hb_arabic_pua_u8+127u,((((((u)>>2))>>4))>>4)))<<4)+((((((u)>>2))>>4))&15)))<<4)+((((u)>>2))&15)])<<2)+((u)&3)]) : 0;
}
#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */
+61 -206
View File
@@ -16,222 +16,77 @@
#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH
#define HB_OT_SHAPER_ARABIC_TABLE_HH
#include "hb.hh"
#define A JOINING_GROUP_ALAPH
#define DR JOINING_GROUP_DALATH_RISH
#define C JOINING_TYPE_C
#define D JOINING_TYPE_D
#define L JOINING_TYPE_L
#define R JOINING_TYPE_R
#define T JOINING_TYPE_T
#define U JOINING_TYPE_U
#define X JOINING_TYPE_X
#include <stdint.h>
static const uint8_t joining_table[] =
static const uint8_t _hb_arabic_joining_u8[737]=
{
0, 16, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 96,135, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0,
0, 0, 0, 8, 16, 24, 32, 40, 48, 0, 56, 0, 64, 72, 80, 0,
0, 0, 0, 88, 96,104, 0, 0, 0, 0, 0,112,120, 0, 0, 0,
0, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0,
0,144, 0, 0, 0, 0, 0,152, 0, 0, 0, 0, 0, 0,160,168,
176,184,192, 0, 0,200,208, 0, 0, 0, 0, 0, 0, 0, 0,216,
224, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 10, 11, 12,
1, 1, 13, 0, 0, 0, 14, 15, 1, 1, 2, 2, 4, 1, 1, 1,
1, 16, 17, 18, 3, 0, 19, 0, 20, 0, 21, 22, 23, 1, 24, 0,
0, 0, 25, 1, 26, 1, 27, 28, 4, 0, 29, 1, 1, 1, 30, 0,
31, 32, 5, 33, 34, 35, 36, 2, 2, 37, 38, 6, 0, 1, 39, 40,
5, 1, 7, 0, 0, 41, 0, 0, 0, 42, 43, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 7, 44, 1, 1, 1, 1, 45, 0,
0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, 6, 0,
0, 1, 1, 1, 1, 1, 1, 49, 0, 50, 51, 52, 53, 54, 55, 0,
0, 56, 57, 58, 0, 0, 59, 0, 0, 60, 1, 1, 1, 61, 0, 0,
0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63,
1, 64, 0, 65, 0, 0, 0, 66, 1, 67, 0, 0, 0, 0, 0, 68,
69, 70, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3, 0, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
1, 72, 73, 0, 0, 0, 0, 0, 0,119,119,119,119, 51, 51, 51,
51, 34, 34, 34, 34,119,119, 7,119, 34, 51, 51, 51, 35, 51, 51,
51, 0,119,119,119,115,119,119,119, 0, 0, 0,119,112, 7,119,
119, 3, 34, 34, 35, 35, 51, 51, 35, 34, 50, 51, 51, 50,115,119,
119,119,119,119, 51, 39, 34, 32, 34, 50, 35, 34, 34, 34, 34, 35,
35, 51, 34, 39,119,119,119,119, 34,119, 51,115, 55,119,119,119,
103,116, 51, 83, 37, 34, 51, 51, 50, 50, 53, 50, 83,119,119, 39,
51, 35, 34, 51, 51, 51, 35, 50, 51, 35, 35, 50, 51,119, 51, 51,
51, 51,115,119,119,119,115,119,119, 50, 51, 51, 34, 51, 51, 50,
34,114,119,119,119, 3, 51, 51, 32, 35,114,119,119, 34, 50, 51,
3, 48, 51, 51, 50, 51, 34, 2, 50, 35, 50, 51, 51,119,112,119,
119,119,119,119, 48,119,115,119,112, 0, 0, 96, 54,115,115,119,
119,119,119, 48,119,119,119,119, 7,119,119,119, 0, 51, 1,119,
119, 51, 51, 35, 32, 32, 2, 16, 34, 34, 50, 51, 19, 51, 51, 35,
51, 35, 0,114,119,119, 55, 51, 35, 35, 35, 34, 51, 35, 51, 50,
34, 35,119,119,119, 39, 34, 50, 3, 49, 51, 51, 51, 51, 50,119,
119,119, 50,115, 51, 51, 35, 51, 51, 51, 51, 3,119, 55, 51,114,
119, 51, 51, 34, 51, 51,119,119,119, 3, 51, 34, 2, 35, 50, 35,
51, 48, 34, 3, 0, 32, 19,119,119, 51, 51,119,119,119,103,119,
119,
};
#define joining_offset_0x0600u 0
/* Arabic */
/* 0600 */ U,U,U,U,U,U,X,X,U,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 0620 */ D,U,R,R,R,R,D,R,D,R,D,D,D,D,D,R,R,R,R,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 0640 */ C,D,D,D,D,D,D,D,R,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 0660 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,D,D,X,R,R,R,U,R,R,R,D,D,D,D,D,D,D,D,
/* 0680 */ D,D,D,D,D,D,D,D,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,D,D,D,D,D,D,
/* 06A0 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 06C0 */ R,D,D,R,R,R,R,R,R,R,R,R,D,R,D,R,D,D,R,R,X,R,X,X,X,X,X,X,X,U,X,X,
/* 06E0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,R,R,X,X,X,X,X,X,X,X,X,X,D,D,D,X,X,D,
/* Syriac */
/* 0700 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,T,A,X,D,D,D,DR,DR,R,R,R,D,D,D,D,R,D,
/* 0720 */ D,D,D,D,D,D,D,D,R,D,DR,D,R,D,D,DR,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 0740 */ X,X,X,X,X,X,X,X,X,X,X,X,X,R,D,D,
/* Arabic Supplement */
/* 0740 */ D,D,D,D,D,D,D,D,D,R,R,R,D,D,D,D,
/* 0760 */ D,D,D,D,D,D,D,D,D,D,D,R,R,D,D,D,D,R,D,R,R,D,D,D,R,R,D,D,D,D,D,D,
/* FILLER */
/* 0780 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 07A0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* NKo */
/* 07C0 */ X,X,X,X,X,X,X,X,X,X,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 07E0 */ D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,C,X,X,X,X,X,
/* FILLER */
/* 0800 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 0820 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* Mandaic */
/* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X,
/* Syriac Supplement */
/* 0860 */ D,U,D,D,D,D,U,R,D,R,R,X,X,X,X,X,
/* Arabic Extended-B */
/* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,
/* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,D,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* Arabic Extended-A */
/* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,D,D,D,D,R,D,D,D,D,D,D,
/* 08C0 */ D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 08E0 */ X,X,U,
#define joining_offset_0x1806u 739
/* Mongolian */
/* 1800 */ U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,
/* 1880 */ U,U,U,U,U,T,T,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 18A0 */ D,D,D,D,D,D,D,D,D,X,D,
#define joining_offset_0x200cu 904
/* General Punctuation */
/* 2000 */ U,C,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 2020 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 2040 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 2060 */ X,X,X,X,X,X,U,U,U,U,
#define joining_offset_0xa840u 998
/* Phags-pa */
/* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U,
#define joining_offset_0x10ac0u 1050
/* Manichaean */
/* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D,
/* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R,
#define joining_offset_0x10b80u 1098
/* Psalter Pahlavi */
/* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U,
#define joining_offset_0x10d00u 1146
/* Hanifi Rohingya */
/* 10D00 */ L,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 10D20 */ D,D,R,D,
#define joining_offset_0x10ec2u 1182
/* Arabic Extended-C */
/* 10EC0 */ R,D,D,X,D,D,
#define joining_offset_0x10f30u 1188
/* Sogdian */
/* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D,
/* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R,X,X,X,X,X,X,X,X,X,X,X,
/* 10F60 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* Old Uyghur */
/* 10F60 */ D,D,D,D,R,R,D,D,D,D,D,D,D,D,D,D,
/* 10F80 */ D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* 10FA0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
/* Chorasmian */
/* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D,
/* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L,
#define joining_offset_0x110bdu 1344
/* Kaithi */
/* 110A0 */ U,X,X,
/* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U,
#define joining_offset_0x1e900u 1361
/* Adlam */
/* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
/* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T,
}; /* Table items: 1437; occupancy: 58% */
static inline uint8_t _hb_arabic_joining_b4 (const uint8_t* a, unsigned i)
{
return (a[i>>1]>>((i&1)<<2))&15;
}
static inline uint8_t _hb_arabic_joining_joining_type (unsigned u)
{
/* packtab: [2^4,2^3,2^3,2^3] */
return u<125260u ? (uint8_t)(_hb_arabic_joining_b4(_hb_arabic_joining_u8+441u,((_hb_arabic_joining_u8[209u+_hb_arabic_joining_u8[123u+((_hb_arabic_joining_b4(_hb_arabic_joining_u8,((((((u)>>3))>>3))>>3)))<<3)+((((((u)>>3))>>3))&7)]+((((u)>>3))&7)])<<3)+((u)&7))) : 7;
}
static unsigned int
joining_type (hb_codepoint_t u)
{
switch (u >> 12)
{
case 0x0u:
if (hb_in_range<hb_codepoint_t> (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
break;
case 0x1u:
if (hb_in_range<hb_codepoint_t> (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
break;
case 0x2u:
if (hb_in_range<hb_codepoint_t> (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
break;
case 0xAu:
if (hb_in_range<hb_codepoint_t> (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u];
break;
case 0x10u:
if (hb_in_range<hb_codepoint_t> (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u];
if (hb_in_range<hb_codepoint_t> (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u];
if (hb_in_range<hb_codepoint_t> (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u];
if (hb_in_range<hb_codepoint_t> (u, 0x10EC2u, 0x10EC7u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u];
if (hb_in_range<hb_codepoint_t> (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u];
break;
case 0x11u:
if (hb_in_range<hb_codepoint_t> (u, 0x110BDu, 0x110CDu)) return joining_table[u - 0x110BDu + joining_offset_0x110bdu];
break;
case 0x1Eu:
if (hb_in_range<hb_codepoint_t> (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u];
break;
default:
break;
}
return X;
return _hb_arabic_joining_joining_type (u);
}
#undef A
#undef DR
#undef C
#undef D
#undef L
#undef R
#undef T
#undef U
#undef X
static const uint16_t shaping_table[][4] =
{
+35 -12
View File
@@ -77,8 +77,8 @@ enum hb_arabic_joining_type_t {
JOINING_GROUP_DALATH_RISH = 5,
NUM_STATE_MACHINE_COLS = 6,
JOINING_TYPE_T = 7,
JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */
JOINING_TYPE_T = 6,
JOINING_TYPE_X = 7 /* means: use general-category to choose between U or T. */
};
#include "hb-ot-shaper-arabic-table.hh"
@@ -561,20 +561,29 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed);
DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating);
/* Number of additional times to repeat each repeating tile. */
int n_copies = 0;
static constexpr unsigned STCH_MAX_GLYPHS = 256;
hb_position_t w_remaining = w_total - w_fixed;
if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0)
n_copies = (sign * w_remaining) / (sign * w_repeating) - 1;
/* Number of additional times to repeat each repeating tile. */
unsigned int n_copies = 0;
int64_t w_remaining_signed = (int64_t) w_total - w_fixed;
int64_t w_repeating_signed = w_repeating;
if (sign < 0)
{
w_remaining_signed = -w_remaining_signed;
w_repeating_signed = -w_repeating_signed;
}
hb_position_t w_remaining = (hb_position_t) (w_total - w_fixed);
if (w_remaining_signed > w_repeating_signed && w_repeating_signed > 0)
n_copies = w_remaining_signed / w_repeating_signed - 1;
/* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */
hb_position_t extra_repeat_overlap = 0;
hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1);
int64_t shortfall = w_remaining_signed - w_repeating_signed * (n_copies + 1);
if (shortfall > 0 && n_repeating > 0)
{
++n_copies;
hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining;
int64_t excess = (n_copies + 1) * w_repeating_signed - w_remaining_signed;
if (excess > 0)
{
extra_repeat_overlap = excess / (n_copies * n_repeating);
@@ -582,10 +591,22 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
}
}
unsigned int max_copies = 0;
if (n_repeating > 0)
{
unsigned int base_glyphs = n_fixed + n_repeating;
if (base_glyphs < STCH_MAX_GLYPHS)
max_copies = (STCH_MAX_GLYPHS - base_glyphs) / n_repeating;
}
n_copies = hb_min (n_copies, max_copies);
if (step == MEASURE)
{
extra_glyphs_needed += n_copies * n_repeating;
DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies);
unsigned int added_glyphs = 0;
if (unlikely (hb_unsigned_mul_overflows (n_copies, n_repeating, &added_glyphs) ||
hb_unsigned_add_overflows (extra_glyphs_needed, added_glyphs, &extra_glyphs_needed)))
break;
DEBUG_MSG (ARABIC, nullptr, "will add extra %u copies of repeating tiles", n_copies);
}
else
{
@@ -629,7 +650,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (step == MEASURE)
{
if (unlikely (!buffer->ensure (count + extra_glyphs_needed)))
unsigned int total_glyphs = 0;
if (unlikely (hb_unsigned_add_overflows (count, extra_glyphs_needed, &total_glyphs) ||
!buffer->ensure (total_glyphs)))
break;
}
else
+2 -2
View File
@@ -298,7 +298,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
end = start + 2;
if (unlikely (!buffer->successful))
break;
buffer->merge_out_clusters (start, end);
buffer->merge_out_grapheme_clusters (start, end);
continue;
}
}
@@ -371,7 +371,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (i < end)
info[i++].hangul_shaping_feature() = TJMO;
buffer->merge_out_clusters (start, end);
buffer->merge_out_grapheme_clusters (start, end);
continue;
}
else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint)))
+7 -7
View File
@@ -53,7 +53,7 @@ enum indic_syllable_type_t {
};
#line 54 "hb-ot-shaper-indic-machine.hh"
#line 57 "hb-ot-shaper-indic-machine.hh"
#define indic_syllable_machine_ex_A 9u
#define indic_syllable_machine_ex_C 1u
#define indic_syllable_machine_ex_CM 16u
@@ -77,7 +77,7 @@ enum indic_syllable_type_t {
#define indic_syllable_machine_ex_ZWNJ 5u
#line 76 "hb-ot-shaper-indic-machine.hh"
#line 81 "hb-ot-shaper-indic-machine.hh"
static const unsigned char _indic_syllable_machine_trans_keys[] = {
8u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u,
8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 4u, 57u,
@@ -1126,7 +1126,7 @@ find_syllables_indic (hb_buffer_t *buffer)
int cs;
hb_glyph_info_t *info = buffer->info;
#line 1119 "hb-ot-shaper-indic-machine.hh"
#line 1130 "hb-ot-shaper-indic-machine.hh"
{
cs = indic_syllable_machine_start;
ts = 0;
@@ -1142,7 +1142,7 @@ find_syllables_indic (hb_buffer_t *buffer)
unsigned int syllable_serial = 1;
#line 1131 "hb-ot-shaper-indic-machine.hh"
#line 1146 "hb-ot-shaper-indic-machine.hh"
{
int _slen;
int _trans;
@@ -1156,7 +1156,7 @@ _resume:
#line 1 "NONE"
{ts = p;}
break;
#line 1143 "hb-ot-shaper-indic-machine.hh"
#line 1160 "hb-ot-shaper-indic-machine.hh"
}
_keys = _indic_syllable_machine_trans_keys + (cs<<1);
@@ -1268,7 +1268,7 @@ _eof_trans:
#line 117 "hb-ot-shaper-indic-machine.rl"
{act = 7;}
break;
#line 1232 "hb-ot-shaper-indic-machine.hh"
#line 1272 "hb-ot-shaper-indic-machine.hh"
}
_again:
@@ -1277,7 +1277,7 @@ _again:
#line 1 "NONE"
{ts = 0;}
break;
#line 1239 "hb-ot-shaper-indic-machine.hh"
#line 1281 "hb-ot-shaper-indic-machine.hh"
}
if ( ++p != pe )
+102 -382
View File
@@ -130,392 +130,112 @@ static_assert (OT_VPst == M_Cat(VPst), "");
#define _(S,M) INDIC_COMBINE_CATEGORIES (_OT_##S, _POS_##M)
static const uint16_t indic_table[] = {
#define indic_offset_0x0028u 0
/* Basic Latin */
/* 0028 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X),
/* 0030 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0038 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x00b0u 24
/* Latin-1 Supplement */
/* 00B0 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X), _(X,X),
/* 00B8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 00C0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 00C8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 00D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C),
#define indic_offset_0x0900u 64
/* Devanagari */
/* 0900 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), _(V,C),
/* 0908 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
/* 0910 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0918 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0920 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0928 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0930 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0938 */ _(C,C), _(C,C), _(M,AS), _(M,AS), _(N,X), _(S,SM), _(M,AS), _(M,LM),
/* 0940 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS),
/* 0948 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(H,B), _(M,LM), _(M,AS),
/* 0950 */ _(X,X), _(A,SM), _(A,SM),_(SM,SM),_(SM,SM), _(M,AS), _(M,AS), _(M,AS),
/* 0958 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0960 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0968 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0970 */ _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
/* 0978 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* Bengali */
/* 0980 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
/* 0988 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C),
/* 0990 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0998 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 09A0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 09A8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 09B0 */ _(R,C), _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C),
/* 09B8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM),
/* 09C0 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM),
/* 09C8 */ _(M,LM), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(C,C), _(X,X),
/* 09D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP),
/* 09D8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C),
/* 09E0 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 09E8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 09F0 */ _(R,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 09F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X),_(SM,SM), _(X,X),
/* Gurmukhi */
/* 0A00 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
/* 0A08 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C),
/* 0A10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0A18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0A20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0A28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0A30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(X,X),
/* 0A38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(X,X), _(M,AP), _(M,LM),
/* 0A40 */_(MP,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP),
/* 0A48 */ _(M,AP), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X),
/* 0A50 */ _(X,X), _(M,B), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0A58 */ _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X),
/* 0A60 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0A68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0A70 */_(SM,SM),_(SM,SM), _(C,C), _(C,C), _(X,X), _(CM,C), _(X,X), _(X,X),
/* 0A78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* Gujarati */
/* 0A80 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
/* 0A88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C),
/* 0A90 */ _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0A98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0AA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0AA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0AB0 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C),
/* 0AB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM),
/* 0AC0 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AS), _(X,X), _(M,AS),
/* 0AC8 */ _(M,AS), _(M,AP), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X),
/* 0AD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0AD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0AE0 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0AE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0AF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0AF8 */ _(X,X), _(C,C), _(A,SM), _(N,X), _(A,SM), _(N,X), _(N,X), _(N,X),
/* Oriya */
/* 0B00 */ _(X,X),_(SM,BS),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
/* 0B08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C),
/* 0B10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0B18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0B20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0B28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0B30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C),
/* 0B38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,A),
/* 0B40 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM),
/* 0B48 */ _(M,A), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X),
/* 0B50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(N,X), _(M,A), _(M,AP),
/* 0B58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C),
/* 0B60 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0B68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0B70 */ _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0B78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* Tamil */
/* 0B80 */ _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(V,C), _(V,C), _(V,C),
/* 0B88 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(V,C), _(V,C),
/* 0B90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(X,X), _(X,X),
/* 0B98 */ _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), _(C,C), _(C,C),
/* 0BA0 */ _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X),
/* 0BA8 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C),
/* 0BB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0BB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), _(M,AP),
/* 0BC0 */ _(M,AS), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(M,LM), _(M,LM),
/* 0BC8 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(X,X), _(X,X),
/* 0BD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP),
/* 0BD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0BE0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0BE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0BF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0BF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* Telugu */
/* 0C00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C),
/* 0C08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C),
/* 0C10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0C18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0C20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0C28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0C30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0C38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS),
/* 0C40 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,BS),
/* 0C48 */ _(M,BS), _(X,X), _(M,BS), _(M,BS), _(M,BS), _(H,T), _(X,X), _(X,X),
/* 0C50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,BS), _(M,BS), _(X,X),
/* 0C58 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(C,C), _(X,X), _(X,X),
/* 0C60 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0C68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0C70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0C78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* Kannada */
/* 0C80 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C),
/* 0C88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C),
/* 0C90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0C98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0CA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0CA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0CB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C),
/* 0CB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS),
/* 0CC0 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,AS),
/* 0CC8 */ _(M,AS), _(X,X), _(M,AS), _(M,AS), _(M,BS), _(H,T), _(X,X), _(X,X),
/* 0CD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AS), _(M,AS), _(X,X),
/* 0CD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X),
/* 0CE0 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0CE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0CF0 */ _(X,X), _(CS,C), _(CS,C),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0CF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* Malayalam */
/* 0D00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(GB,C), _(V,C), _(V,C), _(V,C),
/* 0D08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C),
/* 0D10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C),
/* 0D18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0D20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0D28 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0D30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 0D38 */ _(C,C), _(C,C), _(C,C), _(M,AS), _(M,AS), _(S,SM), _(M,AP), _(M,AP),
/* 0D40 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(X,X), _(M,LM), _(M,LM),
/* 0D48 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(Rf,X), _(X,X),
/* 0D50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(M,AP),
/* 0D58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C),
/* 0D60 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C),
/* 0D68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 0D70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 0D78 */ _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
#define indic_offset_0x1000u 1216
/* Myanmar */
/* 1000 */ _(C,C), _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C),
/* 1008 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1010 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1018 */ _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1020 */ _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
/* 1028 */ _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), _(VA,T), _(VA,T), _(VB,B),
/* 1030 */ _(VB,B), _(VL,L), _(A,SM), _(VA,T), _(VA,T), _(VA,T), _(A,SM), _(N,X),
/* 1038 */_(SM,SM), _(H,X), _(As,X), _(MY,X), _(MR,X), _(MW,X), _(MH,X), _(C,C),
/* 1040 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 1048 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(C,C), _(X,X),
/* 1050 */ _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R),
/* 1058 */ _(VB,B), _(VB,B), _(R,C), _(C,C), _(C,C), _(C,C), _(MY,X), _(MY,X),
/* 1060 */ _(ML,X), _(C,C), _(VR,R), _(PT,X), _(PT,X), _(C,C), _(C,C), _(VR,R),
/* 1068 */ _(VR,R), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(C,C), _(C,C),
/* 1070 */ _(C,C), _(VA,T), _(VA,T), _(VA,T), _(VA,T), _(C,C), _(C,C), _(C,C),
/* 1078 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1080 */ _(C,C), _(C,C), _(MW,X), _(VR,R), _(VL,L), _(VA,T), _(VA,T),_(SM,SM),
/* 1088 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(C,C),_(SM,SM),
/* 1090 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 1098 */ _(GB,C), _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(VA,T), _(X,X), _(X,X),
#define indic_offset_0x1780u 1376
/* Khmer */
/* 1780 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1788 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1790 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 1798 */ _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* 17A0 */ _(C,C), _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
/* 17A8 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C),
/* 17B0 */ _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(VR,R), _(VA,T),
/* 17B8 */ _(VA,T), _(VA,T), _(VA,T), _(VB,B), _(VB,B), _(VB,B), _(VA,T), _(VR,R),
/* 17C0 */ _(VR,R), _(VL,L), _(VL,L), _(VL,L), _(VR,R), _(VR,R), _(Xg,X), _(Yg,X),
/* 17C8 */ _(Yg,X), _(Rt,X), _(Rt,X), _(Xg,X), _(Rt,X), _(Xg,X), _(Xg,X), _(Xg,X),
/* 17D0 */ _(Xg,X), _(Xg,X), _(H,X), _(Yg,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 17D8 */ _(X,X), _(GB,C), _(X,X), _(X,X), _(S,SM), _(Yg,X), _(X,X), _(X,X),
/* 17E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 17E8 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x1cd0u 1488
/* Vedic Extensions */
/* 1CD0 */ _(A,SM), _(A,SM), _(A,SM), _(X,X), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
/* 1CD8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
/* 1CE0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
/* 1CE8 */ _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(A,SM), _(S,SM), _(S,SM),
/* 1CF0 */ _(S,SM), _(S,SM), _(C,C), _(C,C), _(A,SM), _(C,C), _(C,C), _(A,SM),
/* 1CF8 */ _(A,SM), _(A,SM), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x2008u 1536
/* General Punctuation */
/* 2008 */ _(X,X), _(X,X), _(X,X), _(X,X),_(ZWNJ,X),_(ZWJ,X), _(X,X), _(X,X),
/* 2010 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X),
/* 2018 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 2020 */ _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x2070u 1568
/* Superscripts and Subscripts */
/* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SP,SM), _(X,X), _(X,X), _(X,X),
/* 2078 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X),
/* 2080 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x25f8u 1592
/* Geometric Shapes */
/* 25F8 */ _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X),
#define indic_offset_0xa8e0u 1600
/* Devanagari Extended */
/* A8E0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
/* A8E8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM),
/* A8F0 */ _(A,SM), _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM),
/* A8F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), _(M,AS),
#define indic_offset_0xa9e0u 1632
/* Myanmar Extended-B */
/* A9E0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(VA,T), _(X,X), _(C,C),
/* A9E8 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* A9F0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* A9F8 */ _(GB,C), _(GB,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X),
#define indic_offset_0xaa60u 1664
/* Myanmar Extended-A */
/* AA60 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* AA68 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C),
/* AA70 */ _(X,X), _(C,C), _(C,C), _(C,C), _(GB,C), _(GB,C), _(GB,C), _(X,X),
/* AA78 */ _(X,X), _(X,X), _(C,C), _(PT,X), _(N,X), _(N,X), _(C,C), _(C,C),
#define indic_offset_0xfe00u 1696
/* Variation Selectors */
/* FE00 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X),
/* FE08 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X),
#define indic_offset_0x11300u 1712
/* Grantha */
/* 11300 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x11338u 1720
/* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X),
#define indic_offset_0x116d0u 1728
/* Myanmar Extended-C */
/* 116D0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 116D8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C),
/* 116E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X),
}; /* Table items: 1752; occupancy: 71% */
#include <stdint.h>
static const uint16_t _hb_indic_values[42]=
{
_(A,SM), _(As,X), _(C,C), _(CM,C), _(CS,C), _(DC,C), _(GB,C), _(H,B),
_(H,T), _(H,X), _(M,A), _(M,AP), _(M,AS), _(M,B), _(M,BS), _(M,LM),
_(MH,X), _(ML,X), _(MP,AP), _(MR,X), _(MW,X), _(MY,X), _(N,X), _(PT,X),
_(R,C), _(Rf,X), _(Rt,X), _(S,SM), _(SM,BS), _(SM,SM), _(SP,SM), _(V,C),
_(VA,T), _(VB,B), _(VL,L), _(VR,R), _(VS,X), _(X,X), _(Xg,X), _(Yg,X),
_(ZWJ,X),_(ZWNJ,X),
};
static const uint8_t _hb_indic_u8[1220]=
{
1, 0, 50, 4, 5, 96, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,
0, 0, 0, 0,208,224, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 22, 23,
24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 26, 0, 0,
0, 27, 0, 0, 0, 0, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0, 33, 0, 34,
0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 36, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 25, 2, 10, 0, 0, 0, 0, 26, 0,
27, 0, 0, 0, 28, 0, 0, 0, 0, 0, 29, 11, 30, 1, 1, 1,
4, 31, 32, 33, 34, 1, 6, 2, 35, 1, 12, 13, 7, 1, 1, 3,
36, 14, 15, 37, 16, 17, 6, 2, 38, 39, 18, 40, 7, 1, 1, 3,
41, 42, 43, 44, 45, 46, 19, 2, 47, 0, 18, 48, 49, 1, 1, 3,
20, 14, 50, 51, 0, 0, 21, 2, 0, 52, 53, 13, 7, 1, 1, 3,
20, 54, 15, 55, 56, 17, 6, 2, 57, 0, 58, 59, 60, 61, 62, 63,
4, 64, 65, 66, 16, 0, 19, 2, 0, 0, 67, 8, 9, 1, 1, 3,
4, 22, 68, 69, 70, 71, 23, 2, 0, 0, 12, 8, 9, 1, 1, 3,
72, 22, 73, 74, 75, 76, 23, 2, 77, 0, 78, 8, 9, 1, 1, 1,
4, 79, 80, 81, 82, 83, 21, 2, 0, 84, 85, 1, 1, 86, 87, 88,
89, 90, 2, 91, 92, 93, 94, 95, 96, 1, 97, 98, 2, 99, 0, 0,
0, 0, 1, 1, 1,100,101, 11,102,103,104,105,106,107, 2, 10,
0, 0, 0, 0,108, 5, 5,109,110,111, 0,112,113, 0,114, 0,
0, 0, 0, 0, 0, 0, 0, 0,115, 0,116, 0, 0, 0, 0, 0,
0, 0, 0,117, 0, 0, 0, 0, 0,118, 0, 0, 0, 0, 5, 5,
119,120, 0, 0, 0, 0,121, 1, 2,122, 0, 0, 0, 0, 1, 1,
123,124, 24, 24, 0, 0, 0, 0, 0, 0,125, 0, 0, 0, 0, 0,
0,126, 0, 0, 2, 2, 2, 10, 0, 0, 0, 0, 2, 2, 2, 2,
4, 4, 4, 4, 10, 2, 2, 2, 26, 2, 2, 2, 8, 8, 8, 8,
6, 18, 0, 4, 20, 14, 24, 2, 6, 6, 20, 6, 20, 6, 24, 2,
4, 0, 0, 0, 6, 6, 6, 6, 66, 12, 14, 6, 6, 6, 20, 14,
2, 0, 36, 50, 52, 18, 38, 68, 0, 0, 0, 32, 0, 0, 2, 16,
56, 12, 14, 6, 0, 0, 0, 4, 44, 2, 16, 2, 6, 22, 0, 4,
2, 0, 36, 28, 6, 28, 0, 4, 30, 30, 30, 30, 0, 0, 42, 0,
34, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 42, 12, 12, 6, 6,
6, 6, 24, 2, 2, 18, 36,112, 18, 18, 18, 18, 18, 18,114,116,
118,120,122, 18, 0, 6, 6, 6, 44, 10, 0, 2, 54, 32, 46, 10,
26, 0, 0, 0, 0, 0, 34, 70, 6, 20, 0, 14, 44, 2, 16, 10,
2, 0, 72, 50,124, 48, 0, 32, 48, 32, 46, 0,126, 0, 0, 0,
16, 2, 10, 10, 12, 2,128, 0, 6, 6, 6, 14, 6, 14, 24, 2,
22, 22, 52, 74, 76, 32, 46, 0, 16, 58, 58, 78,130, 12, 14, 6,
2, 0, 36,132,134, 32, 46, 0, 0, 0, 80,136, 16, 0, 0, 0,
0, 70, 14, 6, 6, 20, 0, 6, 20, 6, 24, 0, 16, 10, 10, 2,
0, 16, 10, 0, 2, 10, 0, 2, 2, 0, 0, 22, 76, 48, 0, 82,
54, 22, 84, 0, 12, 12,138, 6, 28, 60, 38, 28, 86, 28, 88, 0,
0, 0,140, 86, 2, 10, 16, 0, 26, 2, 16, 2, 28, 60, 38, 60,
38, 18, 88, 0, 0, 0, 74, 38, 0, 0, 16, 10,142,144, 0, 0,
12, 12,146, 6, 2,148,150, 22, 22, 22, 48, 82, 54, 22, 84,152,
0, 0, 2,154, 0, 0, 0, 14, 0, 2, 2, 2, 2, 2, 26, 2,
2,156, 2, 2, 90, 6, 6, 6, 6,158, 92, 94,160,162, 62, 58,
164,166,168,170, 4, 4, 0, 10, 2, 6, 6, 96, 98, 26, 2,172,
174,100,176,178,100,102,102, 2,104, 62,180, 2, 2,182,184,186,
12, 12, 12,188, 4, 12,190, 0, 2, 26, 2, 2, 2, 90, 6, 6,
6, 6, 0, 92, 62, 94, 98,192,194,196, 96,198,200,106,106,108,
108,202, 0, 0, 42, 0,204, 0, 8,206, 8, 8,208, 40,210, 40,
40, 2,212,214, 8, 34, 0, 0, 0, 0,216, 0, 4, 4, 4, 0,
0, 34, 0, 0, 0, 0,110, 0, 0, 64,110, 0, 0, 0,218, 0,
0, 42, 4, 34, 8, 40, 40, 40, 0, 0, 0,220, 2, 2,104, 16,
4, 2, 2, 10, 16, 2, 4, 34, 0,222, 78, 2, 56, 12, 0, 0,
0, 80, 72, 0, 37, 37, 2, 2, 6, 6, 31, 31, 0, 0, 2, 37,
29, 29, 37, 31, 37, 2, 12, 12, 31, 37, 11, 11, 31, 2, 24, 2,
14, 14, 36, 36, 37, 11, 6, 37, 22, 27, 12, 37, 27, 27, 37, 6,
24, 37, 11, 7, 11, 37, 11, 15, 11, 12, 15, 37, 37, 29, 0, 22,
14, 12, 32, 32, 30, 30, 6, 29, 37, 15, 29, 37, 22, 37, 37, 12,
12, 11, 22, 22, 37, 22, 15, 15, 11, 8, 14, 37, 14, 8, 2, 31,
35, 32, 32, 33, 35, 35, 33, 33, 35, 23, 23, 23, 2, 32, 26, 38,
38, 38, 30, 37, 12, 15, 12, 7, 15, 12, 37, 0, 0, 29, 29, 12,
18, 11, 37, 13, 37, 3, 37, 28, 11, 10, 10, 37, 10, 11, 29, 31,
37, 14, 37, 4, 4, 29, 6, 31, 2, 12, 12, 27, 25, 37, 2, 11,
2, 24, 31, 35, 33, 34, 0, 32, 29, 9, 1, 21, 19, 20, 16, 2,
21, 21, 17, 2, 23, 2, 2, 35, 32, 2, 20, 35, 34, 32, 32, 29,
2, 29, 29, 32, 32, 35, 35, 34, 34, 34, 38, 39, 39, 26, 9, 39,
27, 39, 0, 37, 0, 27, 27, 0, 0, 2, 2, 0, 41, 40, 5, 37,
31, 12, 2, 23,
};
static inline uint8_t _hb_indic_b4 (const uint8_t* a, unsigned i)
{
return (a[i>>1]>>((i&1)<<2))&15;
}
static inline uint8_t _hb_indic_get_categories_index (unsigned u)
{
/* packtab: [2^4,2^3,2^3,2^2,2^1] */
return u<71396u ? (uint8_t)(_hb_indic_u8[996u+_hb_indic_u8[488u+((_hb_indic_u8[186u+((_hb_indic_u8[70u+((_hb_indic_b4(_hb_indic_u8,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)]+((u)&1)]) : 37;
}
uint16_t
hb_indic_get_categories (hb_codepoint_t u)
{
switch (u >> 12)
{
case 0x0u:
if (unlikely (u == 0x00A0u)) return _(GB,C);
if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0D7Fu)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
break;
case 0x1u:
if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
break;
case 0x2u:
if (unlikely (u == 0x25CCu)) return _(DC,C);
if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2027u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
if (hb_in_range<hb_codepoint_t> (u, 0x25F8u, 0x25FFu)) return indic_table[u - 0x25F8u + indic_offset_0x25f8u];
break;
case 0xAu:
if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
break;
case 0xFu:
if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return indic_table[u - 0xFE00u + indic_offset_0xfe00u];
break;
case 0x11u:
if (hb_in_range<hb_codepoint_t> (u, 0x11300u, 0x11307u)) return indic_table[u - 0x11300u + indic_offset_0x11300u];
if (hb_in_range<hb_codepoint_t> (u, 0x11338u, 0x1133Fu)) return indic_table[u - 0x11338u + indic_offset_0x11338u];
if (hb_in_range<hb_codepoint_t> (u, 0x116D0u, 0x116E7u)) return indic_table[u - 0x116D0u + indic_offset_0x116d0u];
break;
default:
break;
}
return _(X,X);
return _hb_indic_values[_hb_indic_get_categories_index (u)];
}
#undef _
+7 -7
View File
@@ -48,7 +48,7 @@ enum khmer_syllable_type_t {
};
#line 49 "hb-ot-shaper-khmer-machine.hh"
#line 52 "hb-ot-shaper-khmer-machine.hh"
#define khmer_syllable_machine_ex_C 1u
#define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u
#define khmer_syllable_machine_ex_H 4u
@@ -66,7 +66,7 @@ enum khmer_syllable_type_t {
#define khmer_syllable_machine_ex_ZWNJ 5u
#line 65 "hb-ot-shaper-khmer-machine.hh"
#line 70 "hb-ot-shaper-khmer-machine.hh"
static const unsigned char _khmer_syllable_machine_trans_keys[] = {
5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u,
5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u,
@@ -294,7 +294,7 @@ find_syllables_khmer (hb_buffer_t *buffer)
int cs;
hb_glyph_info_t *info = buffer->info;
#line 287 "hb-ot-shaper-khmer-machine.hh"
#line 298 "hb-ot-shaper-khmer-machine.hh"
{
cs = khmer_syllable_machine_start;
ts = 0;
@@ -310,7 +310,7 @@ find_syllables_khmer (hb_buffer_t *buffer)
unsigned int syllable_serial = 1;
#line 299 "hb-ot-shaper-khmer-machine.hh"
#line 314 "hb-ot-shaper-khmer-machine.hh"
{
int _slen;
int _trans;
@@ -324,7 +324,7 @@ _resume:
#line 1 "NONE"
{ts = p;}
break;
#line 311 "hb-ot-shaper-khmer-machine.hh"
#line 328 "hb-ot-shaper-khmer-machine.hh"
}
_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
@@ -394,7 +394,7 @@ _eof_trans:
#line 98 "hb-ot-shaper-khmer-machine.rl"
{act = 3;}
break;
#line 368 "hb-ot-shaper-khmer-machine.hh"
#line 398 "hb-ot-shaper-khmer-machine.hh"
}
_again:
@@ -403,7 +403,7 @@ _again:
#line 1 "NONE"
{ts = 0;}
break;
#line 375 "hb-ot-shaper-khmer-machine.hh"
#line 407 "hb-ot-shaper-khmer-machine.hh"
}
if ( ++p != pe )
+7 -7
View File
@@ -50,7 +50,7 @@ enum myanmar_syllable_type_t {
};
#line 51 "hb-ot-shaper-myanmar-machine.hh"
#line 54 "hb-ot-shaper-myanmar-machine.hh"
#define myanmar_syllable_machine_ex_A 9u
#define myanmar_syllable_machine_ex_As 32u
#define myanmar_syllable_machine_ex_C 1u
@@ -78,7 +78,7 @@ enum myanmar_syllable_type_t {
#define myanmar_syllable_machine_ex_ZWNJ 5u
#line 77 "hb-ot-shaper-myanmar-machine.hh"
#line 82 "hb-ot-shaper-myanmar-machine.hh"
static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u,
3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u,
@@ -549,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
int cs;
hb_glyph_info_t *info = buffer->info;
#line 542 "hb-ot-shaper-myanmar-machine.hh"
#line 553 "hb-ot-shaper-myanmar-machine.hh"
{
cs = myanmar_syllable_machine_start;
ts = 0;
@@ -565,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
unsigned int syllable_serial = 1;
#line 554 "hb-ot-shaper-myanmar-machine.hh"
#line 569 "hb-ot-shaper-myanmar-machine.hh"
{
int _slen;
int _trans;
@@ -579,7 +579,7 @@ _resume:
#line 1 "NONE"
{ts = p;}
break;
#line 566 "hb-ot-shaper-myanmar-machine.hh"
#line 583 "hb-ot-shaper-myanmar-machine.hh"
}
_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
@@ -649,7 +649,7 @@ _eof_trans:
#line 113 "hb-ot-shaper-myanmar-machine.rl"
{act = 3;}
break;
#line 623 "hb-ot-shaper-myanmar-machine.hh"
#line 653 "hb-ot-shaper-myanmar-machine.hh"
}
_again:
@@ -658,7 +658,7 @@ _again:
#line 1 "NONE"
{ts = 0;}
break;
#line 630 "hb-ot-shaper-myanmar-machine.hh"
#line 662 "hb-ot-shaper-myanmar-machine.hh"
}
if ( ++p != pe )
+5 -7
View File
@@ -356,13 +356,11 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan,
sizeof (buffer->out_info[0]) * (end - start - 2));
buffer->out_info[start] = t;
}
else
{
/* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
* previous cluster. */
if (start)
buffer->merge_out_clusters (start - 1, end);
}
/* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
* previous cluster. */
if (start)
buffer->merge_out_grapheme_clusters (start - 1, end);
}
buffer->sync ();
+7 -7
View File
@@ -53,7 +53,7 @@ enum use_syllable_type_t {
};
#line 54 "hb-ot-shaper-use-machine.hh"
#line 57 "hb-ot-shaper-use-machine.hh"
#define use_syllable_machine_ex_B 1u
#define use_syllable_machine_ex_CGJ 6u
#define use_syllable_machine_ex_CMAbv 31u
@@ -100,7 +100,7 @@ enum use_syllable_type_t {
#define use_syllable_machine_ex_ZWNJ 14u
#line 99 "hb-ot-shaper-use-machine.hh"
#line 104 "hb-ot-shaper-use-machine.hh"
static const unsigned char _use_syllable_machine_trans_keys[] = {
49u, 51u, 0u, 56u, 11u, 56u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u,
14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u,
@@ -929,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer)
unsigned int act HB_UNUSED;
int cs;
#line 922 "hb-ot-shaper-use-machine.hh"
#line 933 "hb-ot-shaper-use-machine.hh"
{
cs = use_syllable_machine_start;
ts = 0;
@@ -942,7 +942,7 @@ find_syllables_use (hb_buffer_t *buffer)
unsigned int syllable_serial = 1;
#line 931 "hb-ot-shaper-use-machine.hh"
#line 946 "hb-ot-shaper-use-machine.hh"
{
int _slen;
int _trans;
@@ -956,7 +956,7 @@ _resume:
#line 1 "NONE"
{ts = p;}
break;
#line 943 "hb-ot-shaper-use-machine.hh"
#line 960 "hb-ot-shaper-use-machine.hh"
}
_keys = _use_syllable_machine_trans_keys + (cs<<1);
@@ -1078,7 +1078,7 @@ _eof_trans:
#line 181 "hb-ot-shaper-use-machine.rl"
{act = 9;}
break;
#line 1039 "hb-ot-shaper-use-machine.hh"
#line 1082 "hb-ot-shaper-use-machine.hh"
}
_again:
@@ -1087,7 +1087,7 @@ _again:
#line 1 "NONE"
{ts = 0;}
break;
#line 1046 "hb-ot-shaper-use-machine.hh"
#line 1091 "hb-ot-shaper-use-machine.hh"
}
if ( ++p != pe )
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
/*
* The following functions are generated by running:
*
* ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
* gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
*
* on files with these headers:
*
File diff suppressed because it is too large Load Diff
+126 -27
View File
@@ -234,6 +234,29 @@ lang_matches (const char *lang_str,
(lang_str[spec_len] == '\0' || lang_str[spec_len] == '-');
}
static bool
bfind_tag (const hb_tag_t *array,
unsigned len,
hb_tag_t key)
{
unsigned min = 0;
unsigned max = len;
while (min < max)
{
unsigned mid = min + (max - min) / 2;
hb_tag_t val = array[mid];
if (key < val)
max = mid;
else if (key > val)
min = mid + 1;
else
return true;
}
return false;
}
struct LangTag
{
hb_tag_t language;
@@ -247,6 +270,20 @@ struct LangTag
{ return cmp (that->language); }
};
struct LangTagRange
{
hb_tag_t language;
uint16_t offset;
uint8_t count;
int cmp (hb_tag_t a) const
{
return a < this->language ? -1 : a > this->language ? +1 : 0;
}
int cmp (const LangTagRange *that) const
{ return cmp (that->language); }
};
#include "hb-ot-tag-table.hh"
/* The corresponding languages IDs for the following IDs are unclear,
@@ -307,46 +344,84 @@ hb_ot_tags_from_language (const char *lang_str,
lang_str = s + 1;
}
#endif
const LangTag *ot_languages = nullptr;
unsigned ot_languages_len = 0;
const char *dash = strchr (lang_str, '-');
unsigned first_len = dash ? dash - lang_str : limit - lang_str;
hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len);
if (first_len == 2)
{
ot_languages = ot_languages2;
ot_languages_len = ARRAY_LENGTH (ot_languages2);
static hb_atomic_t<unsigned> last_tag_idx_2 = 0; /* Poor man's cache. */
unsigned tag_idx = last_tag_idx_2;
if (likely (tag_idx < ARRAY_LENGTH (ot_languages2) &&
ot_languages2[tag_idx].language == lang_tag) ||
hb_sorted_array (ot_languages2).bfind (lang_tag, &tag_idx))
{
last_tag_idx_2 = tag_idx;
unsigned int i;
while (tag_idx != 0 &&
ot_languages2[tag_idx].language == ot_languages2[tag_idx - 1].language)
tag_idx--;
for (i = 0;
i < *count &&
tag_idx + i < ARRAY_LENGTH (ot_languages2) &&
ot_languages2[tag_idx + i].tag != HB_TAG_NONE &&
ot_languages2[tag_idx + i].language == ot_languages2[tag_idx].language;
i++)
tags[i] = ot_languages2[tag_idx + i].tag;
*count = i;
return;
}
}
#ifndef HB_NO_LANGUAGE_LONG
else if (first_len == 3)
{
ot_languages = ot_languages3;
ot_languages_len = ARRAY_LENGTH (ot_languages3);
}
#endif
static hb_atomic_t<unsigned> last_tag_idx_3 = 0; /* Poor man's cache. */
unsigned tag_idx = last_tag_idx_3;
hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len);
if (likely (tag_idx < ARRAY_LENGTH (ot_languages3) &&
ot_languages3[tag_idx].language == lang_tag) ||
hb_sorted_array (ot_languages3).bfind (lang_tag, &tag_idx))
{
last_tag_idx_3 = tag_idx;
if (*count)
{
tags[0] = ot_languages3[tag_idx].tag;
*count = 1;
}
else
*count = 0;
return;
}
static hb_atomic_t<unsigned> last_tag_idx = 0; /* Poor man's cache. */
unsigned tag_idx = last_tag_idx;
static hb_atomic_t<unsigned> last_tag_idx_3_multi = 0; /* Poor man's cache. */
unsigned multi_tag_idx = last_tag_idx_3_multi;
if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) ||
hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx))
{
last_tag_idx = tag_idx;
unsigned int i;
while (tag_idx != 0 &&
ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language)
tag_idx--;
for (i = 0;
i < *count &&
tag_idx + i < ot_languages_len &&
ot_languages[tag_idx + i].tag != HB_TAG_NONE &&
ot_languages[tag_idx + i].language == ot_languages[tag_idx].language;
i++)
tags[i] = ot_languages[tag_idx + i].tag;
*count = i;
if (likely (multi_tag_idx < ARRAY_LENGTH (ot_languages3_multi) &&
ot_languages3_multi[multi_tag_idx].language == lang_tag) ||
hb_sorted_array (ot_languages3_multi).bfind (lang_tag, &multi_tag_idx))
{
last_tag_idx_3_multi = multi_tag_idx;
const LangTagRange &range = ot_languages3_multi[multi_tag_idx];
unsigned int i;
for (i = 0; i < *count && i < range.count; i++)
tags[i] = ot_languages3_multi_values[range.offset + i];
*count = i;
return;
}
if (bfind_tag (ot_languages3_blocked, ARRAY_LENGTH (ot_languages3_blocked), lang_tag))
{
*count = 0;
return;
}
/* Assume it's ISO-639-3 and upper-case and use it. */
tags[0] = lang_tag & ~0x20202000u;
*count = 1;
return;
}
#endif
}
#ifndef HB_NO_LANGUAGE_LONG
@@ -525,6 +600,13 @@ hb_ot_tag_to_language (hb_tag_t tag)
hb_tag_to_string (ot_languages3[i].language, buf);
return hb_language_from_string (buf, 3);
}
for (i = 0; i < ARRAY_LENGTH (ot_languages3_multi); i++)
for (unsigned int j = 0; j < ot_languages3_multi[i].count; j++)
if (ot_languages3_multi_values[ot_languages3_multi[i].offset + j] == tag)
{
hb_tag_to_string (ot_languages3_multi[i].language, buf);
return hb_language_from_string (buf, 3);
}
#endif
/* Return a custom language in the form of "x-hbot-AABBCCDD".
@@ -642,6 +724,23 @@ test_langs_sorted ()
abort();
}
}
for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3_multi); i++)
{
int c = ot_languages3_multi[i].cmp (&ot_languages3_multi[i - 1]);
if (c > 0)
{
fprintf (stderr, "ot_languages3_multi not sorted at index %u: %08x %d %08x\n",
i, ot_languages3_multi[i-1].language, c, ot_languages3_multi[i].language);
abort();
}
}
for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3_blocked); i++)
if (ot_languages3_blocked[i] < ot_languages3_blocked[i - 1])
{
fprintf (stderr, "ot_languages3_blocked not sorted at index %u: %08x < %08x\n",
i, ot_languages3_blocked[i], ot_languages3_blocked[i - 1]);
abort();
}
#endif
}
+42 -6
View File
@@ -409,6 +409,13 @@ struct tuple_delta_t
return;
}
if (!axis_limit.is_point () &&
!(-1.0 <= axis_limit.minimum &&
axis_limit.minimum <= axis_limit.middle &&
axis_limit.middle <= axis_limit.maximum &&
axis_limit.maximum <= +1.0))
return;
rebase_tent_result_t &solutions = scratch.first;
rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second);
for (unsigned i = 0; i < solutions.length; i++)
@@ -1700,6 +1707,16 @@ struct item_variations_t
const hb_map_t& get_varidx_map () const
{ return varidx_map; }
bool add_vardata_encoding_for_testing (hb_vector_t<const hb_vector_t<int>*> &&rows,
unsigned num_cols)
{
encodings.push (delta_row_encoding_t (std::move (rows), num_cols));
return !encodings.in_error ();
}
bool compile_varidx_map_for_testing (const hb_hashmap_t<unsigned, const hb_vector_t<int>*>& front_mapping)
{ return compile_varidx_map (front_mapping); }
bool instantiate (const ItemVariationStore& varStore,
const hb_subset_plan_t *plan,
bool optimize=true,
@@ -2046,27 +2063,46 @@ struct item_variations_t
{
/* full encoding_row -> new VarIdxes mapping */
hb_hashmap_t<const hb_vector_t<int>*, unsigned> back_mapping;
hb_vector_t<delta_row_encoding_t> split_encodings;
for (unsigned major = 0; major < encodings.length; major++)
for (unsigned i = 0; i < encodings.length; i++)
{
delta_row_encoding_t& encoding = encodings[major];
delta_row_encoding_t& encoding = encodings[i];
/* just sanity check, this shouldn't happen */
if (encoding.is_empty ())
return false;
unsigned num_rows = encoding.items.length;
unsigned num_cols = encoding.chars.length;
/* sort rows, make result deterministic */
encoding.items.qsort (_cmp_row);
/* compile old to new var_idxes mapping */
for (unsigned minor = 0; minor < num_rows; minor++)
for (unsigned start = 0; start < num_rows; start += 0xFFFFu)
{
unsigned new_varidx = (major << 16) + minor;
back_mapping.set (encoding.items.arrayZ[minor], new_varidx);
unsigned chunk_len = hb_min (num_rows - start, 0xFFFFu);
hb_vector_t<const hb_vector_t<int>*> rows;
if (!rows.alloc (chunk_len))
return false;
unsigned major = split_encodings.length;
for (unsigned minor = 0; minor < chunk_len; minor++)
{
const hb_vector_t<int> *row = encoding.items.arrayZ[start + minor];
rows.push (row);
if (!back_mapping.set (row, (major << 16) + minor))
return false;
}
split_encodings.push (delta_row_encoding_t (std::move (rows), num_cols));
}
}
encodings = std::move (split_encodings);
if (encodings.in_error () || back_mapping.in_error ())
return false;
for (auto _ : front_mapping.iter ())
{
unsigned old_varidx = _.first;
+107 -2
View File
@@ -634,6 +634,83 @@ struct gvar_GVAR
static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end)
{ return (i >= end) ? start : (i + 1); }
#ifndef HB_OPTIMIZE_SIZE
template <bool is_x>
#endif
static bool decompile_deltas_add_to_points (const HBUINT8 *&p /* IN/OUT */,
hb_array_t<contour_point_t> points,
float scalar,
const HBUINT8 *end,
unsigned start
#ifdef HB_OPTIMIZE_SIZE
, bool is_x
#endif
)
{
unsigned i = 0;
unsigned count = points.length;
while (i < count)
{
if (unlikely (p + 1 > end)) return false;
unsigned control = *p++;
unsigned run_count = (control & TupleValues::VALUE_RUN_COUNT_MASK) + 1;
unsigned stop = i + run_count;
if (unlikely (stop > count)) return false;
unsigned skip = i < start ? hb_min (start - i, run_count) : 0;
i += skip;
switch (control & TupleValues::VALUES_SIZE_MASK)
{
case TupleValues::VALUES_ARE_ZEROS:
i = stop;
break;
case TupleValues::VALUES_ARE_WORDS:
{
if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
p += skip * HBINT16::static_size;
const auto *pp = (const HBINT16 *) p;
for (; i < stop; i++)
{
float v = *pp++ * scalar;
if (is_x) points.arrayZ[i].x += v;
else points.arrayZ[i].y += v;
}
p = (const HBUINT8 *) pp;
}
break;
case TupleValues::VALUES_ARE_LONGS:
{
if (unlikely (p + run_count * HBINT32::static_size > end)) return false;
p += skip * HBINT32::static_size;
const auto *pp = (const HBINT32 *) p;
for (; i < stop; i++)
{
float v = *pp++ * scalar;
if (is_x) points.arrayZ[i].x += v;
else points.arrayZ[i].y += v;
}
p = (const HBUINT8 *) pp;
}
break;
case TupleValues::VALUES_ARE_BYTES:
{
if (unlikely (p + run_count > end)) return false;
p += skip * HBINT8::static_size;
const auto *pp = (const HBINT8 *) p;
for (; i < stop; i++)
{
float v = *pp++ * scalar;
if (is_x) points.arrayZ[i].x += v;
else points.arrayZ[i].y += v;
}
p = (const HBUINT8 *) pp;
}
break;
}
}
return true;
}
public:
bool apply_deltas_to_points (hb_codepoint_t glyph,
hb_array_t<const int> coords,
@@ -656,6 +733,9 @@ struct gvar_GVAR
shared_indices, &iterator))
return true; /* so isn't applied at all */
bool any_private_points = false;
bool private_points_checked = false;
/* Save original points for inferred delta calculation */
auto &orig_points_vec = scratch.orig_points;
orig_points_vec.clear (); // Populated lazily
@@ -682,6 +762,20 @@ struct gvar_GVAR
gvar_cache);
if (scalar == 0.f) continue;
if (!private_points_checked)
{
auto scan = iterator;
do
{
if (scan.current_tuple->has_private_points ())
{
any_private_points = true;
break;
}
} while (scan.move_to_next ());
private_points_checked = true;
}
const HBUINT8 *p = iterator.get_serialized_data ();
unsigned int length = iterator.current_tuple->get_data_size ();
if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
@@ -706,6 +800,19 @@ struct gvar_GVAR
bool apply_to_all = (indices.length == 0);
unsigned num_deltas = apply_to_all ? points.length : indices.length;
unsigned start_deltas = (apply_to_all && phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0);
if (apply_to_all && !any_private_points)
{
#ifdef HB_OPTIMIZE_SIZE
if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas, true))) return false;
if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas, false))) return false;
#else
if (unlikely (!decompile_deltas_add_to_points<true> (p, points, scalar, end, start_deltas))) return false;
if (unlikely (!decompile_deltas_add_to_points<false> (p, points, scalar, end, start_deltas))) return false;
#endif
continue;
}
if (unlikely (!x_deltas.resize_dirty (num_deltas))) return false;
if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false;
if (unlikely (!y_deltas.resize_dirty (num_deltas))) return false;
@@ -724,8 +831,6 @@ struct gvar_GVAR
{
for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
points.arrayZ[i].translate (deltas.arrayZ[i]);
flush = false;
}
hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0,
(phantom_only ? 4 : count) * sizeof (deltas[0]));
+6 -6
View File
@@ -742,14 +742,14 @@ hb_paint_pop_group (hb_paint_funcs_t *funcs, void *paint_data,
/**
* hb_paint_custom_palette_color:
* @funcs: paint functions
* @paint_data: associated data passed by the caller
* @color_index: color index
* @color: (out): fetched color
* @funcs: paint functions.
* @paint_data: associated data passed by the caller.
* @color_index: color index to fetch.
* @color: (out): fetched color.
*
* Gets the custom palette color for @color_index.
* Gets the custom palette override color for @color_index.
*
* Return value: `true` if found, `false` otherwise
* Return value: `true` if a custom color is provided, `false` otherwise.
*
* Since: 7.0.0
*/
+19 -18
View File
@@ -684,23 +684,24 @@ typedef void (*hb_paint_pop_group_func_t) (hb_paint_funcs_t *funcs,
/**
* hb_paint_custom_palette_color_func_t:
* @funcs: paint functions object
* @paint_data: The data accompanying the paint functions in hb_font_paint_glyph()
* @color_index: the color index
* @color: (out): fetched color
* @user_data: User data pointer passed to hb_paint_funcs_set_pop_group_func()
* @funcs: paint functions object.
* @paint_data: data accompanying the paint functions in hb_font_paint_glyph().
* @color_index: color index to fetch.
* @color: (out): fetched color.
* @user_data: user data pointer passed to hb_paint_funcs_set_custom_palette_color_func().
*
* A virtual method for the #hb_paint_funcs_t to fetch a color from the custom
* color palette.
* A virtual method for #hb_paint_funcs_t to fetch a custom palette override
* color for @color_index.
*
* Custom palette colors override the colors from the fonts selected color
* palette. It is not necessary to override all palette entries; for entries
* that should be taken from the font palette, return `false`.
* Custom palette colors override colors from the font's selected color palette.
* It is not necessary to override all palette entries; return `false` for
* entries that should be taken from the font palette.
*
* This function might get called multiple times, but the custom palette is
* expected to remain unchanged for duration of a hb_font_paint_glyph() call.
* This function might be called multiple times, but the custom palette is
* expected to remain unchanged for the duration of one
* hb_font_paint_glyph() call.
*
* Return value: `true` if found, `false` otherwise
* Return value: `true` if a custom color is provided, `false` otherwise.
*
* Since: 7.0.0
*/
@@ -934,12 +935,12 @@ hb_paint_funcs_set_pop_group_func (hb_paint_funcs_t *funcs,
/**
* hb_paint_funcs_set_custom_palette_color_func:
* @funcs: A paint functions struct
* @func: (closure user_data) (destroy destroy) (scope notified): The custom-palette-color callback
* @user_data: Data to pass to @func
* @destroy: (nullable): Function to call when @user_data is no longer needed
* @funcs: a paint functions struct.
* @func: (closure user_data) (destroy destroy) (scope notified): custom-palette-color callback.
* @user_data: data to pass to @func.
* @destroy: (nullable): function to call when @user_data is no longer needed.
*
* Sets the custom-palette-color callback on the paint functions struct.
* Sets the custom-palette-color callback on @funcs.
*
* Since: 7.0.0
*/
File diff suppressed because it is too large Load Diff
+967
View File
@@ -0,0 +1,967 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#include "hb.hh"
#include "hb-raster-image.hh"
#include <math.h>
#ifdef HAVE_PNG
#include <png.h>
#endif
#ifdef HAVE_PNG
struct hb_raster_png_read_blob_t
{
const uint8_t *data = nullptr;
size_t size = 0;
size_t offset = 0;
};
static void
hb_raster_png_error (png_structp png,
png_const_charp msg HB_UNUSED)
{
png_longjmp (png, 1);
}
static void
hb_raster_png_warning (png_structp png HB_UNUSED,
png_const_charp msg HB_UNUSED)
{}
static void
hb_raster_png_read_blob (png_structp png, png_bytep out, png_size_t length)
{
hb_raster_png_read_blob_t *r = (hb_raster_png_read_blob_t *) png_get_io_ptr (png);
if (!r || !r->data || length > r->size - r->offset)
png_error (png, "read error");
hb_memcpy (out, r->data + r->offset, length);
r->offset += length;
}
struct hb_raster_png_write_blob_t
{
hb_vector_t<char> data;
};
static void
hb_raster_png_write_blob (png_structp png, png_bytep in, png_size_t length)
{
hb_raster_png_write_blob_t *w = (hb_raster_png_write_blob_t *) png_get_io_ptr (png);
if (!w)
png_error (png, "write error");
unsigned old_length = w->data.length;
if (!w->data.resize_dirty ((int) (old_length + length)))
png_error (png, "write error");
hb_memcpy (w->data.arrayZ + old_length, in, length);
}
static void
hb_raster_png_flush_blob (png_structp png HB_UNUSED)
{}
#endif
/*
* Image compositing
*/
/* Unpack premultiplied pixel to float RGBA [0,1]. */
static inline void
unpack_to_float (uint32_t px, float &r, float &g, float &b, float &a)
{
b = (px & 0xFF) / 255.f;
g = ((px >> 8) & 0xFF) / 255.f;
r = ((px >> 16) & 0xFF) / 255.f;
a = (px >> 24) / 255.f;
}
/* Pack float RGBA [0,1] premultiplied back to uint32_t. */
static inline uint32_t
pack_from_float (float r, float g, float b, float a)
{
return hb_raster_pack_pixel ((uint8_t) (hb_clamp (b, 0.f, 1.f) * 255.f + 0.5f),
(uint8_t) (hb_clamp (g, 0.f, 1.f) * 255.f + 0.5f),
(uint8_t) (hb_clamp (r, 0.f, 1.f) * 255.f + 0.5f),
(uint8_t) (hb_clamp (a, 0.f, 1.f) * 255.f + 0.5f));
}
/* Separable blend mode functions: operate on unpremultiplied [0,1] channels. */
static inline float
blend_multiply (float sc, float dc) { return sc * dc; }
static inline float
blend_screen (float sc, float dc) { return sc + dc - sc * dc; }
static inline float
blend_overlay (float sc, float dc)
{ return dc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); }
static inline float
blend_darken (float sc, float dc) { return hb_min (sc, dc); }
static inline float
blend_lighten (float sc, float dc) { return hb_max (sc, dc); }
static inline float
blend_color_dodge (float sc, float dc)
{
if (dc <= 0.f) return 0.f;
if (sc >= 1.f) return 1.f;
return hb_min (1.f, dc / (1.f - sc));
}
static inline float
blend_color_burn (float sc, float dc)
{
if (dc >= 1.f) return 1.f;
if (sc <= 0.f) return 0.f;
return 1.f - hb_min (1.f, (1.f - dc) / sc);
}
static inline float
blend_hard_light (float sc, float dc)
{ return sc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); }
static inline float
blend_soft_light (float sc, float dc)
{
if (sc <= 0.5f)
return dc - (1.f - 2.f * sc) * dc * (1.f - dc);
float d = (dc <= 0.25f) ? ((16.f * dc - 12.f) * dc + 4.f) * dc
: sqrtf (dc);
return dc + (2.f * sc - 1.f) * (d - dc);
}
static inline float
blend_difference (float sc, float dc) { return fabsf (sc - dc); }
static inline float
blend_exclusion (float sc, float dc) { return sc + dc - 2.f * sc * dc; }
/* Apply a separable blend mode per-pixel.
* Both src and dst are premultiplied BGRA32. */
static inline uint32_t
apply_separable_blend (uint32_t src, uint32_t dst,
float (*blend_fn)(float, float))
{
float sr, sg, sb, sa;
float dr, dg, db, da;
unpack_to_float (src, sr, sg, sb, sa);
unpack_to_float (dst, dr, dg, db, da);
float usr = sa > 0.f ? sr / sa : 0.f;
float usg = sa > 0.f ? sg / sa : 0.f;
float usb = sa > 0.f ? sb / sa : 0.f;
float udr = da > 0.f ? dr / da : 0.f;
float udg = da > 0.f ? dg / da : 0.f;
float udb = da > 0.f ? db / da : 0.f;
float br = blend_fn (usr, udr);
float bg = blend_fn (usg, udg);
float bb = blend_fn (usb, udb);
float ra = sa + da - sa * da;
float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr;
float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg;
float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb;
return pack_from_float (rr, rg, rb, ra);
}
/* HSL helpers */
static inline float
hsl_luminosity (float r, float g, float b)
{ return 0.299f * r + 0.587f * g + 0.114f * b; }
static inline float
hsl_saturation (float r, float g, float b)
{ return hb_max (hb_max (r, g), b) - hb_min (hb_min (r, g), b); }
static inline void
hsl_clip_color (float &r, float &g, float &b)
{
float l = hsl_luminosity (r, g, b);
float mn = hb_min (hb_min (r, g), b);
float mx = hb_max (hb_max (r, g), b);
if (mn < 0.f)
{
float d = l - mn;
if (d > 0.f) { r = l + (r - l) * l / d; g = l + (g - l) * l / d; b = l + (b - l) * l / d; }
}
if (mx > 1.f)
{
float d = mx - l;
if (d > 0.f) { r = l + (r - l) * (1.f - l) / d; g = l + (g - l) * (1.f - l) / d; b = l + (b - l) * (1.f - l) / d; }
}
}
static inline void
hsl_set_luminosity (float &r, float &g, float &b, float l)
{
float d = l - hsl_luminosity (r, g, b);
r += d; g += d; b += d;
hsl_clip_color (r, g, b);
}
static inline void
hsl_set_saturation_inner (float &mn, float &mid, float &mx, float s)
{
if (mx > mn)
{
mid = (mid - mn) * s / (mx - mn);
mx = s;
}
else
mid = mx = 0.f;
mn = 0.f;
}
static inline void
hsl_set_saturation (float &r, float &g, float &b, float s)
{
if (r <= g)
{
if (g <= b) hsl_set_saturation_inner (r, g, b, s);
else if (r <= b) hsl_set_saturation_inner (r, b, g, s);
else hsl_set_saturation_inner (b, r, g, s);
}
else
{
if (r <= b) hsl_set_saturation_inner (g, r, b, s);
else if (g <= b) hsl_set_saturation_inner (g, b, r, s);
else hsl_set_saturation_inner (b, g, r, s);
}
}
static inline uint32_t
apply_hsl_blend (uint32_t src, uint32_t dst,
hb_paint_composite_mode_t mode)
{
float sr, sg, sb, sa;
float dr, dg, db, da;
unpack_to_float (src, sr, sg, sb, sa);
unpack_to_float (dst, dr, dg, db, da);
float usr = sa > 0.f ? sr / sa : 0.f;
float usg = sa > 0.f ? sg / sa : 0.f;
float usb = sa > 0.f ? sb / sa : 0.f;
float udr = da > 0.f ? dr / da : 0.f;
float udg = da > 0.f ? dg / da : 0.f;
float udb = da > 0.f ? db / da : 0.f;
float br = udr, bg = udg, bb = udb;
if (mode == HB_PAINT_COMPOSITE_MODE_HSL_HUE)
{
br = usr; bg = usg; bb = usb;
hsl_set_saturation (br, bg, bb, hsl_saturation (udr, udg, udb));
hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
}
else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_SATURATION)
{
br = udr; bg = udg; bb = udb;
hsl_set_saturation (br, bg, bb, hsl_saturation (usr, usg, usb));
hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
}
else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_COLOR)
{
br = usr; bg = usg; bb = usb;
hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb));
}
else /* HSL_LUMINOSITY */
{
br = udr; bg = udg; bb = udb;
hsl_set_luminosity (br, bg, bb, hsl_luminosity (usr, usg, usb));
}
float ra = sa + da - sa * da;
float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr;
float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg;
float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb;
return pack_from_float (rr, rg, rb, ra);
}
/* Composite per-pixel with full blend mode support. */
static inline uint32_t
composite_pixel (uint32_t src, uint32_t dst,
hb_paint_composite_mode_t mode)
{
uint8_t sa = (uint8_t) (src >> 24);
uint8_t da = (uint8_t) (dst >> 24);
switch (mode)
{
case HB_PAINT_COMPOSITE_MODE_CLEAR:
return 0;
case HB_PAINT_COMPOSITE_MODE_SRC:
return src;
case HB_PAINT_COMPOSITE_MODE_DEST:
return dst;
case HB_PAINT_COMPOSITE_MODE_SRC_OVER:
return hb_raster_src_over (src, dst);
case HB_PAINT_COMPOSITE_MODE_DEST_OVER:
return hb_raster_src_over (dst, src);
case HB_PAINT_COMPOSITE_MODE_SRC_IN:
return hb_raster_alpha_mul (src, da);
case HB_PAINT_COMPOSITE_MODE_DEST_IN:
return hb_raster_alpha_mul (dst, sa);
case HB_PAINT_COMPOSITE_MODE_SRC_OUT:
return hb_raster_alpha_mul (src, 255 - da);
case HB_PAINT_COMPOSITE_MODE_DEST_OUT:
return hb_raster_alpha_mul (dst, 255 - sa);
case HB_PAINT_COMPOSITE_MODE_SRC_ATOP:
{
/* Fa=Da, Fb=1-Sa */
uint32_t a = hb_raster_alpha_mul (src, da);
uint32_t b = hb_raster_alpha_mul (dst, 255 - sa);
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
return hb_raster_pack_pixel (rb, rg, rr, ra);
}
case HB_PAINT_COMPOSITE_MODE_DEST_ATOP:
{
uint32_t a = hb_raster_alpha_mul (dst, sa);
uint32_t b = hb_raster_alpha_mul (src, 255 - da);
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
return hb_raster_pack_pixel (rb, rg, rr, ra);
}
case HB_PAINT_COMPOSITE_MODE_XOR:
{
uint32_t a = hb_raster_alpha_mul (src, 255 - da);
uint32_t b = hb_raster_alpha_mul (dst, 255 - sa);
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF));
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF));
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF));
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24));
return hb_raster_pack_pixel (rb, rg, rr, ra);
}
case HB_PAINT_COMPOSITE_MODE_PLUS:
{
uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (src & 0xFF) + (dst & 0xFF));
uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF));
uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF));
uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (src >> 24) + (dst >> 24));
return hb_raster_pack_pixel (rb, rg, rr, ra);
}
case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return apply_separable_blend (src, dst, blend_multiply);
case HB_PAINT_COMPOSITE_MODE_SCREEN: return apply_separable_blend (src, dst, blend_screen);
case HB_PAINT_COMPOSITE_MODE_OVERLAY: return apply_separable_blend (src, dst, blend_overlay);
case HB_PAINT_COMPOSITE_MODE_DARKEN: return apply_separable_blend (src, dst, blend_darken);
case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return apply_separable_blend (src, dst, blend_lighten);
case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return apply_separable_blend (src, dst, blend_color_dodge);
case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return apply_separable_blend (src, dst, blend_color_burn);
case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return apply_separable_blend (src, dst, blend_hard_light);
case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return apply_separable_blend (src, dst, blend_soft_light);
case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return apply_separable_blend (src, dst, blend_difference);
case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return apply_separable_blend (src, dst, blend_exclusion);
case HB_PAINT_COMPOSITE_MODE_HSL_HUE:
case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION:
case HB_PAINT_COMPOSITE_MODE_HSL_COLOR:
case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY:
return apply_hsl_blend (src, dst, mode);
default:
return hb_raster_src_over (src, dst);
}
}
/* hb_raster_image_t */
unsigned
hb_raster_image_t::bytes_per_pixel (hb_raster_format_t format)
{
return format == HB_RASTER_FORMAT_BGRA32 ? 4u : 1u;
}
bool
hb_raster_image_t::configure (hb_raster_format_t format,
hb_raster_extents_t extents)
{
if (format != HB_RASTER_FORMAT_A8 &&
format != HB_RASTER_FORMAT_BGRA32)
format = HB_RASTER_FORMAT_A8;
unsigned bpp = bytes_per_pixel (format);
if (extents.width > UINT_MAX / bpp)
return false;
unsigned min_stride = extents.width * bpp;
if (extents.stride == 0 || extents.stride < min_stride)
extents.stride = min_stride;
if (extents.height && extents.stride > (size_t) -1 / extents.height)
return false;
size_t buf_size = (size_t) extents.stride * extents.height;
if (buf_size > HB_RASTER_MAX_BUFFER_SIZE)
return false;
if (unlikely (!buffer.resize_dirty (buf_size)))
return false;
this->format = format;
this->extents = extents;
return true;
}
bool
hb_raster_image_t::deserialize_from_png (hb_blob_t *blob)
{
#ifndef HAVE_PNG
return false;
#else
if (!blob)
return false;
unsigned blob_len = 0;
const uint8_t *blob_data = (const uint8_t *) hb_blob_get_data (blob, &blob_len);
if (!blob_data || !blob_len)
return false;
png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING, nullptr,
hb_raster_png_error,
hb_raster_png_warning);
if (!png)
return false;
png_infop info = png_create_info_struct (png);
if (!info)
{
png_destroy_read_struct (&png, nullptr, nullptr);
return false;
}
hb_raster_png_read_blob_t reader;
reader.data = blob_data;
reader.size = (size_t) blob_len;
reader.offset = 0;
hb_vector_t<uint8_t> rgba;
hb_vector_t<png_bytep> rows;
if (setjmp (png_jmpbuf (png)))
{
png_destroy_read_struct (&png, &info, nullptr);
rgba.fini ();
rows.fini ();
return false;
}
png_set_read_fn (png, &reader, hb_raster_png_read_blob);
png_read_info (png, info);
png_uint_32 w = 0, h = 0;
int bit_depth = 0, color_type = 0;
int interlace_type = 0, compression_type = 0, filter_method = 0;
png_get_IHDR (png, info, &w, &h, &bit_depth, &color_type,
&interlace_type, &compression_type, &filter_method);
if (!w || !h || w > (png_uint_32) INT_MAX || h > (png_uint_32) INT_MAX)
{
png_destroy_read_struct (&png, &info, nullptr);
return false;
}
bool has_trns = png_get_valid (png, info, PNG_INFO_tRNS);
if (bit_depth == 16)
png_set_strip_16 (png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb (png);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8 (png);
if (has_trns)
png_set_tRNS_to_alpha (png);
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb (png);
if (!(color_type & PNG_COLOR_MASK_ALPHA) && !has_trns)
png_set_add_alpha (png, 0xff, PNG_FILLER_AFTER);
if (interlace_type != PNG_INTERLACE_NONE)
png_set_interlace_handling (png);
png_read_update_info (png, info);
if (png_get_bit_depth (png, info) != 8 || png_get_channels (png, info) != 4)
{
png_destroy_read_struct (&png, &info, nullptr);
return false;
}
png_size_t rowbytes = png_get_rowbytes (png, info);
if (rowbytes < (png_size_t) w * 4u)
{
png_destroy_read_struct (&png, &info, nullptr);
return false;
}
size_t rgba_size = (size_t) rowbytes * (size_t) h;
if (h && rgba_size / (size_t) h != (size_t) rowbytes)
{
png_destroy_read_struct (&png, &info, nullptr);
return false;
}
if (!rgba.resize (rgba_size))
{
png_destroy_read_struct (&png, &info, nullptr);
return false;
}
if (!rows.resize ((unsigned) h))
{
png_destroy_read_struct (&png, &info, nullptr);
return false;
}
for (unsigned y = 0; y < (unsigned) h; y++)
rows[y] = (png_bytep) (rgba.arrayZ + (size_t) y * (size_t) rowbytes);
png_read_image (png, rows.arrayZ);
png_read_end (png, nullptr);
png_destroy_read_struct (&png, &info, nullptr);
hb_raster_image_t decoded;
hb_raster_extents_t decoded_extents = {0, 0, (unsigned) w, (unsigned) h, 0};
if (!decoded.configure (HB_RASTER_FORMAT_BGRA32, decoded_extents))
return false;
for (unsigned y = 0; y < (unsigned) h; y++)
{
hb_packed_t<uint32_t> *dst = (hb_packed_t<uint32_t> *) (decoded.buffer.arrayZ + (size_t) ((unsigned) h - 1 - y) * decoded.extents.stride);
const uint8_t *src = rgba.arrayZ + (size_t) y * (size_t) rowbytes;
for (unsigned x = 0; x < (unsigned) w; x++)
{
uint8_t r = src[4 * x + 0];
uint8_t g = src[4 * x + 1];
uint8_t b = src[4 * x + 2];
uint8_t a = src[4 * x + 3];
dst[x] = hb_packed_t<uint32_t> ((uint32_t) hb_raster_div255 (b * a)
| ((uint32_t) hb_raster_div255 (g * a) << 8)
| ((uint32_t) hb_raster_div255 (r * a) << 16)
| ((uint32_t) a << 24));
}
}
hb_swap (buffer, decoded.buffer);
hb_swap (this->extents, decoded.extents);
hb_swap (format, decoded.format);
return true;
#endif
}
hb_blob_t *
hb_raster_image_t::serialize_to_png_or_fail () const
{
#ifndef HAVE_PNG
return nullptr;
#else
if (format != HB_RASTER_FORMAT_BGRA32 || !extents.width || !extents.height)
return nullptr;
png_structp png = png_create_write_struct (PNG_LIBPNG_VER_STRING, nullptr,
hb_raster_png_error,
hb_raster_png_warning);
if (!png)
return nullptr;
png_infop info = png_create_info_struct (png);
if (!info)
{
png_destroy_write_struct (&png, nullptr);
return nullptr;
}
hb_raster_png_write_blob_t writer;
hb_vector_t<uint8_t> rgba;
hb_vector_t<png_bytep> rows;
if (setjmp (png_jmpbuf (png)))
{
png_destroy_write_struct (&png, &info);
writer.data.fini ();
rgba.fini ();
rows.fini ();
return nullptr;
}
png_set_write_fn (png, &writer, hb_raster_png_write_blob, hb_raster_png_flush_blob);
png_set_IHDR (png, info,
extents.width, extents.height,
8, PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_write_info (png, info);
size_t rowbytes = (size_t) extents.width * 4u;
size_t rgba_size = rowbytes * (size_t) extents.height;
if (extents.height && rgba_size / (size_t) extents.height != rowbytes)
{
png_destroy_write_struct (&png, &info);
return nullptr;
}
if (!rgba.resize (rgba_size))
{
png_destroy_write_struct (&png, &info);
return nullptr;
}
if (!rows.resize (extents.height))
{
png_destroy_write_struct (&png, &info);
return nullptr;
}
for (unsigned y = 0; y < extents.height; y++)
{
uint8_t *dst = rgba.arrayZ + (size_t) y * rowbytes;
const uint8_t *src = buffer.arrayZ + (size_t) (extents.height - 1 - y) * extents.stride;
for (unsigned x = 0; x < extents.width; x++)
{
uint32_t px;
hb_memcpy (&px, src + x * 4, 4);
uint8_t b = (uint8_t) (px & 0xFF);
uint8_t g = (uint8_t) ((px >> 8) & 0xFF);
uint8_t r = (uint8_t) ((px >> 16) & 0xFF);
uint8_t a = (uint8_t) (px >> 24);
dst[4 * x + 3] = a;
if (a)
{
dst[4 * x + 0] = (uint8_t) hb_min (255u, ((unsigned) r * 255u + a / 2u) / a);
dst[4 * x + 1] = (uint8_t) hb_min (255u, ((unsigned) g * 255u + a / 2u) / a);
dst[4 * x + 2] = (uint8_t) hb_min (255u, ((unsigned) b * 255u + a / 2u) / a);
}
else
dst[4 * x + 0] = dst[4 * x + 1] = dst[4 * x + 2] = 0;
}
rows[y] = (png_bytep) dst;
}
png_write_image (png, rows.arrayZ);
png_write_end (png, info);
png_destroy_write_struct (&png, &info);
unsigned length = 0;
char *data = writer.data.steal (&length);
if (!data && length)
return nullptr;
hb_blob_t *blob = hb_blob_create_or_fail (data, length,
HB_MEMORY_MODE_WRITABLE,
data, hb_free);
if (!blob)
hb_free (data);
return blob;
#endif
}
void
hb_raster_image_t::clear ()
{
size_t buf_size = (size_t) extents.stride * extents.height;
hb_memset (buffer.arrayZ, 0, buf_size);
}
const uint8_t *
hb_raster_image_t::get_buffer () const
{
return buffer.arrayZ;
}
void
hb_raster_image_t::composite_from (const hb_raster_image_t *src,
hb_paint_composite_mode_t mode)
{
unsigned w = extents.width;
unsigned h = extents.height;
unsigned stride = extents.stride;
for (unsigned y = 0; y < h; y++)
{
hb_packed_t<uint32_t> *dp = (hb_packed_t<uint32_t> *) (buffer.arrayZ + y * stride);
const hb_packed_t<uint32_t> *sp = (const hb_packed_t<uint32_t> *) (src->buffer.arrayZ + y * stride);
for (unsigned x = 0; x < w; x++)
dp[x] = hb_packed_t<uint32_t> (composite_pixel ((uint32_t) sp[x], (uint32_t) dp[x], mode));
}
}
/* Composite src image onto dst image.
* Both images must have the same extents and BGRA32 format. */
void
hb_raster_image_composite (hb_raster_image_t *dst,
const hb_raster_image_t *src,
hb_paint_composite_mode_t mode)
{
dst->composite_from (src, mode);
}
/**
* hb_raster_image_create_or_fail:
*
* Creates a new raster image object.
*
* Return value: (transfer full):
* A newly allocated #hb_raster_image_t with a reference count of 1,
* or `NULL` on allocation failure.
*
* The returned image can be released with hb_raster_image_destroy(), or
* transferred for reuse with hb_raster_draw_recycle_image() or
* hb_raster_paint_recycle_image().
*
* Since: 13.0.0
**/
hb_raster_image_t *
hb_raster_image_create_or_fail (void)
{
return hb_object_create<hb_raster_image_t> ();
}
/**
* hb_raster_image_reference: (skip)
* @image: a raster image
*
* Increases the reference count on @image by one.
*
* This prevents @image from being destroyed until a matching
* call to hb_raster_image_destroy() is made.
*
* Return value: (transfer full):
* The referenced #hb_raster_image_t.
*
* Since: 13.0.0
**/
hb_raster_image_t *
hb_raster_image_reference (hb_raster_image_t *image)
{
return hb_object_reference (image);
}
/**
* hb_raster_image_destroy: (skip)
* @image: a raster image
*
* Decreases the reference count on @image by one. When the
* reference count reaches zero, the image and its pixel buffer
* are freed.
*
* Since: 13.0.0
**/
void
hb_raster_image_destroy (hb_raster_image_t *image)
{
if (!hb_object_destroy (image)) return;
hb_free (image);
}
/**
* hb_raster_image_set_user_data: (skip)
* @image: a raster image
* @key: the user-data key
* @data: a pointer to the user data
* @destroy: (nullable): a callback to call when @data is not needed anymore
* @replace: whether to replace an existing data with the same key
*
* Attaches a user-data key/data pair to the specified raster image.
*
* Return value: `true` if success, `false` otherwise
*
* Since: 13.0.0
**/
hb_bool_t
hb_raster_image_set_user_data (hb_raster_image_t *image,
hb_user_data_key_t *key,
void *data,
hb_destroy_func_t destroy,
hb_bool_t replace)
{
return hb_object_set_user_data (image, key, data, destroy, replace);
}
/**
* hb_raster_image_get_user_data: (skip)
* @image: a raster image
* @key: the user-data key
*
* Fetches the user-data associated with the specified key,
* attached to the specified raster image.
*
* Return value: (transfer none):
* A pointer to the user data
*
* Since: 13.0.0
**/
void *
hb_raster_image_get_user_data (hb_raster_image_t *image,
hb_user_data_key_t *key)
{
return hb_object_get_user_data (image, key);
}
/**
* hb_raster_image_configure:
* @image: a raster image
* @format: the pixel format
* @extents: (nullable): desired image extents
*
* Configures @image format and extents together, resizing backing storage
* at most once. This function does not clear pixel contents.
*
* Passing `NULL` for @extents clears extents and releases the backing
* allocation.
*
* Return value: `true` if configuration succeeds, `false` on allocation
* failure
*
* Since: 13.0.0
**/
hb_bool_t
hb_raster_image_configure (hb_raster_image_t *image,
hb_raster_format_t format,
const hb_raster_extents_t *extents)
{
if (unlikely (!extents))
{
image->extents = {};
image->buffer.resize_exact (0);
return true;
}
return image->configure (format, *extents);
}
/**
* hb_raster_image_clear:
* @image: a raster image
*
* Clears @image pixels to zero while keeping current extents and format.
*
* Since: 13.0.0
**/
void
hb_raster_image_clear (hb_raster_image_t *image)
{
image->clear ();
}
/**
* hb_raster_image_get_buffer:
* @image: a raster image
*
* Fetches the raw pixel buffer of @image. The buffer layout is
* described by the extents obtained from hb_raster_image_get_extents()
* and the format from hb_raster_image_get_format(). Rows are stored
* bottom-to-top.
*
* Return value: (transfer none) (array):
* The pixel buffer, or `NULL`
*
* Since: 13.0.0
**/
const uint8_t *
hb_raster_image_get_buffer (hb_raster_image_t *image)
{
return image->get_buffer ();
}
/**
* hb_raster_image_get_extents:
* @image: a raster image
* @extents: (out) (nullable): the image extents
*
* Fetches the pixel-buffer extents of @image.
*
* Since: 13.0.0
**/
void
hb_raster_image_get_extents (hb_raster_image_t *image,
hb_raster_extents_t *extents)
{
if (extents)
*extents = image->extents;
}
/**
* hb_raster_image_get_format:
* @image: a raster image
*
* Fetches the pixel format of @image.
*
* Return value:
* The #hb_raster_format_t of the image
*
* Since: 13.0.0
**/
hb_raster_format_t
hb_raster_image_get_format (hb_raster_image_t *image)
{
return image->format;
}
/**
* hb_raster_image_deserialize_from_png_or_fail:
* @image: a raster image
* @png: PNG data
*
* Replaces @image contents by deserializing a PNG blob into a
* #HB_RASTER_FORMAT_BGRA32 raster image.
*
* On success, @image extents are reset to pixel extents with origin
* `(0, 0)`. Rows in the resulting image buffer are stored bottom-to-top.
* On failure, @image is left unchanged.
*
* Return value: `true` if deserialization succeeded, `false` otherwise
*
* Since: 13.1.0
**/
hb_bool_t
hb_raster_image_deserialize_from_png_or_fail (hb_raster_image_t *image,
hb_blob_t *png)
{
return image->deserialize_from_png (png);
}
/**
* hb_raster_image_serialize_to_png_or_fail:
* @image: a raster image
*
* Serializes @image to a PNG blob.
*
* Currently only #HB_RASTER_FORMAT_BGRA32 images are supported.
*
* Return value: (transfer full):
* A newly allocated PNG #hb_blob_t, or `NULL` on failure
*
* Since: 13.1.0
**/
hb_blob_t *
hb_raster_image_serialize_to_png_or_fail (hb_raster_image_t *image)
{
return image->serialize_to_png_or_fail ();
}
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_IMAGE_HH
#define HB_RASTER_IMAGE_HH
#include "hb.hh"
#include "hb-raster-utils.hh"
#include "hb-raster.h"
#include "hb-object.hh"
#include "hb-vector.hh"
/* hb_raster_image_t — pixel artifact */
struct hb_raster_image_t
{
hb_object_header_t header;
hb_vector_t<uint8_t> buffer;
hb_raster_extents_t extents = {};
hb_raster_format_t format = HB_RASTER_FORMAT_A8;
HB_INTERNAL static unsigned bytes_per_pixel (hb_raster_format_t format);
HB_INTERNAL bool configure (hb_raster_format_t format, hb_raster_extents_t extents);
HB_INTERNAL bool deserialize_from_png (hb_blob_t *png);
HB_INTERNAL hb_blob_t *serialize_to_png_or_fail () const;
HB_INTERNAL void clear ();
HB_INTERNAL const uint8_t *get_buffer () const;
HB_INTERNAL void composite_from (const hb_raster_image_t *src,
hb_paint_composite_mode_t mode);
};
/* Composite src image onto dst. */
HB_INTERNAL void
hb_raster_image_composite (hb_raster_image_t *dst,
const hb_raster_image_t *src,
hb_paint_composite_mode_t mode);
#endif /* HB_RASTER_IMAGE_HH */
File diff suppressed because it is too large Load Diff
+205
View File
@@ -0,0 +1,205 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_PAINT_HH
#define HB_RASTER_PAINT_HH
#include "hb.hh"
#include "hb-raster-image.hh"
#include "hb-geometry.hh"
/* hb_raster_clip_t — alpha mask for clipping */
struct hb_raster_clip_t
{
hb_vector_t<uint8_t> alpha; /* A8 mask, same extents as root surface */
unsigned width = 0;
unsigned height = 0;
unsigned stride = 0;
/* Fast path: simple rectangle (no alpha buffer needed) */
bool is_rect = true;
int rect_x0 = 0, rect_y0 = 0;
int rect_x1 = 0, rect_y1 = 0;
/* Bounding box of non-zero alpha region (valid for both rect and mask) */
unsigned min_x = 0, min_y = 0;
unsigned max_x = 0, max_y = 0;
void init_full (unsigned w, unsigned h)
{
width = w;
height = h;
stride = (w + 3u) & ~3u;
is_rect = true;
rect_x0 = 0;
rect_y0 = 0;
rect_x1 = (int) w;
rect_y1 = (int) h;
min_x = 0;
min_y = 0;
max_x = w;
max_y = h;
}
void update_bounds_from_rect ()
{
min_x = (unsigned) hb_max (rect_x0, 0);
min_y = (unsigned) hb_max (rect_y0, 0);
max_x = (unsigned) hb_max (hb_min (rect_x1, (int) width), 0);
max_y = (unsigned) hb_max (hb_min (rect_y1, (int) height), 0);
}
uint8_t get_alpha (unsigned x, unsigned y) const
{
if (is_rect)
return ((int) x >= rect_x0 && (int) x < rect_x1 &&
(int) y >= rect_y0 && (int) y < rect_y1) ? 255 : 0;
if (x >= width || y >= height) return 0;
return alpha[y * stride + x];
}
};
/* hb_raster_paint_t — color glyph paint context */
struct hb_raster_paint_t
{
hb_object_header_t header;
/* Configuration */
hb_transform_t<> base_transform = {1, 0, 0, 1, 0, 0};
float x_scale_factor = 1.f;
float y_scale_factor = 1.f;
hb_raster_extents_t fixed_extents = {};
bool has_extents = false;
hb_color_t foreground = HB_COLOR (0, 0, 0, 255);
hb_map_t *custom_palette = nullptr;
/* SVG rendering state */
hb_codepoint_t svg_glyph = 0;
hb_font_t *svg_font = nullptr;
unsigned svg_palette = 0;
/* Stacks */
hb_vector_t<hb_transform_t<>> transform_stack;
hb_vector_t<hb_raster_clip_t> clip_stack;
hb_vector_t<hb_raster_clip_t> clip_cache;
hb_vector_t<hb_raster_image_t *> surface_stack;
/* Cached surface pool (freelist for reuse across push/pop group) */
hb_vector_t<hb_raster_image_t *> surface_cache;
hb_vector_t<hb_color_stop_t> scratch_color_stops;
/* Internal rasterizer for clip-to-glyph */
hb_raster_draw_t *clip_rdr = nullptr;
/* Helpers */
hb_raster_image_t *acquire_surface ()
{
hb_raster_image_t *img;
if (surface_cache.length)
img = surface_cache.pop ();
else
{
img = hb_raster_image_create_or_fail ();
if (unlikely (!img)) return nullptr;
}
if (unlikely (!img->configure (HB_RASTER_FORMAT_BGRA32, fixed_extents)))
{
hb_raster_image_destroy (img);
return nullptr;
}
img->clear ();
return img;
}
void release_surface (hb_raster_image_t *img)
{
if (!surface_cache.push_or_fail (img))
hb_raster_image_destroy (img);
}
hb_raster_clip_t acquire_clip (unsigned w, unsigned h)
{
hb_raster_clip_t clip;
if (clip_cache.length)
clip = clip_cache.pop ();
clip.width = w;
clip.height = h;
clip.stride = (w + 3u) & ~3u;
clip.is_rect = false;
return clip;
}
void release_clip (hb_raster_clip_t &&clip)
{
if (clip.alpha.arrayZ)
clip_cache.push (std::move (clip));
}
void release_all_clips ()
{
while (clip_stack.length)
release_clip (clip_stack.pop ());
}
hb_raster_image_t *current_surface ()
{
return surface_stack.length ? surface_stack.tail () : nullptr;
}
hb_raster_clip_t &current_clip ()
{
return clip_stack.tail ();
}
hb_transform_t<> &current_transform ()
{
return transform_stack.tail ();
}
void apply_scale_factor (hb_transform_t<> &t) const
{
t.xx /= x_scale_factor;
t.xy /= x_scale_factor;
t.x0 /= x_scale_factor;
t.yx /= y_scale_factor;
t.yy /= y_scale_factor;
t.y0 /= y_scale_factor;
}
hb_transform_t<> current_effective_transform ()
{
hb_transform_t<> t = current_transform ();
apply_scale_factor (t);
return t;
}
};
#endif /* HB_RASTER_PAINT_HH */
+501
View File
@@ -0,0 +1,501 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-base.hh"
#include "hb-raster-svg-parse.hh"
void
svg_parse_style_props (hb_svg_str_t style, hb_svg_style_props_t *out)
{
if (style.is_null ()) return;
const char *p = style.data;
const char *end = style.data + style.len;
while (p < end)
{
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ';'))
p++;
if (p >= end) break;
const char *name_start = p;
while (p < end && *p != ':' && *p != ';')
p++;
const char *name_end = p;
while (name_end > name_start &&
(name_end[-1] == ' ' || name_end[-1] == '\t' || name_end[-1] == '\n' || name_end[-1] == '\r'))
name_end--;
if (p >= end || *p != ':')
{
while (p < end && *p != ';') p++;
continue;
}
p++; /* skip ':' */
const char *value_start = p;
while (p < end && *p != ';')
p++;
const char *value_end = p;
while (value_start < value_end &&
(*value_start == ' ' || *value_start == '\t' || *value_start == '\n' || *value_start == '\r'))
value_start++;
while (value_end > value_start &&
(value_end[-1] == ' ' || value_end[-1] == '\t' || value_end[-1] == '\n' || value_end[-1] == '\r'))
value_end--;
hb_svg_str_t prop_name = {name_start, (unsigned) (name_end - name_start)};
hb_svg_str_t prop_value = {value_start, (unsigned) (value_end - value_start)};
if (prop_name.len)
{
switch (hb_svg_ascii_lower (prop_name.data[0]))
{
case 'c':
if (prop_name.len == 9 && prop_name.eq_ascii_ci ("clip-path")) out->clip_path = prop_value;
else if (prop_name.len == 5 && prop_name.eq_ascii_ci ("color")) out->color = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("cx")) out->cx = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("cy")) out->cy = prop_value;
break;
case 'd':
if (prop_name.len == 7 && prop_name.eq_ascii_ci ("display")) out->display = prop_value;
else if (prop_name.len == 1 && prop_name.eq_ascii_ci ("d")) out->d = prop_value;
break;
case 'f':
if (prop_name.len == 4 && prop_name.eq_ascii_ci ("fill")) out->fill = prop_value;
else if (prop_name.len == 12 && prop_name.eq_ascii_ci ("fill-opacity")) out->fill_opacity = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fx")) out->fx = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fy")) out->fy = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fr")) out->fr = prop_value;
break;
case 'g':
if ((prop_name.len == 13 && prop_name.eq_ascii_ci ("gradientunits")) ||
(prop_name.len == 14 && prop_name.eq_ascii_ci ("gradient-units"))) out->gradient_units = prop_value;
else if ((prop_name.len == 17 && prop_name.eq_ascii_ci ("gradienttransform")) ||
(prop_name.len == 18 && prop_name.eq_ascii_ci ("gradient-transform"))) out->gradient_transform = prop_value;
break;
case 'h':
if (prop_name.len == 6 && prop_name.eq_ascii_ci ("height")) out->height = prop_value;
break;
case 'o':
if (prop_name.len == 7 && prop_name.eq_ascii_ci ("opacity")) out->opacity = prop_value;
else if (prop_name.len == 6 && prop_name.eq_ascii_ci ("offset")) out->offset = prop_value;
break;
case 'p':
if (prop_name.len == 6 && prop_name.eq_ascii_ci ("points")) out->points = prop_value;
break;
case 'r':
if (prop_name.len == 1 && prop_name.eq_ascii_ci ("r")) out->r = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("rx")) out->rx = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("ry")) out->ry = prop_value;
break;
case 's':
if (prop_name.len == 10 && prop_name.eq_ascii_ci ("stop-color")) out->stop_color = prop_value;
else if (prop_name.len == 12 && prop_name.eq_ascii_ci ("stop-opacity")) out->stop_opacity = prop_value;
else if ((prop_name.len == 12 && prop_name.eq_ascii_ci ("spreadmethod")) ||
(prop_name.len == 13 && prop_name.eq_ascii_ci ("spread-method"))) out->spread_method = prop_value;
break;
case 't':
if (prop_name.len == 9 && prop_name.eq_ascii_ci ("transform")) out->transform = prop_value;
break;
case 'v':
if (prop_name.len == 10 && prop_name.eq_ascii_ci ("visibility")) out->visibility = prop_value;
break;
case 'w':
if (prop_name.len == 5 && prop_name.eq_ascii_ci ("width")) out->width = prop_value;
break;
case 'x':
if (prop_name.len == 1 && prop_name.eq_ascii_ci ("x")) out->x = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("x1")) out->x1 = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("x2")) out->x2 = prop_value;
break;
case 'y':
if (prop_name.len == 1 && prop_name.eq_ascii_ci ("y")) out->y = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("y1")) out->y1 = prop_value;
else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("y2")) out->y2 = prop_value;
break;
default:
break;
}
}
if (p < end && *p == ';') p++;
}
}
float
svg_parse_number_or_percent (hb_svg_str_t s, bool *is_percent)
{
if (is_percent) *is_percent = false;
s = s.trim ();
if (!s.len) return 0.f;
if (s.data[s.len - 1] == '%')
{
if (is_percent) *is_percent = true;
hb_svg_str_t n = {s.data, s.len - 1};
return n.to_float () / 100.f;
}
return s.to_float ();
}
hb_svg_str_t
hb_raster_svg_find_href_attr (const hb_svg_xml_parser_t &parser)
{
hb_svg_str_t href = parser.find_attr ("href");
if (href.is_null ())
href = parser.find_attr ("xlink:href");
return href;
}
bool
hb_raster_svg_parse_id_ref (hb_svg_str_t s,
hb_svg_str_t *out_id,
hb_svg_str_t *out_tail)
{
if (out_id) *out_id = {};
if (out_tail) *out_tail = {};
s = s.trim ();
if (s.len && s.data[0] == '#')
{
hb_svg_str_t id = {s.data + 1, s.len - 1};
id = id.trim ();
if (!id.len)
return false;
if (out_id) *out_id = id;
return true;
}
if (!s.starts_with_ascii_ci ("url("))
return false;
const char *p = s.data + 4;
const char *end = s.data + s.len;
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
const char *q = p;
char quote = 0;
while (q < end)
{
char c = *q;
if (quote)
{
if (c == quote) quote = 0;
}
else
{
if (c == '"' || c == '\'') quote = c;
else if (c == ')') break;
}
q++;
}
if (q >= end || *q != ')')
return false;
hb_svg_str_t id = {(const char *) p, (unsigned) (q - p)};
id = id.trim ();
if (id.len >= 2 &&
((id.data[0] == '\'' && id.data[id.len - 1] == '\'') ||
(id.data[0] == '"' && id.data[id.len - 1] == '"')))
{
id.data++;
id.len -= 2;
}
id = id.trim ();
if (id.len && id.data[0] == '#')
{
id.data++;
id.len--;
}
id = id.trim ();
if (!id.len)
return false;
if (out_id) *out_id = id;
if (out_tail)
{
const char *tail = q + 1;
while (tail < end && (*tail == ' ' || *tail == '\t' || *tail == '\n' || *tail == '\r')) tail++;
*out_tail = {(const char *) tail, (unsigned) (end - tail)};
}
return true;
}
bool
hb_raster_svg_parse_local_id_ref (hb_svg_str_t s,
hb_svg_str_t *out_id,
hb_svg_str_t *out_tail)
{
if (out_id) *out_id = {};
if (out_tail) *out_tail = {};
s = s.trim ();
if (s.len && s.data[0] == '#')
{
hb_svg_str_t id = {s.data + 1, s.len - 1};
id = id.trim ();
if (!id.len)
return false;
if (out_id) *out_id = id;
return true;
}
if (!s.starts_with_ascii_ci ("url("))
return false;
hb_svg_str_t id;
if (!hb_raster_svg_parse_id_ref (s, &id, out_tail))
return false;
const char *p = s.data + 4;
const char *end = s.data + s.len;
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
if (p < end && (*p == '\'' || *p == '"'))
{
p++;
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
if (p >= end || *p != '#')
return false;
}
else if (p >= end || *p != '#')
return false;
if (out_id) *out_id = id;
return true;
}
bool
hb_raster_svg_find_element_by_id (const char *doc_start,
unsigned doc_len,
const OT::SVG::accelerator_t *svg_accel,
const OT::SVG::svg_doc_cache_t *doc_cache,
hb_svg_str_t id,
const char **found)
{
*found = nullptr;
if (!doc_start || !doc_len || !id.len)
return false;
if (doc_cache && svg_accel)
{
unsigned start = 0, end = 0;
OT::SVG::svg_id_span_t key = {id.data, id.len};
if (svg_accel->doc_cache_find_id_span (doc_cache, key, &start, &end))
{
if (start < doc_len && end <= doc_len && start < end)
{
*found = doc_start + start;
return true;
}
}
}
hb_svg_xml_parser_t search (doc_start, doc_len);
while (true)
{
hb_svg_token_type_t tok = search.next ();
if (tok == SVG_TOKEN_EOF) break;
if (tok != SVG_TOKEN_OPEN_TAG && tok != SVG_TOKEN_SELF_CLOSE_TAG) continue;
hb_svg_str_t attr_id = search.find_attr ("id");
if (attr_id.len == id.len && 0 == memcmp (attr_id.data, id.data, id.len))
{
*found = search.tag_start;
return true;
}
}
return false;
}
bool
hb_raster_svg_parse_viewbox (hb_svg_str_t viewbox_str,
float *x,
float *y,
float *w,
float *h)
{
if (!viewbox_str.len)
return false;
hb_svg_float_parser_t vb_fp (viewbox_str);
float vb_x = vb_fp.next_float ();
float vb_y = vb_fp.next_float ();
float vb_w = vb_fp.next_float ();
float vb_h = vb_fp.next_float ();
if (vb_w <= 0.f || vb_h <= 0.f)
return false;
if (x) *x = vb_x;
if (y) *y = vb_y;
if (w) *w = vb_w;
if (h) *h = vb_h;
return true;
}
static inline float
svg_align_offset (hb_svg_str_t align,
float leftover,
char axis)
{
if (leftover <= 0.f) return 0.f;
if ((axis == 'x' && align.starts_with_ascii_ci ("xMin")) ||
(axis == 'y' && (align.eq_ascii_ci ("xMinYMin") ||
align.eq_ascii_ci ("xMidYMin") ||
align.eq_ascii_ci ("xMaxYMin"))))
return 0.f;
if ((axis == 'x' && align.starts_with_ascii_ci ("xMax")) ||
(axis == 'y' && (align.eq_ascii_ci ("xMinYMax") ||
align.eq_ascii_ci ("xMidYMax") ||
align.eq_ascii_ci ("xMaxYMax"))))
return leftover;
return leftover * 0.5f;
}
bool
hb_raster_svg_compute_viewbox_transform (float viewport_w,
float viewport_h,
float vb_x,
float vb_y,
float vb_w,
float vb_h,
hb_svg_str_t preserve_aspect_ratio,
hb_svg_transform_t *out)
{
if (!(viewport_w > 0.f && viewport_h > 0.f && vb_w > 0.f && vb_h > 0.f))
return false;
hb_svg_str_t par = preserve_aspect_ratio.trim ();
if (par.starts_with_ascii_ci ("defer"))
{
const char *p = par.data + 5;
const char *end = par.data + par.len;
while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
par = hb_svg_str_t (p, (unsigned) (end - p));
}
if (!par.len)
par = hb_svg_str_t ("xMidYMid meet", 12);
bool is_none = false;
bool is_slice = false;
hb_svg_str_t align = par;
const char *p = par.data;
const char *end = par.data + par.len;
while (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') p++;
align = hb_svg_str_t (par.data, (unsigned) (p - par.data)).trim ();
if (par.starts_with_ascii_ci ("none"))
is_none = true;
else if (align.starts_with_ascii_ci ("x"))
{
const char *mode = p;
while (mode < end && (*mode == ' ' || *mode == '\t' || *mode == '\n' || *mode == '\r')) mode++;
if (mode < end && hb_svg_str_t (mode, (unsigned) (end - mode)).starts_with_ascii_ci ("slice"))
is_slice = true;
}
hb_svg_transform_t t;
if (is_none)
{
t.xx = viewport_w / vb_w;
t.yy = viewport_h / vb_h;
t.dx = -vb_x * t.xx;
t.dy = -vb_y * t.yy;
*out = t;
return true;
}
float sx = viewport_w / vb_w;
float sy = viewport_h / vb_h;
float s = is_slice ? hb_max (sx, sy) : hb_min (sx, sy);
float scaled_w = vb_w * s;
float scaled_h = vb_h * s;
float leftover_x = viewport_w - scaled_w;
float leftover_y = viewport_h - scaled_h;
if (!align.starts_with_ascii_ci ("x"))
align = hb_svg_str_t ("xMidYMid", 8);
t.xx = s;
t.yy = s;
t.dx = svg_align_offset (align, leftover_x, 'x') - vb_x * s;
t.dy = svg_align_offset (align, leftover_y, 'y') - vb_y * s;
*out = t;
return true;
}
bool
hb_raster_svg_compute_use_target_viewbox_transform (hb_svg_xml_parser_t &target_parser,
float use_w,
float use_h,
hb_svg_transform_t *out)
{
if (!(target_parser.tag_name.eq ("svg") || target_parser.tag_name.eq ("symbol")))
return false;
float viewport_w = use_w;
float viewport_h = use_h;
hb_svg_style_props_t target_style_props;
svg_parse_style_props (target_parser.find_attr ("style"), &target_style_props);
if (viewport_w <= 0.f)
viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (target_parser, target_style_props.width, "width"));
if (viewport_h <= 0.f)
viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (target_parser, target_style_props.height, "height"));
float vb_x = 0.f, vb_y = 0.f, vb_w = 0.f, vb_h = 0.f;
if (!hb_raster_svg_parse_viewbox (target_parser.find_attr ("viewBox"), &vb_x, &vb_y, &vb_w, &vb_h))
return false;
if (!(viewport_w > 0.f && viewport_h > 0.f))
{
viewport_w = vb_w;
viewport_h = vb_h;
}
return hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h, vb_x, vb_y, vb_w, vb_h,
target_parser.find_attr ("preserveAspectRatio"),
out);
}
void
hb_raster_svg_parse_use_geometry (hb_svg_xml_parser_t &parser,
float *x,
float *y,
float *w,
float *h)
{
hb_svg_style_props_t style_props;
svg_parse_style_props (parser.find_attr ("style"), &style_props);
if (x) *x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.x, "x"));
if (y) *y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.y, "y"));
if (w) *w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.width, "width"));
if (h) *h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.height, "height"));
}
float
hb_raster_svg_parse_non_percent_length (hb_svg_str_t s)
{
bool is_percent = false;
float v = svg_parse_number_or_percent (s, &is_percent);
return is_percent ? 0.f : v;
}
#endif /* !HB_NO_RASTER_SVG */
+228
View File
@@ -0,0 +1,228 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_BASE_HH
#define HB_RASTER_SVG_BASE_HH
#include "hb.hh"
#include "OT/Color/svg/svg.hh"
#include <math.h>
#include <string.h>
#include <stdlib.h>
static inline char
hb_svg_ascii_lower (char c)
{
if (c >= 'A' && c <= 'Z')
return c + ('a' - 'A');
return c;
}
struct hb_svg_str_t
{
const char *data;
unsigned len;
hb_svg_str_t () : data (nullptr), len (0) {}
hb_svg_str_t (const char *d, unsigned l) : data (d), len (l) {}
bool is_null () const { return !data; }
bool eq (const char *s) const
{
unsigned slen = (unsigned) strlen (s);
return len == slen && memcmp (data, s, len) == 0;
}
bool starts_with (const char *prefix) const
{
unsigned plen = (unsigned) strlen (prefix);
return len >= plen && memcmp (data, prefix, plen) == 0;
}
bool eq_ascii_ci (const char *lit) const
{
unsigned n = (unsigned) strlen (lit);
if (len != n) return false;
for (unsigned i = 0; i < n; i++)
if (hb_svg_ascii_lower (data[i]) != hb_svg_ascii_lower (lit[i]))
return false;
return true;
}
bool starts_with_ascii_ci (const char *lit) const
{
unsigned n = (unsigned) strlen (lit);
if (len < n) return false;
for (unsigned i = 0; i < n; i++)
if (hb_svg_ascii_lower (data[i]) != hb_svg_ascii_lower (lit[i]))
return false;
return true;
}
float to_float () const
{
if (!data || !len) return 0.f;
char buf[64];
unsigned n = hb_min (len, (unsigned) sizeof (buf) - 1);
memcpy (buf, data, n);
buf[n] = '\0';
float v = strtof (buf, nullptr);
return std::isfinite (v) ? v : 0.f;
}
hb_svg_str_t trim_left () const
{
const char *p = data;
unsigned l = len;
while (l && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
{
p++;
l--;
}
return {p, l};
}
hb_svg_str_t trim () const
{
hb_svg_str_t s = trim_left ();
while (s.len && (s.data[s.len - 1] == ' ' || s.data[s.len - 1] == '\t' ||
s.data[s.len - 1] == '\n' || s.data[s.len - 1] == '\r'))
s.len--;
return s;
}
};
struct hb_svg_style_props_t
{
hb_svg_str_t fill;
hb_svg_str_t fill_opacity;
hb_svg_str_t opacity;
hb_svg_str_t transform;
hb_svg_str_t clip_path;
hb_svg_str_t display;
hb_svg_str_t color;
hb_svg_str_t visibility;
hb_svg_str_t offset;
hb_svg_str_t stop_color;
hb_svg_str_t stop_opacity;
hb_svg_str_t spread_method;
hb_svg_str_t gradient_units;
hb_svg_str_t gradient_transform;
hb_svg_str_t x;
hb_svg_str_t y;
hb_svg_str_t width;
hb_svg_str_t height;
hb_svg_str_t cx;
hb_svg_str_t cy;
hb_svg_str_t r;
hb_svg_str_t fx;
hb_svg_str_t fy;
hb_svg_str_t fr;
hb_svg_str_t rx;
hb_svg_str_t ry;
hb_svg_str_t x1;
hb_svg_str_t y1;
hb_svg_str_t x2;
hb_svg_str_t y2;
hb_svg_str_t points;
hb_svg_str_t d;
};
struct hb_svg_xml_parser_t;
struct hb_svg_transform_t;
HB_INTERNAL void svg_parse_style_props (hb_svg_str_t style, hb_svg_style_props_t *out);
HB_INTERNAL float svg_parse_number_or_percent (hb_svg_str_t s, bool *is_percent);
HB_INTERNAL hb_svg_str_t hb_raster_svg_find_href_attr (const hb_svg_xml_parser_t &parser);
HB_INTERNAL bool hb_raster_svg_parse_id_ref (hb_svg_str_t s,
hb_svg_str_t *out_id,
hb_svg_str_t *out_tail);
HB_INTERNAL bool hb_raster_svg_parse_local_id_ref (hb_svg_str_t s,
hb_svg_str_t *out_id,
hb_svg_str_t *out_tail);
HB_INTERNAL bool hb_raster_svg_find_element_by_id (const char *doc_start,
unsigned doc_len,
const OT::SVG::accelerator_t *svg_accel,
const OT::SVG::svg_doc_cache_t *doc_cache,
hb_svg_str_t id,
const char **found);
HB_INTERNAL bool hb_raster_svg_parse_viewbox (hb_svg_str_t viewbox_str,
float *x,
float *y,
float *w,
float *h);
HB_INTERNAL bool hb_raster_svg_compute_viewbox_transform (float viewport_w,
float viewport_h,
float vb_x,
float vb_y,
float vb_w,
float vb_h,
hb_svg_str_t preserve_aspect_ratio,
hb_svg_transform_t *out);
HB_INTERNAL bool hb_raster_svg_compute_use_target_viewbox_transform (hb_svg_xml_parser_t &target_parser,
float use_w,
float use_h,
hb_svg_transform_t *out);
HB_INTERNAL void hb_raster_svg_parse_use_geometry (hb_svg_xml_parser_t &parser,
float *x,
float *y,
float *w,
float *h);
HB_INTERNAL float hb_raster_svg_parse_non_percent_length (hb_svg_str_t s);
static inline float
svg_parse_float_clamped01 (hb_svg_str_t s)
{
return hb_clamp (s.to_float (), 0.f, 1.f);
}
static inline bool
svg_str_is_inherit (hb_svg_str_t s)
{
return s.trim ().eq_ascii_ci ("inherit");
}
static inline bool
svg_str_is_none (hb_svg_str_t s)
{
return s.trim ().eq_ascii_ci ("none");
}
static inline bool
hb_raster_svg_tag_is_container (hb_svg_str_t tag)
{
return tag.eq ("g") || tag.eq ("a") || tag.eq ("svg") || tag.eq ("symbol");
}
static inline bool
hb_raster_svg_tag_is_container_or_use (hb_svg_str_t tag)
{
return hb_raster_svg_tag_is_container (tag) || tag.eq ("use");
}
#endif /* HB_RASTER_SVG_BASE_HH */
+109
View File
@@ -0,0 +1,109 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-bbox.hh"
static void
svg_bbox_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
void *draw_data,
hb_draw_state_t *st HB_UNUSED,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
((hb_extents_t<> *) draw_data)->add_point (to_x, to_y);
}
static void
svg_bbox_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
void *draw_data,
hb_draw_state_t *st HB_UNUSED,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
((hb_extents_t<> *) draw_data)->add_point (to_x, to_y);
}
static void
svg_bbox_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
void *draw_data,
hb_draw_state_t *st HB_UNUSED,
float control_x, float control_y,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
hb_extents_t<> *ext = (hb_extents_t<> *) draw_data;
ext->add_point (control_x, control_y);
ext->add_point (to_x, to_y);
}
static void
svg_bbox_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
void *draw_data,
hb_draw_state_t *st HB_UNUSED,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
hb_extents_t<> *ext = (hb_extents_t<> *) draw_data;
ext->add_point (control1_x, control1_y);
ext->add_point (control2_x, control2_y);
ext->add_point (to_x, to_y);
}
static hb_draw_funcs_t *
svg_bbox_draw_funcs ()
{
static hb_draw_funcs_t *funcs = nullptr;
if (unlikely (!funcs))
{
funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (funcs, svg_bbox_move_to, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (funcs, svg_bbox_line_to, nullptr, nullptr);
hb_draw_funcs_set_quadratic_to_func (funcs, svg_bbox_quadratic_to, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (funcs, svg_bbox_cubic_to, nullptr, nullptr);
hb_draw_funcs_make_immutable (funcs);
}
return funcs;
}
bool
hb_raster_svg_compute_shape_bbox (const hb_svg_shape_emit_data_t &shape,
hb_extents_t<> *bbox)
{
hb_extents_t<> ext;
hb_svg_shape_emit_data_t tmp = shape;
hb_raster_svg_shape_path_emit (svg_bbox_draw_funcs (), &ext, &tmp);
if (ext.is_empty ()) return false;
*bbox = ext;
return true;
}
#endif /* !HB_NO_RASTER_SVG */
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_BBOX_HH
#define HB_RASTER_SVG_BBOX_HH
#include "hb.hh"
#include "hb-geometry.hh"
#include "hb-raster-svg-parse.hh"
HB_INTERNAL bool
hb_raster_svg_compute_shape_bbox (const hb_svg_shape_emit_data_t &shape,
hb_extents_t<> *bbox);
#endif /* HB_RASTER_SVG_BBOX_HH */
+528
View File
@@ -0,0 +1,528 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-clip.hh"
#include "hb-raster.h"
#include "hb-raster-paint.hh"
#include "hb-raster-svg.hh"
#include "hb-raster-svg-base.hh"
#include "hb-decycler.hh"
#include <math.h>
static inline bool
svg_transform_is_identity (const hb_svg_transform_t &t)
{
return t.xx == 1.f && t.yx == 0.f &&
t.xy == 0.f && t.yy == 1.f &&
t.dx == 0.f && t.dy == 0.f;
}
static bool
svg_parse_element_transform (hb_svg_xml_parser_t &parser,
hb_svg_transform_t *out)
{
hb_svg_style_props_t style_props;
svg_parse_style_props (parser.find_attr ("style"), &style_props);
hb_svg_str_t transform = svg_pick_attr_or_style (parser, style_props.transform, "transform");
if (!transform.len)
return false;
hb_raster_svg_parse_transform (transform, out);
return true;
}
struct hb_svg_clip_collect_context_t
{
hb_svg_defs_t *defs;
hb_svg_clip_path_def_t *clip;
const char *doc_start;
unsigned doc_len;
const OT::SVG::accelerator_t *svg_accel;
const OT::SVG::svg_doc_cache_t *doc_cache;
hb_decycler_t *use_decycler;
bool *had_alloc_failure;
};
static inline void
svg_clip_append_shape (hb_svg_clip_collect_context_t *ctx,
const hb_svg_shape_emit_data_t &shape,
const hb_svg_transform_t &transform)
{
hb_svg_clip_shape_t clip_shape;
clip_shape.shape = shape;
if (!svg_transform_is_identity (transform))
{
clip_shape.has_transform = true;
clip_shape.transform = transform;
}
if (likely (ctx->defs->clip_shapes.push_or_fail (clip_shape)))
ctx->clip->shape_count++;
else if (ctx->had_alloc_failure)
*ctx->had_alloc_failure = true;
}
static inline bool
svg_resolve_element_visibility (hb_svg_xml_parser_t &parser,
bool parent_visible)
{
hb_svg_style_props_t style_props;
svg_parse_style_props (parser.find_attr ("style"), &style_props);
hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display");
hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility");
if (display_str.trim ().eq_ascii_ci ("none"))
return false;
hb_svg_str_t vis_trim = visibility_str.trim ();
if (!vis_trim.len || vis_trim.eq_ascii_ci ("inherit"))
return parent_visible;
if (vis_trim.eq_ascii_ci ("hidden") ||
vis_trim.eq_ascii_ci ("collapse"))
return false;
if (vis_trim.eq_ascii_ci ("visible"))
return true;
return parent_visible;
}
static void
svg_clip_collect_ref_element (hb_svg_clip_collect_context_t *ctx,
hb_svg_xml_parser_t &parser,
const hb_svg_transform_t &base_transform,
unsigned depth,
bool suppress_viewbox_once = false,
bool parent_visible = true,
bool allow_symbol_once = false);
static void
svg_clip_collect_use_target (hb_svg_clip_collect_context_t *ctx,
hb_svg_xml_parser_t &use_parser,
const hb_svg_transform_t &base_transform,
unsigned depth)
{
const unsigned SVG_MAX_CLIP_USE_DEPTH = 64;
if (depth >= SVG_MAX_CLIP_USE_DEPTH)
return;
hb_svg_str_t href = hb_raster_svg_find_href_attr (use_parser);
hb_svg_str_t ref_id;
if (!hb_raster_svg_parse_local_id_ref (href, &ref_id, nullptr))
return;
const char *found = nullptr;
if (!hb_raster_svg_find_element_by_id (ctx->doc_start, ctx->doc_len,
ctx->svg_accel, ctx->doc_cache,
ref_id, &found))
return;
hb_decycler_node_t node (*ctx->use_decycler);
if (unlikely (!node.visit ((uintptr_t) found)))
return;
hb_svg_transform_t effective = base_transform;
float use_x = 0.f, use_y = 0.f, use_w = 0.f, use_h = 0.f;
hb_raster_svg_parse_use_geometry (use_parser, &use_x, &use_y, &use_w, &use_h);
if (use_x != 0.f || use_y != 0.f)
{
hb_svg_transform_t tr;
tr.dx = use_x;
tr.dy = use_y;
effective.multiply (tr);
}
unsigned remaining = ctx->doc_len - (unsigned) (found - ctx->doc_start);
hb_svg_xml_parser_t ref_parser (found, remaining);
hb_svg_token_type_t rt = ref_parser.next ();
if (rt != SVG_TOKEN_OPEN_TAG && rt != SVG_TOKEN_SELF_CLOSE_TAG)
return;
bool viewport_mapped = false;
hb_svg_transform_t vb_t;
if (hb_raster_svg_compute_use_target_viewbox_transform (ref_parser, use_w, use_h, &vb_t))
{
effective.multiply (vb_t);
viewport_mapped = true;
}
bool allow_symbol = ref_parser.tag_name.eq ("symbol");
svg_clip_collect_ref_element (ctx, ref_parser, effective, depth + 1,
viewport_mapped, true, allow_symbol);
}
static void
svg_clip_collect_ref_element (hb_svg_clip_collect_context_t *ctx,
hb_svg_xml_parser_t &parser,
const hb_svg_transform_t &base_transform,
unsigned depth,
bool suppress_viewbox_once,
bool parent_visible,
bool allow_symbol_once)
{
const unsigned SVG_MAX_CLIP_REF_DEPTH = 64;
if (depth >= SVG_MAX_CLIP_REF_DEPTH)
{
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
bool is_visible = svg_resolve_element_visibility (parser, parent_visible);
if (!is_visible)
{
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
/* Definitions are not directly renderable clip geometry. */
if (parser.tag_name.eq ("defs"))
{
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
if (parser.tag_name.eq ("symbol") && !allow_symbol_once)
{
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
if (parser.tag_name.eq ("symbol"))
allow_symbol_once = false;
hb_svg_transform_t effective = base_transform;
hb_svg_style_props_t geom_style_props;
svg_parse_style_props (parser.find_attr ("style"), &geom_style_props);
hb_svg_transform_t local_t;
if (svg_parse_element_transform (parser, &local_t))
effective.multiply (local_t);
if (parser.tag_name.eq ("svg"))
{
float svg_x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.x, "x"));
float svg_y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.y, "y"));
if (svg_x != 0.f || svg_y != 0.f)
{
hb_svg_transform_t tr;
tr.dx = svg_x;
tr.dy = svg_y;
effective.multiply (tr);
}
if (!suppress_viewbox_once)
{
float vb_x = 0.f, vb_y = 0.f, vb_w = 0.f, vb_h = 0.f;
if (hb_raster_svg_parse_viewbox (parser.find_attr ("viewBox"),
&vb_x, &vb_y, &vb_w, &vb_h))
{
float viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.width, "width"));
float viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.height, "height"));
if (!(viewport_w > 0.f && viewport_h > 0.f))
{
viewport_w = vb_w;
viewport_h = vb_h;
}
hb_svg_transform_t vb_t;
if (hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h,
vb_x, vb_y, vb_w, vb_h,
parser.find_attr ("preserveAspectRatio"),
&vb_t))
effective.multiply (vb_t);
}
suppress_viewbox_once = false;
}
}
hb_svg_shape_emit_data_t shape;
if (hb_raster_svg_parse_shape_tag (parser, &shape))
{
svg_clip_append_shape (ctx, shape, effective);
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
if (parser.tag_name.eq ("use"))
{
svg_clip_collect_use_target (ctx, parser, effective, depth + 1);
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
bool is_container = hb_raster_svg_tag_is_container (parser.tag_name);
if (!is_container || parser.self_closing)
{
if (!parser.self_closing)
svg_skip_subtree (parser);
return;
}
int inner_depth = 1;
while (inner_depth > 0)
{
hb_svg_token_type_t tok = parser.next ();
if (tok == SVG_TOKEN_EOF) break;
if (tok == SVG_TOKEN_CLOSE_TAG)
{
inner_depth--;
continue;
}
if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG)
svg_clip_collect_ref_element (ctx, parser, effective, depth + 1, false, is_visible, false);
}
}
void
hb_raster_svg_process_clip_path_def (hb_svg_defs_t *defs,
hb_svg_xml_parser_t &parser,
hb_svg_token_type_t tok,
const char *doc_start,
unsigned doc_len,
const OT::SVG::accelerator_t *svg_accel,
const OT::SVG::svg_doc_cache_t *doc_cache)
{
hb_svg_clip_path_def_t clip;
hb_svg_str_t id = parser.find_attr ("id");
hb_svg_str_t units = parser.find_attr ("clipPathUnits").trim ();
if (units.eq_ascii_ci ("objectBoundingBox"))
clip.units_user_space = false;
else if (units.eq_ascii_ci ("userSpaceOnUse"))
clip.units_user_space = true;
else
clip.units_user_space = true;
hb_svg_transform_t cp_t;
if (svg_parse_element_transform (parser, &cp_t))
{
clip.has_clip_transform = true;
clip.clip_transform = cp_t;
}
clip.first_shape = defs->clip_shapes.length;
clip.shape_count = 0;
if (tok == SVG_TOKEN_OPEN_TAG)
{
const unsigned SVG_MAX_CLIP_DEPTH = 64;
hb_svg_transform_t inherited[SVG_MAX_CLIP_DEPTH];
bool inherited_visibility[SVG_MAX_CLIP_DEPTH];
inherited[0] = hb_svg_transform_t ();
inherited[1] = hb_svg_transform_t ();
inherited_visibility[0] = true;
inherited_visibility[1] = true;
hb_decycler_t use_decycler;
int cdepth = 1;
bool had_alloc_failure = false;
hb_svg_clip_collect_context_t collect_ctx = {
defs, &clip,
doc_start, doc_len, svg_accel, doc_cache,
&use_decycler, &had_alloc_failure
};
while (cdepth > 0)
{
hb_svg_token_type_t ct = parser.next ();
if (ct == SVG_TOKEN_EOF) break;
if (ct == SVG_TOKEN_CLOSE_TAG) { cdepth--; continue; }
if (ct == SVG_TOKEN_OPEN_TAG || ct == SVG_TOKEN_SELF_CLOSE_TAG)
{
if (parser.tag_name.eq ("symbol"))
{
if (ct == SVG_TOKEN_OPEN_TAG)
{
int skip_depth = 1;
while (skip_depth > 0)
{
hb_svg_token_type_t st = parser.next ();
if (st == SVG_TOKEN_EOF) break;
if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--;
else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++;
}
}
continue;
}
if (parser.tag_name.eq ("defs"))
{
if (ct == SVG_TOKEN_OPEN_TAG)
{
int skip_depth = 1;
while (skip_depth > 0)
{
hb_svg_token_type_t st = parser.next ();
if (st == SVG_TOKEN_EOF) break;
if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--;
else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++;
}
}
continue;
}
bool parent_visible = (unsigned) cdepth < SVG_MAX_CLIP_DEPTH
? inherited_visibility[cdepth]
: true;
bool is_visible = svg_resolve_element_visibility (parser, parent_visible);
bool is_hidden = !is_visible;
if (is_hidden)
{
if (ct == SVG_TOKEN_OPEN_TAG)
{
int skip_depth = 1;
while (skip_depth > 0)
{
hb_svg_token_type_t st = parser.next ();
if (st == SVG_TOKEN_EOF) break;
if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--;
else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++;
}
}
continue;
}
hb_svg_transform_t effective = (unsigned) cdepth < SVG_MAX_CLIP_DEPTH
? inherited[cdepth]
: hb_svg_transform_t ();
hb_svg_transform_t local;
bool has_local_transform = svg_parse_element_transform (parser, &local);
if (has_local_transform)
{
effective.multiply (local);
}
if (parser.tag_name.eq ("use"))
{
svg_clip_collect_use_target (&collect_ctx, parser, effective, 0);
}
else
{
hb_svg_shape_emit_data_t shape;
if (hb_raster_svg_parse_shape_tag (parser, &shape))
svg_clip_append_shape (&collect_ctx, shape, effective);
}
if (ct == SVG_TOKEN_OPEN_TAG)
{
if ((unsigned) (cdepth + 1) < SVG_MAX_CLIP_DEPTH)
{
inherited[cdepth + 1] = effective;
inherited_visibility[cdepth + 1] = is_visible;
}
cdepth++;
}
}
}
if (had_alloc_failure)
id = {};
}
if (id.len)
(void) defs->add_clip_path (hb_bytes_t (id.data, id.len), clip);
}
struct hb_svg_clip_emit_data_t
{
const hb_svg_defs_t *defs;
const hb_svg_clip_path_def_t *clip;
hb_transform_t<> base_transform;
hb_transform_t<> bbox_transform;
bool has_bbox_transform = false;
};
static inline hb_transform_t<>
svg_to_hb_transform (const hb_svg_transform_t &t)
{
return hb_transform_t<> (t.xx, t.yx, t.xy, t.yy, t.dx, t.dy);
}
static void
svg_clip_path_emit (hb_draw_funcs_t *dfuncs,
void *draw_data,
void *user_data)
{
hb_raster_draw_t *rdr = (hb_raster_draw_t *) draw_data;
hb_svg_clip_emit_data_t *ed = (hb_svg_clip_emit_data_t *) user_data;
const hb_svg_clip_path_def_t *clip = ed->clip;
for (unsigned i = 0; i < clip->shape_count; i++)
{
const hb_svg_clip_shape_t &s = ed->defs->clip_shapes[clip->first_shape + i];
hb_transform_t<> t = ed->base_transform;
if (ed->has_bbox_transform)
t.multiply (ed->bbox_transform);
if (clip->has_clip_transform)
t.multiply (svg_to_hb_transform (clip->clip_transform));
if (s.has_transform)
t.multiply (svg_to_hb_transform (s.transform));
hb_raster_draw_set_transform (rdr, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0);
hb_svg_shape_emit_data_t shape = s.shape;
hb_raster_svg_shape_path_emit (dfuncs, draw_data, &shape);
}
}
bool
hb_raster_svg_push_clip_path_ref (hb_raster_paint_t *paint,
hb_svg_defs_t *defs,
hb_svg_str_t clip_path_str,
const hb_extents_t<> *object_bbox)
{
if (clip_path_str.is_null ()) return false;
hb_svg_str_t trimmed = clip_path_str.trim ();
if (!trimmed.len || trimmed.eq_ascii_ci ("none")) return false;
hb_svg_str_t clip_id;
if (!hb_raster_svg_parse_local_id_ref (trimmed, &clip_id, nullptr))
return false;
const hb_svg_clip_path_def_t *clip = defs->find_clip_path (hb_bytes_t (clip_id.data, clip_id.len));
if (!clip) return false;
hb_svg_clip_emit_data_t ed;
ed.defs = defs;
ed.clip = clip;
ed.base_transform = paint->current_effective_transform ();
if (!clip->units_user_space)
{
if (!object_bbox || object_bbox->is_empty ())
return false;
float w = object_bbox->xmax - object_bbox->xmin;
float h = object_bbox->ymax - object_bbox->ymin;
if (!(std::isfinite (w) && std::isfinite (h)) || w <= 0.f || h <= 0.f)
return false;
ed.has_bbox_transform = true;
ed.bbox_transform = hb_transform_t<> (w, 0, 0, h, object_bbox->xmin, object_bbox->ymin);
}
hb_raster_paint_push_clip_path (paint, svg_clip_path_emit, &ed);
return true;
}
#endif /* !HB_NO_RASTER_SVG */
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_CLIP_HH
#define HB_RASTER_SVG_CLIP_HH
#include "hb.hh"
#include "OT/Color/svg/svg.hh"
#include "hb-paint.hh"
#include "hb-raster-svg-defs.hh"
struct hb_raster_paint_t;
HB_INTERNAL void
hb_raster_svg_process_clip_path_def (hb_svg_defs_t *defs,
hb_svg_xml_parser_t &parser,
hb_svg_token_type_t tok,
const char *doc_start,
unsigned doc_len,
const OT::SVG::accelerator_t *svg_accel,
const OT::SVG::svg_doc_cache_t *doc_cache);
HB_INTERNAL bool
hb_raster_svg_push_clip_path_ref (hb_raster_paint_t *paint,
hb_svg_defs_t *defs,
hb_svg_str_t clip_path_str,
const hb_extents_t<> *object_bbox);
#endif /* HB_RASTER_SVG_CLIP_HH */
+340
View File
@@ -0,0 +1,340 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-color.hh"
#include "hb-raster.h"
#include "hb-raster-svg-base.hh"
#include "hb-ot-color.h"
#include <string.h>
#include <stdlib.h>
struct hb_svg_named_color_t
{
const char *name;
uint32_t rgb; /* 0xRRGGBB */
};
static const hb_svg_named_color_t svg_named_colors[] = {
{"aliceblue", 0xF0F8FF},
{"antiquewhite", 0xFAEBD7},
{"aqua", 0x00FFFF},
{"aquamarine", 0x7FFFD4},
{"azure", 0xF0FFFF},
{"beige", 0xF5F5DC},
{"bisque", 0xFFE4C4},
{"black", 0x000000},
{"blanchedalmond", 0xFFEBCD},
{"blue", 0x0000FF},
{"blueviolet", 0x8A2BE2},
{"brown", 0xA52A2A},
{"burlywood", 0xDEB887},
{"cadetblue", 0x5F9EA0},
{"chartreuse", 0x7FFF00},
{"chocolate", 0xD2691E},
{"coral", 0xFF7F50},
{"cornflowerblue", 0x6495ED},
{"cornsilk", 0xFFF8DC},
{"crimson", 0xDC143C},
{"cyan", 0x00FFFF},
{"darkblue", 0x00008B},
{"darkcyan", 0x008B8B},
{"darkgoldenrod", 0xB8860B},
{"darkgray", 0xA9A9A9},
{"darkgreen", 0x006400},
{"darkgrey", 0xA9A9A9},
{"darkkhaki", 0xBDB76B},
{"darkmagenta", 0x8B008B},
{"darkolivegreen", 0x556B2F},
{"darkorange", 0xFF8C00},
{"darkorchid", 0x9932CC},
{"darkred", 0x8B0000},
{"darksalmon", 0xE9967A},
{"darkseagreen", 0x8FBC8F},
{"darkslateblue", 0x483D8B},
{"darkslategray", 0x2F4F4F},
{"darkslategrey", 0x2F4F4F},
{"darkturquoise", 0x00CED1},
{"darkviolet", 0x9400D3},
{"deeppink", 0xFF1493},
{"deepskyblue", 0x00BFFF},
{"dimgray", 0x696969},
{"dimgrey", 0x696969},
{"dodgerblue", 0x1E90FF},
{"firebrick", 0xB22222},
{"floralwhite", 0xFFFAF0},
{"forestgreen", 0x228B22},
{"fuchsia", 0xFF00FF},
{"gainsboro", 0xDCDCDC},
{"ghostwhite", 0xF8F8FF},
{"gold", 0xFFD700},
{"goldenrod", 0xDAA520},
{"gray", 0x808080},
{"green", 0x008000},
{"greenyellow", 0xADFF2F},
{"grey", 0x808080},
{"honeydew", 0xF0FFF0},
{"hotpink", 0xFF69B4},
{"indianred", 0xCD5C5C},
{"indigo", 0x4B0082},
{"ivory", 0xFFFFF0},
{"khaki", 0xF0E68C},
{"lavender", 0xE6E6FA},
{"lavenderblush", 0xFFF0F5},
{"lawngreen", 0x7CFC00},
{"lemonchiffon", 0xFFFACD},
{"lightblue", 0xADD8E6},
{"lightcoral", 0xF08080},
{"lightcyan", 0xE0FFFF},
{"lightgoldenrodyellow", 0xFAFAD2},
{"lightgray", 0xD3D3D3},
{"lightgreen", 0x90EE90},
{"lightgrey", 0xD3D3D3},
{"lightpink", 0xFFB6C1},
{"lightsalmon", 0xFFA07A},
{"lightseagreen", 0x20B2AA},
{"lightskyblue", 0x87CEFA},
{"lightslategray", 0x778899},
{"lightslategrey", 0x778899},
{"lightsteelblue", 0xB0C4DE},
{"lightyellow", 0xFFFFE0},
{"lime", 0x00FF00},
{"limegreen", 0x32CD32},
{"linen", 0xFAF0E6},
{"magenta", 0xFF00FF},
{"maroon", 0x800000},
{"mediumaquamarine", 0x66CDAA},
{"mediumblue", 0x0000CD},
{"mediumorchid", 0xBA55D3},
{"mediumpurple", 0x9370DB},
{"mediumseagreen", 0x3CB371},
{"mediumslateblue", 0x7B68EE},
{"mediumspringgreen", 0x00FA9A},
{"mediumturquoise", 0x48D1CC},
{"mediumvioletred", 0xC71585},
{"midnightblue", 0x191970},
{"mintcream", 0xF5FFFA},
{"mistyrose", 0xFFE4E1},
{"moccasin", 0xFFE4B5},
{"navajowhite", 0xFFDEAD},
{"navy", 0x000080},
{"oldlace", 0xFDF5E6},
{"olive", 0x808000},
{"olivedrab", 0x6B8E23},
{"orange", 0xFFA500},
{"orangered", 0xFF4500},
{"orchid", 0xDA70D6},
{"palegoldenrod", 0xEEE8AA},
{"palegreen", 0x98FB98},
{"paleturquoise", 0xAFEEEE},
{"palevioletred", 0xDB7093},
{"papayawhip", 0xFFEFD5},
{"peachpuff", 0xFFDAB9},
{"peru", 0xCD853F},
{"pink", 0xFFC0CB},
{"plum", 0xDDA0DD},
{"powderblue", 0xB0E0E6},
{"purple", 0x800080},
{"rebeccapurple", 0x663399},
{"red", 0xFF0000},
{"rosybrown", 0xBC8F8F},
{"royalblue", 0x4169E1},
{"saddlebrown", 0x8B4513},
{"salmon", 0xFA8072},
{"sandybrown", 0xF4A460},
{"seagreen", 0x2E8B57},
{"seashell", 0xFFF5EE},
{"sienna", 0xA0522D},
{"silver", 0xC0C0C0},
{"skyblue", 0x87CEEB},
{"slateblue", 0x6A5ACD},
{"slategray", 0x708090},
{"slategrey", 0x708090},
{"snow", 0xFFFAFA},
{"springgreen", 0x00FF7F},
{"steelblue", 0x4682B4},
{"tan", 0xD2B48C},
{"teal", 0x008080},
{"thistle", 0xD8BFD8},
{"tomato", 0xFF6347},
{"turquoise", 0x40E0D0},
{"violet", 0xEE82EE},
{"wheat", 0xF5DEB3},
{"white", 0xFFFFFF},
{"whitesmoke", 0xF5F5F5},
{"yellow", 0xFFFF00},
{"yellowgreen", 0x9ACD32},
};
static int
hexval (char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
/* Parse SVG color value; returns HB_COLOR with alpha = 255.
* Sets *is_none if "none". */
hb_color_t
hb_raster_svg_parse_color (hb_svg_str_t s,
hb_paint_funcs_t *pfuncs,
void *paint_data,
hb_color_t foreground,
hb_face_t *face,
unsigned palette,
bool *is_none)
{
*is_none = false;
s = s.trim ();
if (!s.len) { *is_none = true; return HB_COLOR (0, 0, 0, 0); }
if (s.eq_ascii_ci ("none") || s.eq_ascii_ci ("transparent"))
{
*is_none = true;
return HB_COLOR (0, 0, 0, 0);
}
if (s.eq_ascii_ci ("currentColor"))
return foreground;
/* var(--colorN) → CPAL palette color */
if (s.starts_with_ascii_ci ("var("))
{
const char *p = s.data + 4;
const char *e = s.data + s.len;
while (p < e && *p == ' ') p++;
if (p + 7 < e && p[0] == '-' && p[1] == '-' && p[2] == 'c' &&
p[3] == 'o' && p[4] == 'l' && p[5] == 'o' && p[6] == 'r')
{
p += 7;
unsigned color_index = 0;
while (p < e && *p >= '0' && *p <= '9')
color_index = color_index * 10 + (*p++ - '0');
hb_color_t palette_color;
if (hb_paint_custom_palette_color (pfuncs, paint_data, color_index, &palette_color))
return palette_color;
unsigned count = 1;
hb_ot_color_palette_get_colors (face, palette, color_index, &count, &palette_color);
if (count)
return palette_color;
}
/* Fallback value after comma: var(--colorN, fallback) */
p = s.data + 4;
while (p < e && *p != ',') p++;
if (p < e)
{
p++;
while (p < e && *p == ' ') p++;
const char *val_start = p;
/* Find closing paren */
while (e > val_start && *(e - 1) != ')') e--;
if (e > val_start) e--;
hb_svg_str_t fallback = {val_start, (unsigned) (e - val_start)};
return hb_raster_svg_parse_color (fallback, pfuncs, paint_data, foreground, face, palette, is_none);
}
return foreground;
}
/* #RGB or #RRGGBB */
if (s.data[0] == '#')
{
if (s.len == 4) /* #RGB */
{
int r = hexval (s.data[1]);
int g = hexval (s.data[2]);
int b = hexval (s.data[3]);
if (r < 0 || g < 0 || b < 0) return HB_COLOR (0, 0, 0, 255);
return HB_COLOR (b * 17, g * 17, r * 17, 255);
}
if (s.len == 7) /* #RRGGBB */
{
int r = hexval (s.data[1]) * 16 + hexval (s.data[2]);
int g = hexval (s.data[3]) * 16 + hexval (s.data[4]);
int b = hexval (s.data[5]) * 16 + hexval (s.data[6]);
return HB_COLOR (b, g, r, 255);
}
return HB_COLOR (0, 0, 0, 255);
}
/* rgb(r, g, b) or rgb(r%, g%, b%) */
if (s.starts_with ("rgb"))
{
const char *p = s.data + 3;
const char *e = s.data + s.len;
while (p < e && *p != '(') p++;
if (p < e) p++;
auto read_component = [&] () -> int {
while (p < e && (*p == ' ' || *p == ',')) p++;
char buf[32];
unsigned n = 0;
while (p < e && n < sizeof (buf) - 1 && ((*p >= '0' && *p <= '9') || *p == '.' || *p == '-'))
buf[n++] = *p++;
bool is_pct = (p < e && *p == '%');
if (is_pct) p++;
buf[n] = '\0';
float val = strtof (buf, nullptr);
if (is_pct) val = val * 255.f / 100.f;
return hb_clamp ((int) (val + 0.5f), 0, 255);
};
int r = read_component ();
int g = read_component ();
int b = read_component ();
return HB_COLOR (b, g, r, 255);
}
/* Named colors (case-insensitive comparison) */
{
char lower[32];
unsigned n = hb_min (s.len, (unsigned) sizeof (lower) - 1);
for (unsigned i = 0; i < n; i++)
lower[i] = (s.data[i] >= 'A' && s.data[i] <= 'Z') ? s.data[i] + 32 : s.data[i];
lower[n] = '\0';
for (unsigned i = 0; i < ARRAY_LENGTH (svg_named_colors); i++)
if (strcmp (lower, svg_named_colors[i].name) == 0)
{
uint32_t rgb = svg_named_colors[i].rgb;
return HB_COLOR (rgb & 0xFF, (rgb >> 8) & 0xFF, (rgb >> 16) & 0xFF, 255);
}
}
return HB_COLOR (0, 0, 0, 255);
}
#endif /* !HB_NO_RASTER_SVG */
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_COLOR_HH
#define HB_RASTER_SVG_COLOR_HH
#include "hb.hh"
#include "hb-face.hh"
#include "hb-raster-svg-base.hh"
HB_INTERNAL hb_color_t
hb_raster_svg_parse_color (hb_svg_str_t s,
hb_paint_funcs_t *pfuncs,
void *paint_data,
hb_color_t foreground,
hb_face_t *face,
unsigned palette,
bool *is_none);
#endif /* HB_RASTER_SVG_COLOR_HH */
+113
View File
@@ -0,0 +1,113 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_CONTEXT_HH
#define HB_RASTER_SVG_CONTEXT_HH
#include "hb.hh"
#include "OT/Color/svg/svg.hh"
#include "hb-decycler.hh"
#include "hb-raster-paint.hh"
#include "hb-raster-svg-base.hh"
#include "hb-raster-svg-defs.hh"
struct hb_svg_fill_context_t
{
hb_raster_paint_t *paint;
hb_paint_funcs_t *pfuncs;
hb_font_t *font;
unsigned palette;
hb_svg_defs_t *defs;
};
struct hb_svg_use_context_t
{
hb_raster_paint_t *paint;
hb_paint_funcs_t *pfuncs;
const char *doc_start;
unsigned doc_len;
const OT::SVG::accelerator_t *svg_accel;
const OT::SVG::svg_doc_cache_t *doc_cache;
hb_decycler_t *use_decycler;
};
struct hb_svg_render_context_t
{
hb_raster_paint_t *paint;
hb_paint_funcs_t *pfuncs;
hb_font_t *font;
unsigned palette;
hb_color_t foreground;
hb_svg_defs_t defs;
int depth = 0;
const char *doc_start;
unsigned doc_len;
const OT::SVG::accelerator_t *svg_accel = nullptr;
const OT::SVG::svg_doc_cache_t *doc_cache = nullptr;
hb_decycler_t use_decycler;
bool suppress_viewbox_once = false;
bool allow_symbol_render_once = false;
void push_transform (float xx, float yx, float xy, float yy, float dx, float dy)
{
hb_paint_push_transform (pfuncs, paint, xx, yx, xy, yy, dx, dy);
}
void pop_transform ()
{
hb_paint_pop_transform (pfuncs, paint);
}
void push_group ()
{
hb_paint_push_group (pfuncs, paint);
}
void pop_group (hb_paint_composite_mode_t mode)
{
hb_paint_pop_group (pfuncs, paint, mode);
}
void paint_color (hb_color_t color)
{
hb_paint_color (pfuncs, paint, false, color);
}
void pop_clip ()
{
hb_paint_pop_clip (pfuncs, paint);
}
};
struct hb_svg_cascade_t
{
hb_svg_str_t fill;
float fill_opacity = 1.f;
hb_svg_str_t clip_path;
hb_color_t color = HB_COLOR (0, 0, 0, 255);
bool visibility = true;
float opacity = 1.f;
};
#endif /* HB_RASTER_SVG_CONTEXT_HH */
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-defs-scan.hh"
#include "hb-raster-svg-clip.hh"
#include "hb-raster-svg-gradient.hh"
static inline bool
hb_raster_svg_process_defs_child_tag (const hb_svg_defs_scan_context_t *ctx,
hb_svg_xml_parser_t &parser,
hb_svg_token_type_t tok)
{
if (parser.tag_name.eq ("linearGradient"))
{
hb_raster_svg_process_gradient_def (ctx->defs, parser, tok, SVG_GRADIENT_LINEAR,
ctx->pfuncs, ctx->paint_data,
ctx->foreground, ctx->face,
ctx->palette);
return true;
}
if (parser.tag_name.eq ("radialGradient"))
{
hb_raster_svg_process_gradient_def (ctx->defs, parser, tok, SVG_GRADIENT_RADIAL,
ctx->pfuncs, ctx->paint_data,
ctx->foreground, ctx->face,
ctx->palette);
return true;
}
if (parser.tag_name.eq ("clipPath"))
{
hb_raster_svg_process_clip_path_def (ctx->defs, parser, tok,
ctx->doc_start, ctx->doc_len,
ctx->svg_accel, ctx->doc_cache);
return true;
}
return false;
}
void
hb_raster_svg_process_defs_element (const hb_svg_defs_scan_context_t *ctx,
hb_svg_xml_parser_t &parser)
{
int depth = 1;
while (depth > 0)
{
hb_svg_token_type_t tok = parser.next ();
if (tok == SVG_TOKEN_EOF) break;
if (tok == SVG_TOKEN_CLOSE_TAG)
{
depth--;
continue;
}
if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG)
{
if (!hb_raster_svg_process_defs_child_tag (ctx, parser, tok) &&
tok == SVG_TOKEN_OPEN_TAG)
depth++;
}
}
}
void
hb_raster_svg_collect_defs (const hb_svg_defs_scan_context_t *ctx,
const char *data,
unsigned data_len)
{
hb_svg_xml_parser_t parser (data, data_len);
while (true)
{
hb_svg_token_type_t tok = parser.next ();
if (tok == SVG_TOKEN_EOF) break;
if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG)
hb_raster_svg_process_defs_child_tag (ctx, parser, tok);
}
}
#endif /* !HB_NO_RASTER_SVG */
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_DEFS_SCAN_HH
#define HB_RASTER_SVG_DEFS_SCAN_HH
#include "hb.hh"
#include "OT/Color/svg/svg.hh"
#include "hb-raster-svg-defs.hh"
struct hb_svg_defs_scan_context_t
{
hb_svg_defs_t *defs;
hb_paint_funcs_t *pfuncs;
void *paint_data;
hb_color_t foreground;
hb_face_t *face;
unsigned palette;
const char *doc_start;
unsigned doc_len;
const OT::SVG::accelerator_t *svg_accel;
const OT::SVG::svg_doc_cache_t *doc_cache;
};
HB_INTERNAL void
hb_raster_svg_process_defs_element (const hb_svg_defs_scan_context_t *ctx,
hb_svg_xml_parser_t &parser);
HB_INTERNAL void
hb_raster_svg_collect_defs (const hb_svg_defs_scan_context_t *ctx,
const char *data,
unsigned data_len);
#endif /* HB_RASTER_SVG_DEFS_SCAN_HH */
+121
View File
@@ -0,0 +1,121 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-defs.hh"
#include <string.h>
hb_svg_defs_t::~hb_svg_defs_t ()
{
for (unsigned i = 0; i < owned_id_strings.length; i++)
hb_free (owned_id_strings.arrayZ[i]);
}
bool
hb_svg_defs_t::add_id_mapping (hb_hashmap_t<hb_bytes_t, unsigned> *map,
hb_bytes_t id,
unsigned idx)
{
if (!id.length)
return false;
if (map->has (id))
return true;
unsigned n = (unsigned) id.length;
char *owned = (char *) hb_malloc (n + 1);
if (unlikely (!owned))
return false;
hb_memcpy (owned, id.arrayZ, n);
owned[n] = '\0';
if (unlikely (!owned_id_strings.push_or_fail (owned)))
{
hb_free (owned);
return false;
}
hb_bytes_t owned_key = hb_bytes_t (owned, n);
if (unlikely (!map->set (owned_key, idx)))
return false;
return true;
}
bool
hb_svg_defs_t::add_gradient (hb_bytes_t id, const hb_svg_gradient_t &grad)
{
unsigned idx = gradients.length;
gradients.push (grad);
if (unlikely (gradients.in_error ()))
return false;
if (unlikely (!add_id_mapping (&gradient_by_id, id, idx)))
{
gradients.pop ();
return false;
}
return true;
}
const hb_svg_gradient_t *
hb_svg_defs_t::find_gradient (hb_bytes_t id) const
{
if (!id.length) return nullptr;
unsigned *idx = nullptr;
if (gradient_by_id.has (id, &idx))
return &gradients[*idx];
return nullptr;
}
bool
hb_svg_defs_t::add_clip_path (hb_bytes_t id, const hb_svg_clip_path_def_t &clip)
{
unsigned idx = clip_paths.length;
clip_paths.push (clip);
if (unlikely (clip_paths.in_error ()))
return false;
if (unlikely (!add_id_mapping (&clip_path_by_id, id, idx)))
{
clip_paths.pop ();
return false;
}
return true;
}
const hb_svg_clip_path_def_t *
hb_svg_defs_t::find_clip_path (hb_bytes_t id) const
{
if (!id.length) return nullptr;
unsigned *idx = nullptr;
if (clip_path_by_id.has (id, &idx))
return &clip_paths[*idx];
return nullptr;
}
#endif /* !HB_NO_RASTER_SVG */
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_DEFS_HH
#define HB_RASTER_SVG_DEFS_HH
#include "hb.hh"
#include "hb-map.hh"
#include "hb-raster-svg-parse.hh"
enum hb_svg_gradient_type_t
{
SVG_GRADIENT_LINEAR,
SVG_GRADIENT_RADIAL
};
struct hb_svg_gradient_stop_t
{
float offset;
hb_color_t color;
bool is_current_color = false;
};
struct hb_svg_gradient_t
{
hb_svg_gradient_type_t type;
hb_paint_extend_t spread = HB_PAINT_EXTEND_PAD;
bool has_spread = false;
hb_svg_transform_t gradient_transform;
bool has_gradient_transform = false;
bool units_user_space = false;
bool has_units_user_space = false;
float x1 = 0, y1 = 0, x2 = 1, y2 = 0;
bool has_x1 = false, has_y1 = false, has_x2 = false, has_y2 = false;
float cx = 0.5f, cy = 0.5f, r = 0.5f;
float fx = -1.f, fy = -1.f, fr = 0.f;
bool has_cx = false, has_cy = false, has_r = false;
bool has_fx = false, has_fy = false, has_fr = false;
hb_vector_t<hb_svg_gradient_stop_t> stops;
hb_bytes_t href_id = {};
};
struct hb_svg_clip_path_def_t
{
unsigned first_shape = 0;
unsigned shape_count = 0;
hb_svg_transform_t clip_transform;
bool has_clip_transform = false;
bool units_user_space = true;
};
struct hb_svg_clip_shape_t
{
hb_svg_shape_emit_data_t shape;
hb_svg_transform_t transform;
bool has_transform = false;
};
struct hb_svg_defs_t
{
hb_vector_t<hb_svg_gradient_t> gradients;
hb_vector_t<hb_svg_clip_shape_t> clip_shapes;
hb_vector_t<hb_svg_clip_path_def_t> clip_paths;
hb_hashmap_t<hb_bytes_t, unsigned> gradient_by_id;
hb_hashmap_t<hb_bytes_t, unsigned> clip_path_by_id;
hb_vector_t<char *> owned_id_strings;
HB_INTERNAL ~hb_svg_defs_t ();
HB_INTERNAL bool add_id_mapping (hb_hashmap_t<hb_bytes_t, unsigned> *map,
hb_bytes_t id,
unsigned idx);
HB_INTERNAL bool add_gradient (hb_bytes_t id, const hb_svg_gradient_t &grad);
HB_INTERNAL const hb_svg_gradient_t *find_gradient (hb_bytes_t id) const;
HB_INTERNAL bool add_clip_path (hb_bytes_t id, const hb_svg_clip_path_def_t &clip);
HB_INTERNAL const hb_svg_clip_path_def_t *find_clip_path (hb_bytes_t id) const;
};
#endif /* HB_RASTER_SVG_DEFS_HH */
+300
View File
@@ -0,0 +1,300 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-fill.hh"
#include "hb-raster-svg-base.hh"
#include "hb-raster-svg-color.hh"
#include "hb-decycler.hh"
#include <math.h>
struct hb_svg_color_line_data_t
{
const hb_svg_gradient_t *grad;
hb_paint_extend_t extend;
float alpha_scale;
hb_color_t current_color;
};
static unsigned
svg_color_line_get_stops (hb_color_line_t *color_line HB_UNUSED,
void *color_line_data,
unsigned int start,
unsigned int *count,
hb_color_stop_t *color_stops,
void *user_data HB_UNUSED)
{
hb_svg_color_line_data_t *cl = (hb_svg_color_line_data_t *) color_line_data;
const hb_svg_gradient_t *grad = cl->grad;
unsigned total = grad->stops.length;
if (count)
{
unsigned n = hb_min (*count, total > start ? total - start : 0u);
for (unsigned i = 0; i < n; i++)
{
const hb_svg_gradient_stop_t &stop = grad->stops[start + i];
color_stops[i].offset = stop.offset;
color_stops[i].is_foreground = false;
hb_color_t c = stop.is_current_color ? cl->current_color : stop.color;
if (stop.is_current_color)
{
uint8_t a = (uint8_t) hb_raster_div255 (hb_color_get_alpha (c) * hb_color_get_alpha (stop.color));
c = HB_COLOR (hb_color_get_blue (c),
hb_color_get_green (c),
hb_color_get_red (c),
a);
}
if (cl->alpha_scale < 1.f)
{
uint8_t a = (uint8_t) hb_clamp ((int) (hb_color_get_alpha (c) * cl->alpha_scale + 0.5f), 0, 255);
c = HB_COLOR (hb_color_get_blue (c),
hb_color_get_green (c),
hb_color_get_red (c),
a);
}
color_stops[i].color = c;
}
*count = n;
}
return total;
}
static hb_paint_extend_t
svg_color_line_get_extend (hb_color_line_t *color_line HB_UNUSED,
void *color_line_data,
void *user_data HB_UNUSED)
{
hb_svg_color_line_data_t *cl = (hb_svg_color_line_data_t *) color_line_data;
return cl->extend;
}
static bool
svg_parse_paint_url_with_fallback (hb_svg_str_t s,
hb_svg_str_t *out_id,
hb_svg_str_t *fallback)
{
if (out_id) *out_id = {};
hb_svg_str_t id;
if (!hb_raster_svg_parse_local_id_ref (s, &id, fallback))
return false;
if (!s.trim ().starts_with_ascii_ci ("url("))
return false;
if (out_id) *out_id = id;
return id.len > 0;
}
void
hb_raster_svg_emit_fill (const hb_svg_fill_context_t *ctx,
hb_svg_str_t fill_str,
float fill_opacity,
const hb_extents_t<> *object_bbox,
hb_color_t current_color)
{
bool is_none = false;
hb_svg_str_t url_id;
hb_svg_str_t fallback_paint;
bool has_url_paint = svg_parse_paint_url_with_fallback (fill_str, &url_id, &fallback_paint);
const hb_svg_gradient_t *grad = has_url_paint ? ctx->defs->find_gradient (hb_bytes_t (url_id.data, url_id.len)) : nullptr;
if (has_url_paint && !grad)
{
if (fallback_paint.len)
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
return;
}
if (grad)
{
const unsigned SVG_MAX_GRADIENT_REF_DEPTH = 1024;
hb_vector_t<const hb_svg_gradient_t *> chain;
hb_decycler_t decycler;
const hb_svg_gradient_t *cur = grad;
while (cur)
{
if (chain.length >= SVG_MAX_GRADIENT_REF_DEPTH)
break;
hb_decycler_node_t node (decycler);
if (unlikely (!node.visit ((uintptr_t) cur)))
break;
if (unlikely (!chain.push_or_fail (cur)))
{
if (fallback_paint.len)
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
return;
}
if (!cur->href_id.length) break;
const hb_svg_gradient_t *next = ctx->defs->find_gradient (cur->href_id);
if (!next) break;
cur = next;
}
if (!chain.length)
{
if (fallback_paint.len)
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
return;
}
if (!chain.length) {
if (fallback_paint.len)
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
return;
}
hb_svg_gradient_t effective = *chain.arrayZ[chain.length - 1];
for (int i = (int) chain.length - 2; i >= 0; i--)
{
const hb_svg_gradient_t *g = chain.arrayZ[i];
effective.type = g->type;
if (g->stops.length) effective.stops = g->stops;
if (g->has_spread) { effective.spread = g->spread; effective.has_spread = true; }
if (g->has_units_user_space)
{
effective.units_user_space = g->units_user_space;
effective.has_units_user_space = true;
}
if (g->has_gradient_transform)
{
effective.gradient_transform = g->gradient_transform;
effective.has_gradient_transform = true;
}
if (g->has_x1) { effective.x1 = g->x1; effective.has_x1 = true; }
if (g->has_y1) { effective.y1 = g->y1; effective.has_y1 = true; }
if (g->has_x2) { effective.x2 = g->x2; effective.has_x2 = true; }
if (g->has_y2) { effective.y2 = g->y2; effective.has_y2 = true; }
if (g->has_cx) { effective.cx = g->cx; effective.has_cx = true; }
if (g->has_cy) { effective.cy = g->cy; effective.has_cy = true; }
if (g->has_r) { effective.r = g->r; effective.has_r = true; }
if (g->has_fx) { effective.fx = g->fx; effective.has_fx = true; }
if (g->has_fy) { effective.fy = g->fy; effective.has_fy = true; }
if (g->has_fr) { effective.fr = g->fr; effective.has_fr = true; }
}
if (!effective.stops.length)
{
if (fallback_paint.len)
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
return;
}
hb_svg_color_line_data_t cl_data;
cl_data.grad = &effective;
cl_data.extend = effective.spread;
cl_data.alpha_scale = hb_clamp (fill_opacity, 0.f, 1.f);
cl_data.current_color = current_color;
hb_color_line_t cl = {
&cl_data,
svg_color_line_get_stops, nullptr,
svg_color_line_get_extend, nullptr
};
bool has_bbox_transform = !effective.units_user_space && object_bbox && !object_bbox->is_empty ();
if (has_bbox_transform)
{
float w = object_bbox->xmax - object_bbox->xmin;
float h = object_bbox->ymax - object_bbox->ymin;
if (std::isfinite (w) && std::isfinite (h) && w > 0.f && h > 0.f)
hb_paint_push_transform (ctx->pfuncs, ctx->paint, w, 0, 0, h, object_bbox->xmin, object_bbox->ymin);
else
has_bbox_transform = false;
}
if (effective.has_gradient_transform)
hb_paint_push_transform (ctx->pfuncs, ctx->paint,
effective.gradient_transform.xx,
effective.gradient_transform.yx,
effective.gradient_transform.xy,
effective.gradient_transform.yy,
effective.gradient_transform.dx,
effective.gradient_transform.dy);
if (effective.type == SVG_GRADIENT_LINEAR)
{
hb_paint_linear_gradient (ctx->pfuncs, ctx->paint, &cl,
effective.x1, effective.y1,
effective.x2, effective.y2,
effective.x2 - (effective.y2 - effective.y1),
effective.y2 + (effective.x2 - effective.x1));
}
else
{
if (!std::isfinite (effective.r) || effective.r < 0.f)
{
if (effective.has_gradient_transform)
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
if (has_bbox_transform)
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
if (fallback_paint.len)
hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color);
return;
}
float fx = effective.has_fx ? effective.fx : effective.cx;
float fy = effective.has_fy ? effective.fy : effective.cy;
float fr = effective.has_fr ? effective.fr : 0.f;
if (!std::isfinite (fr) || fr < 0.f)
fr = 0.f;
fr = hb_min (fr, effective.r);
hb_paint_radial_gradient (ctx->pfuncs, ctx->paint, &cl,
fx, fy, fr,
effective.cx, effective.cy, effective.r);
}
if (effective.has_gradient_transform)
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
if (has_bbox_transform)
hb_paint_pop_transform (ctx->pfuncs, ctx->paint);
return;
}
hb_color_t color = hb_raster_svg_parse_color (fill_str,
ctx->pfuncs,
ctx->paint,
current_color,
hb_font_get_face (ctx->font),
ctx->palette,
&is_none);
if (is_none) return;
if (fill_opacity < 1.f)
color = HB_COLOR (hb_color_get_blue (color),
hb_color_get_green (color),
hb_color_get_red (color),
(uint8_t) (hb_color_get_alpha (color) * fill_opacity + 0.5f));
hb_paint_color (ctx->pfuncs, ctx->paint, false, color);
}
#endif /* !HB_NO_RASTER_SVG */
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_FILL_HH
#define HB_RASTER_SVG_FILL_HH
#include "hb.hh"
#include "hb-raster-svg-context.hh"
HB_INTERNAL void
hb_raster_svg_emit_fill (const hb_svg_fill_context_t *ctx,
hb_svg_str_t fill_str,
float fill_opacity,
const hb_extents_t<> *object_bbox,
hb_color_t current_color);
#endif /* HB_RASTER_SVG_FILL_HH */
+245
View File
@@ -0,0 +1,245 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_NO_RASTER_SVG
#include "hb.hh"
#include "hb-raster-svg-gradient.hh"
#include "hb-raster-svg-base.hh"
static bool
svg_parse_gradient_stop (hb_svg_xml_parser_t &parser,
hb_svg_gradient_t &grad,
hb_paint_funcs_t *pfuncs,
void *paint_data,
hb_color_t foreground,
hb_face_t *face,
unsigned palette)
{
const unsigned SVG_MAX_GRADIENT_STOPS = 1024;
if (grad.stops.length >= SVG_MAX_GRADIENT_STOPS)
return true;
hb_svg_attr_view_t attrs (parser);
hb_svg_str_t style = attrs.get ("style");
hb_svg_style_props_t style_props;
svg_parse_style_props (style, &style_props);
hb_svg_str_t offset_str = svg_pick_attr_or_style (parser, style_props.offset, "offset");
hb_svg_str_t color_str = svg_pick_attr_or_style (parser, style_props.stop_color, "stop-color");
hb_svg_str_t opacity_str = svg_pick_attr_or_style (parser, style_props.stop_opacity, "stop-opacity");
hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display");
hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility");
if (display_str.trim ().eq_ascii_ci ("none"))
return true;
hb_svg_str_t visibility_trim = visibility_str.trim ();
if (visibility_trim.eq_ascii_ci ("hidden") ||
visibility_trim.eq_ascii_ci ("collapse"))
return true;
float offset = 0;
if (offset_str.len)
offset = hb_clamp (svg_parse_number_or_percent (offset_str, nullptr), 0.f, 1.f);
if (grad.stops.length)
offset = hb_max (offset, grad.stops.arrayZ[grad.stops.length - 1].offset);
bool is_none = false;
hb_color_t color = HB_COLOR (0, 0, 0, 255);
bool is_current_color = false;
if (color_str.len && !svg_str_is_inherit (color_str))
{
is_current_color = color_str.trim ().eq_ascii_ci ("currentColor");
color = hb_raster_svg_parse_color (color_str, pfuncs, paint_data, foreground, face, palette, &is_none);
}
if (opacity_str.len && !svg_str_is_inherit (opacity_str))
{
float opacity = svg_parse_float_clamped01 (opacity_str);
color = HB_COLOR (hb_color_get_blue (color),
hb_color_get_green (color),
hb_color_get_red (color),
(uint8_t) (hb_color_get_alpha (color) * opacity + 0.5f));
}
hb_svg_gradient_stop_t stop;
stop.offset = offset;
stop.color = color;
stop.is_current_color = is_current_color;
return grad.stops.push_or_fail (stop);
}
static void
svg_parse_gradient_attrs (hb_svg_xml_parser_t &parser,
hb_svg_gradient_t &grad)
{
hb_svg_style_props_t style_props;
svg_parse_style_props (parser.find_attr ("style"), &style_props);
hb_svg_str_t spread_str = svg_pick_attr_or_style (parser, style_props.spread_method, "spreadMethod").trim ();
if (spread_str.eq_ascii_ci ("reflect"))
{
grad.spread = HB_PAINT_EXTEND_REFLECT;
grad.has_spread = true;
}
else if (spread_str.eq_ascii_ci ("repeat"))
{
grad.spread = HB_PAINT_EXTEND_REPEAT;
grad.has_spread = true;
}
else if (spread_str.eq_ascii_ci ("pad"))
{
grad.spread = HB_PAINT_EXTEND_PAD;
grad.has_spread = true;
}
hb_svg_str_t units_str = svg_pick_attr_or_style (parser, style_props.gradient_units, "gradientUnits").trim ();
if (units_str.eq_ascii_ci ("userSpaceOnUse"))
{
grad.units_user_space = true;
grad.has_units_user_space = true;
}
else if (units_str.eq_ascii_ci ("objectBoundingBox"))
{
grad.units_user_space = false;
grad.has_units_user_space = true;
}
hb_svg_str_t transform_str = svg_pick_attr_or_style (parser, style_props.gradient_transform, "gradientTransform");
if (transform_str.len)
{
grad.has_gradient_transform = true;
hb_raster_svg_parse_transform (transform_str, &grad.gradient_transform);
}
hb_svg_str_t href = hb_raster_svg_find_href_attr (parser);
if (href.len)
{
hb_svg_str_t href_id;
if (hb_raster_svg_parse_local_id_ref (href, &href_id, nullptr))
grad.href_id = hb_bytes_t (href_id.data, href_id.len);
}
}
static void
svg_parse_gradient_geometry_attrs (hb_svg_xml_parser_t &parser,
hb_svg_gradient_t &grad)
{
hb_svg_style_props_t style_props;
svg_parse_style_props (parser.find_attr ("style"), &style_props);
if (grad.type == SVG_GRADIENT_LINEAR)
{
hb_svg_str_t x1_str = svg_pick_attr_or_style (parser, style_props.x1, "x1");
hb_svg_str_t y1_str = svg_pick_attr_or_style (parser, style_props.y1, "y1");
hb_svg_str_t x2_str = svg_pick_attr_or_style (parser, style_props.x2, "x2");
hb_svg_str_t y2_str = svg_pick_attr_or_style (parser, style_props.y2, "y2");
if (x1_str.len) { grad.x1 = svg_parse_number_or_percent (x1_str, nullptr); grad.has_x1 = true; }
if (y1_str.len) { grad.y1 = svg_parse_number_or_percent (y1_str, nullptr); grad.has_y1 = true; }
if (x2_str.len) { grad.x2 = svg_parse_number_or_percent (x2_str, nullptr); grad.has_x2 = true; }
if (y2_str.len) { grad.y2 = svg_parse_number_or_percent (y2_str, nullptr); grad.has_y2 = true; }
if (!grad.has_x2)
grad.x2 = 1.f;
}
else
{
hb_svg_str_t cx_str = svg_pick_attr_or_style (parser, style_props.cx, "cx");
hb_svg_str_t cy_str = svg_pick_attr_or_style (parser, style_props.cy, "cy");
hb_svg_str_t r_str = svg_pick_attr_or_style (parser, style_props.r, "r");
hb_svg_str_t fx_str = svg_pick_attr_or_style (parser, style_props.fx, "fx");
hb_svg_str_t fy_str = svg_pick_attr_or_style (parser, style_props.fy, "fy");
hb_svg_str_t fr_str = svg_pick_attr_or_style (parser, style_props.fr, "fr");
if (cx_str.len) { grad.cx = svg_parse_number_or_percent (cx_str, nullptr); grad.has_cx = true; }
if (cy_str.len) { grad.cy = svg_parse_number_or_percent (cy_str, nullptr); grad.has_cy = true; }
if (r_str.len) { grad.r = svg_parse_number_or_percent (r_str, nullptr); grad.has_r = true; }
if (fx_str.len) { grad.fx = svg_parse_number_or_percent (fx_str, nullptr); grad.has_fx = true; }
if (fy_str.len) { grad.fy = svg_parse_number_or_percent (fy_str, nullptr); grad.has_fy = true; }
if (fr_str.len) { grad.fr = svg_parse_number_or_percent (fr_str, nullptr); grad.has_fr = true; }
}
}
static void
svg_parse_gradient_children (hb_svg_defs_t *defs,
hb_svg_xml_parser_t &parser,
hb_svg_gradient_t &grad,
hb_svg_str_t *id,
hb_paint_funcs_t *pfuncs,
void *paint_data,
hb_color_t foreground,
hb_face_t *face,
unsigned palette)
{
int gdepth = 1;
bool had_alloc_failure = false;
while (gdepth > 0)
{
hb_svg_token_type_t gt = parser.next ();
if (gt == SVG_TOKEN_EOF) break;
if (gt == SVG_TOKEN_CLOSE_TAG) { gdepth--; continue; }
if ((gt == SVG_TOKEN_OPEN_TAG || gt == SVG_TOKEN_SELF_CLOSE_TAG) &&
parser.tag_name.eq ("stop"))
if (unlikely (!svg_parse_gradient_stop (parser, grad,
pfuncs, paint_data,
foreground, face,
palette)))
had_alloc_failure = true;
if (gt == SVG_TOKEN_OPEN_TAG && !parser.tag_name.eq ("stop"))
gdepth++;
}
if (had_alloc_failure || defs->gradients.in_error ())
*id = {};
}
void
hb_raster_svg_process_gradient_def (hb_svg_defs_t *defs,
hb_svg_xml_parser_t &parser,
hb_svg_token_type_t tok,
hb_svg_gradient_type_t type,
hb_paint_funcs_t *pfuncs,
void *paint_data,
hb_color_t foreground,
hb_face_t *face,
unsigned palette)
{
hb_svg_gradient_t grad;
grad.type = type;
svg_parse_gradient_geometry_attrs (parser, grad);
svg_parse_gradient_attrs (parser, grad);
hb_svg_str_t id = parser.find_attr ("id");
if (tok == SVG_TOKEN_OPEN_TAG)
svg_parse_gradient_children (defs, parser, grad, &id,
pfuncs, paint_data,
foreground, face,
palette);
if (id.len)
(void) defs->add_gradient (hb_bytes_t (id.data, id.len), grad);
}
#endif /* !HB_NO_RASTER_SVG */
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright © 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_RASTER_SVG_GRADIENT_HH
#define HB_RASTER_SVG_GRADIENT_HH
#include "hb.hh"
#include "hb-raster-svg-color.hh"
#include "hb-raster-svg-defs.hh"
HB_INTERNAL void
hb_raster_svg_process_gradient_def (hb_svg_defs_t *defs,
hb_svg_xml_parser_t &parser,
hb_svg_token_type_t tok,
hb_svg_gradient_type_t type,
hb_paint_funcs_t *pfuncs,
void *paint_data,
hb_color_t foreground,
hb_face_t *face,
unsigned palette);
#endif /* HB_RASTER_SVG_GRADIENT_HH */

Some files were not shown because too many files have changed in this diff Show More