Compare commits

..

60 Commits

Author SHA1 Message Date
Behdad Esfahbod d1c00c0470 [COLR] Respect HB_NO_PAINT
Fixes https://github.com/harfbuzz/harfbuzz/issues/4204
2023-04-24 13:24:47 -06:00
Behdad Esfahbod 4129061e37 Revert "Move hb-ot-name-language-static.hh out of hb-static.cc"
This reverts commit 7b5f0dd3a8.

Fixes https://github.com/harfbuzz/harfbuzz/issues/4203
2023-04-24 13:04:05 -06:00
Behdad Esfahbod e76a3649db [atomic] Comment 2023-04-22 10:20:25 -06:00
Behdad Esfahbod 8e43e3a8ce [priority-heap] Comment 2023-04-22 10:16:43 -06:00
Behdad Esfahbod 48f8ed7e02 Docs 2023-04-22 10:14:01 -06:00
Behdad Esfahbod b31684dca4 [cache] Add some AI-generated comments 2023-04-22 09:47:58 -06:00
Behdad Esfahbod bffdca89f7 [pool] Add funny Copilot comment 2023-04-22 09:32:57 -06:00
Behdad Esfahbod a960571f24 [glyf] Comments 2023-04-21 16:45:08 -06:00
Behdad Esfahbod 2b042cc5c6 [VarComposite] Implement trim_padding() 2023-04-21 16:43:47 -06:00
Qunxin Liu 591c9460dc [instancer] compile composite glyphs directly with shifted component
points instead of deltas
2023-04-21 15:47:26 -06:00
Behdad Esfahbod 3520f528aa [CompositeGlyph] Apply gvar deltas with component transform
This was being done wrong for one of the scaled_offsets() cases.
2023-04-21 15:46:36 -06:00
Behdad Esfahbod 33972b3bf6 [glyf] Increase CompositeGlyf memory allocation
The 50% wasn't justified by logic.
2023-04-21 12:37:51 -06:00
Behdad Esfahbod 290cef39be [glyf] When instancing, just spew empty VarComposites
Before we were dropping the entire glyf table.
2023-04-21 12:22:30 -06:00
Behdad Esfahbod 4353192d05 [aat] Tweak a couple sanitize calls that are never called 2023-04-21 11:59:15 -06:00
Behdad Esfahbod 305012609b [hdmx] Remove unused unsafe function 2023-04-21 11:42:18 -06:00
Behdad Esfahbod f74abc307d [face] Comment 2023-04-21 11:37:37 -06:00
Behdad Esfahbod d1f49ba6d2 [VarComposites] More ifdef guards 2023-04-20 18:17:14 -06:00
Behdad Esfahbod 1e9a0511f3 [subset] Fix HB_TINY build 2023-04-20 17:47:04 -06:00
Behdad Esfahbod 106a237e40 [subset/glyf] Close over VarComposite glyphs
Subsetting VarComposite glyphs works now.
2023-04-20 17:46:07 -06:00
Behdad Esfahbod f2d21425a3 [VarC/subset] Support subsetting VarComposites
By renumbering components.
2023-04-20 17:32:59 -06:00
Behdad Esfahbod 15d0a1dcfd [glyf] TODO 2023-04-20 17:28:10 -06:00
Behdad Esfahbod 85d0c3b5f1 [glyf] Comment 2023-04-20 17:17:28 -06:00
Behdad Esfahbod 5d74b42b9e [glyf] Change variable name 2023-04-20 17:15:57 -06:00
Behdad Esfahbod c997e490c7 Remove unnecessary return 2023-04-20 17:02:38 -06:00
Behdad Esfahbod 781da13e99 [glyf] Comment 2023-04-20 16:59:25 -06:00
Behdad Esfahbod 0e4bcf908c [hmtx] Add TODO 2023-04-20 16:23:41 -06:00
Behdad Esfahbod 639f45ef9e [beyond-64k/subset] Implement subsetting of hmtx beyond64k 2023-04-20 16:21:08 -06:00
Behdad Esfahbod b3da715b9c Fix HB_TINY build 2023-04-20 16:05:03 -06:00
Behdad Esfahbod 32f145ff9c Fix build 2023-04-20 15:58:26 -06:00
Behdad Esfahbod 000a3c5dca [beyond-64k/subset] Fetch lsb from glyph table if not available
The beyond-64k hmtx table doesn't encode LSB. If subsetting brings
the glyph under 64k (which currently is the only mode we support),
then we need to encode the LSB, which wasn't available. We need to
fetch xMin from glyf table and set it as LSB.
2023-04-20 15:53:10 -06:00
Behdad Esfahbod 1111c7578e hb_memset 2023-04-20 15:17:23 -06:00
Behdad Esfahbod 219e739c9f [beyond-64k/subset] Lower CompositeGlyph GID24's when possible 2023-04-20 15:13:12 -06:00
Behdad Esfahbod a2e8ecf996 [Glyph] Minor change type of type to enum type
Say that thrice.
2023-04-20 14:08:23 -06:00
Behdad Esfahbod 317e3693da [beyond-64k] Fail hmtx subsetting if subset too large 2023-04-20 11:48:43 -06:00
Behdad Esfahbod 64ecf8720c [beyond-64k] Fix subsetting of maxp 2023-04-20 11:42:45 -06:00
Behdad Esfahbod 7f629c0df2 [docs] clarify purpose of FreeType integration
Fixes https://github.com/harfbuzz/harfbuzz/issues/4200
2023-04-19 13:16:18 -06:00
Garret Rieger 19e1b698c5 [subset] Fix ubsan failure. 2023-04-18 13:21:46 -06:00
Garret Rieger 647b024784 [subset] Fix fuzzer issue https://oss-fuzz.com/testcase-detail/6521393809588224 2023-04-18 13:21:46 -06:00
Garret Rieger 3db6baa20e [subset] add test for lig glyph fix. 2023-04-17 14:35:27 -06:00
Garret Rieger 8658c257c4 [subset] In LigatureSubst subsetting, check if the ligature glyph is in glyphset.
Otherwise coverage will not match the retained ligature sets.
2023-04-17 14:35:27 -06:00
Josef Friedrich ac4c3b3e85 Fix typos in the source code docs 2023-04-17 12:21:05 -06:00
DeadSix27 ef6adadba9 meson: add an option to disable utilities building
Adds the missing utilities option to meson builds for parity with CMake builds
2023-04-17 18:55:38 +02:00
Garret Rieger 90356eb226 [subset] Note --no-layout-closure is only for GSUB. 2023-04-14 15:12:41 -06:00
Garret Rieger 14b9d8d534 [subset] add --no-layout-closure flag.
Disables layout glyph closure. Fixes #4192.
2023-04-14 15:12:41 -06:00
Behdad Esfahbod 9c258936e7 [SECURITY] Update 2023-04-14 11:35:34 -06:00
Pedro Kaj Kjellerup Nacht feb1f6d39e Add security policy
Signed-off-by: Pedro Kaj Kjellerup Nacht <pnacht@google.com>
2023-04-14 11:34:06 -06:00
Pedro Kaj Kjellerup Nacht 26c719e8cd Add read-only top-level permissions to cifuzz.yml
Signed-off-by: Pedro Kaj Kjellerup Nacht <pnacht@google.com>
2023-04-12 10:23:20 -06:00
Qunxin Liu 96ed20725c [instancer] update bound metrics for CFF2 instancing 2023-04-05 17:27:11 -06:00
Behdad Esfahbod 04a47932a3 [paint] Remove enum trailing comma in C header
Fixes https://github.com/harfbuzz/harfbuzz/discussions/4188
2023-03-31 11:44:08 -06:00
Garret Rieger 2cd81fdfb6 [subset] fix memory leak.
Fixes fuzzer issue https://oss-fuzz.com/testcase-detail/6169920089227264
2023-03-30 16:19:41 -06:00
Behdad Esfahbod 453ded0539 [indic] Tighten up base-finding
Fixes https://github.com/harfbuzz/harfbuzz/issues/4185
2023-03-28 13:18:03 -06:00
Qunxin Liu c1acfe9966 [instancer] bug fix in TupleVariationData get_size ()
We need to iterate TupleVariationHeader when calculating the total size
2023-03-27 15:37:06 -06:00
Garret Rieger be87200106 [subset] fix buffer overflow fuzzer reported issue. 2023-03-25 10:11:46 -04:00
Garret Rieger 79ae6b657f [subset] Fix fuzzer found memory leaks. 2023-03-25 10:11:46 -04:00
Qunxin Liu ab87d7d225 [instance] add tests for colrv1 full instancing 2023-03-21 11:51:58 -06:00
Qunxin Liu fe671a5ac8 [instancer] support COLRv1 full instancing 2023-03-21 11:51:58 -06:00
Garret Rieger f0f7f22525 [subset] fix fuzzer found null deref.
https://oss-fuzz.com/testcase-detail/5844352760152064
2023-03-21 11:41:09 -06:00
Garret Rieger 79233a1492 [subset] fix incorrectly specified lock.
Lock variable must have a name or it will immediately destruct.
2023-03-16 20:30:39 -06:00
Garret Rieger 8d8bcde8cf [set] don't allow -1 (HB_SET_VALUE_INVALID) to be inserted into a hb_set_t.
Add tests that check all of the addition methods.
2023-03-15 12:10:18 -06:00
Garret Rieger a84cae424d [subset] Don't add invalid gids (-1) to the glyphset when loading glyph map from the accelerator. 2023-03-15 12:10:18 -06:00
75 changed files with 961 additions and 235 deletions

View File

@ -1,5 +1,9 @@
name: CIFuzz name: CIFuzz
on: [pull_request] on: [pull_request]
permissions:
contents: read
jobs: jobs:
Fuzzing: Fuzzing:
runs-on: ubuntu-latest runs-on: ubuntu-latest

20
SECURITY.md Normal file
View File

@ -0,0 +1,20 @@
# Security Policy
If you have discovered a security vulnerability in this project, please report it
privately. **Do not disclose it as a public issue.** This gives me time to work with you
to fix the issue before public exposure, reducing the chance that the exploit will be
used before a patch is released.
You may submit the report in the following ways:
- send an email to behdad@behdad.org and harfbuzz-admin@googlegroups.com; and/or
- send me a [private vulnerability report](https://github.com/harfbuzz/harfbuzz/security/advisories/new)
Please provide the following information in your report:
- A description of the vulnerability and its impact
- How to reproduce the issue
This project is mostly maintained by two developers, working on a reasonable effort
basis. As such, we ask that you give us 90 days to work on a fix before public
disclosure.

View File

@ -174,7 +174,9 @@
<para> <para>
HarfBuzz provides integration points with FreeType at the HarfBuzz provides integration points with FreeType at the
face-object and font-object level and for the font-functions face-object and font-object level and for the font-functions
virtual-method structure of a font object. To use the virtual-method structure of a font object. These functions
make it easy for clients that use FreeType for rasterization
or font-loading, to use HarfBuzz for shaping. To use the
FreeType-integration API, include the FreeType-integration API, include the
<filename>hb-ft.h</filename> header. <filename>hb-ft.h</filename> header.
</para> </para>

View File

@ -373,7 +373,10 @@ foreach check : check_funcs
endforeach endforeach
subdir('src') subdir('src')
subdir('util')
if not get_option('utilities').disabled()
subdir('util')
endif
if not get_option('tests').disabled() if not get_option('tests').disabled()
subdir('test') subdir('test')

View File

@ -31,6 +31,8 @@ option('docs', type: 'feature', value: 'auto', yield: true,
description: 'Generate documentation with gtk-doc') description: 'Generate documentation with gtk-doc')
option('doc_tests', type: 'boolean', value: false, option('doc_tests', type: 'boolean', value: false,
description: 'Run gtkdoc-check tests') description: 'Run gtkdoc-check tests')
option('utilities', type: 'feature', value: 'enabled', yield: true,
description: 'Build harfbuzz utils')
option('benchmark', type: 'feature', value: 'disabled', option('benchmark', type: 'feature', value: 'disabled',
description: 'Enable benchmark tests') description: 'Enable benchmark tests')

View File

@ -40,7 +40,6 @@
*/ */
#define HB_OT_TAG_COLR HB_TAG('C','O','L','R') #define HB_OT_TAG_COLR HB_TAG('C','O','L','R')
namespace OT { namespace OT {
struct hb_paint_context_t; struct hb_paint_context_t;
} }
@ -242,10 +241,15 @@ struct Variable
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ value.closurev1 (c); } { value.closurev1 (c); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
if (!value.subset (c)) return_trace (false); if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
if (c->plan->all_axes_pinned)
return_trace (true);
//TODO: update varIdxBase for partial-instancing
return_trace (c->serializer->embed (varIdxBase)); return_trace (c->serializer->embed (varIdxBase));
} }
@ -296,10 +300,11 @@ struct NoVariable
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ value.closurev1 (c); } { value.closurev1 (c); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
return_trace (value.subset (c)); return_trace (value.subset (c, instancer, varIdxBase));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -337,11 +342,20 @@ struct ColorStop
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ c->add_palette_index (paletteIndex); } { c->add_palette_index (paletteIndex); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this); auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->stopOffset.set_float (stopOffset.to_float(instancer (varIdxBase, 0)));
out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 1)));
}
return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
HB_SERIALIZE_ERROR_INT_OVERFLOW)); HB_SERIALIZE_ERROR_INT_OVERFLOW));
} }
@ -390,7 +404,8 @@ struct ColorLine
stop.closurev1 (c); stop.closurev1 (c);
} }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this); auto *out = c->serializer->start_embed (this);
@ -402,7 +417,7 @@ struct ColorLine
for (const auto& stop : stops.iter ()) for (const auto& stop : stops.iter ())
{ {
if (!stop.subset (c)) return_trace (false); if (!stop.subset (c, instancer)) return_trace (false);
} }
return_trace (true); return_trace (true);
} }
@ -523,6 +538,25 @@ struct Affine2x3
return_trace (c->check_struct (this)); return_trace (c->check_struct (this));
} }
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->xx.set_float (xx.to_float(instancer (varIdxBase, 0)));
out->yx.set_float (yx.to_float(instancer (varIdxBase, 1)));
out->xy.set_float (xy.to_float(instancer (varIdxBase, 2)));
out->yy.set_float (yy.to_float(instancer (varIdxBase, 3)));
out->dx.set_float (dx.to_float(instancer (varIdxBase, 4)));
out->dy.set_float (dy.to_float(instancer (varIdxBase, 5)));
}
return_trace (true);
}
void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
{ {
c->funcs->push_transform (c->data, c->funcs->push_transform (c->data,
@ -548,7 +582,8 @@ struct PaintColrLayers
{ {
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer HB_UNUSED) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -579,11 +614,20 @@ struct PaintSolid
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ c->add_palette_index (paletteIndex); } { c->add_palette_index (paletteIndex); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this); auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
out->alpha.set_float (alpha.to_float (instancer (varIdxBase, 0)));
if (format == 3 && c->plan->all_axes_pinned)
out->format = 2;
return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
HB_SERIALIZE_ERROR_INT_OVERFLOW)); HB_SERIALIZE_ERROR_INT_OVERFLOW));
} }
@ -618,13 +662,28 @@ struct PaintLinearGradient
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+colorLine).closurev1 (c); } { (this+colorLine).closurev1 (c); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->colorLine.serialize_subset (c, colorLine, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
out->x1 = x1 + (int) roundf (instancer (varIdxBase, 2));
out->y1 = y1 + (int) roundf (instancer (varIdxBase, 3));
out->x2 = x2 + (int) roundf (instancer (varIdxBase, 4));
out->y2 = y2 + (int) roundf (instancer (varIdxBase, 5));
}
if (format == 5 && c->plan->all_axes_pinned)
out->format = 4;
return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -669,13 +728,28 @@ struct PaintRadialGradient
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+colorLine).closurev1 (c); } { (this+colorLine).closurev1 (c); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->colorLine.serialize_subset (c, colorLine, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->x0 = x0 + (int) roundf (instancer (varIdxBase, 0));
out->y0 = y0 + (int) roundf (instancer (varIdxBase, 1));
out->radius0 = radius0 + (unsigned) roundf (instancer (varIdxBase, 2));
out->x1 = x1 + (int) roundf (instancer (varIdxBase, 3));
out->y1 = y1 + (int) roundf (instancer (varIdxBase, 4));
out->radius1 = radius1 + (unsigned) roundf (instancer (varIdxBase, 5));
}
if (format == 7 && c->plan->all_axes_pinned)
out->format = 6;
return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -720,13 +794,26 @@ struct PaintSweepGradient
void closurev1 (hb_colrv1_closure_context_t* c) const void closurev1 (hb_colrv1_closure_context_t* c) const
{ (this+colorLine).closurev1 (c); } { (this+colorLine).closurev1 (c); }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->colorLine.serialize_subset (c, colorLine, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->centerX = centerX + (int) roundf (instancer (varIdxBase, 0));
out->centerY = centerY + (int) roundf (instancer (varIdxBase, 1));
out->startAngle.set_float (startAngle.to_float (instancer (varIdxBase, 2)));
out->endAngle.set_float (endAngle.to_float (instancer (varIdxBase, 3)));
}
if (format == 9 && c->plan->all_axes_pinned)
out->format = 8;
return_trace (out->colorLine.serialize_subset (c, colorLine, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -766,7 +853,8 @@ struct PaintGlyph
{ {
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -776,7 +864,7 @@ struct PaintGlyph
HB_SERIALIZE_ERROR_INT_OVERFLOW)) HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false); return_trace (false);
return_trace (out->paint.serialize_subset (c, paint, this)); return_trace (out->paint.serialize_subset (c, paint, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -807,7 +895,8 @@ struct PaintColrGlyph
{ {
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer HB_UNUSED) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -836,13 +925,16 @@ struct PaintTransform
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
if (!out->transform.serialize_copy (c->serializer, transform, this)) return_trace (false); if (!out->transform.serialize_subset (c, transform, this, instancer)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (format == 13 && c->plan->all_axes_pinned)
out->format = 12;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -871,13 +963,24 @@ struct PaintTranslate
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->dx = dx + (int) roundf (instancer (varIdxBase, 0));
out->dy = dy + (int) roundf (instancer (varIdxBase, 1));
}
if (format == 15 && c->plan->all_axes_pinned)
out->format = 14;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -908,13 +1011,24 @@ struct PaintScale
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
}
if (format == 17 && c->plan->all_axes_pinned)
out->format = 16;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -945,13 +1059,26 @@ struct PaintScaleAroundCenter
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->scaleX.set_float (scaleX.to_float (instancer (varIdxBase, 0)));
out->scaleY.set_float (scaleY.to_float (instancer (varIdxBase, 1)));
out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
}
if (format == 19 && c->plan->all_axes_pinned)
out->format = 18;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -990,13 +1117,21 @@ struct PaintScaleUniform
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
if (format == 21 && c->plan->all_axes_pinned)
out->format = 20;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1025,13 +1160,25 @@ struct PaintScaleUniformAroundCenter
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->scale.set_float (scale.to_float (instancer (varIdxBase, 0)));
out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
}
if (format == 23 && c->plan->all_axes_pinned)
out->format = 22;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1068,13 +1215,21 @@ struct PaintRotate
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
if (format == 25 && c->plan->all_axes_pinned)
out->format = 24;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1103,13 +1258,25 @@ struct PaintRotateAroundCenter
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->angle.set_float (angle.to_float (instancer (varIdxBase, 0)));
out->centerX = centerX + (int) roundf (instancer (varIdxBase, 1));
out->centerY = centerY + (int) roundf (instancer (varIdxBase, 2));
}
if (format ==27 && c->plan->all_axes_pinned)
out->format = 26;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1146,13 +1313,24 @@ struct PaintSkew
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
}
if (format == 29 && c->plan->all_axes_pinned)
out->format = 28;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1183,13 +1361,26 @@ struct PaintSkewAroundCenter
{ {
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
return_trace (out->src.serialize_subset (c, src, this)); if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->xSkewAngle.set_float (xSkewAngle.to_float (instancer (varIdxBase, 0)));
out->ySkewAngle.set_float (ySkewAngle.to_float (instancer (varIdxBase, 1)));
out->centerX = centerX + (int) roundf (instancer (varIdxBase, 2));
out->centerY = centerY + (int) roundf (instancer (varIdxBase, 3));
}
if (format == 31 && c->plan->all_axes_pinned)
out->format = 30;
return_trace (out->src.serialize_subset (c, src, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1228,14 +1419,15 @@ struct PaintComposite
{ {
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false); if (unlikely (!out)) return_trace (false);
if (!out->src.serialize_subset (c, src, this)) return_trace (false); if (!out->src.serialize_subset (c, src, this, instancer)) return_trace (false);
return_trace (out->backdrop.serialize_subset (c, backdrop, this)); return_trace (out->backdrop.serialize_subset (c, backdrop, this, instancer));
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
@ -1283,6 +1475,28 @@ struct ClipBoxFormat1
clip_box.yMax = yMax; clip_box.yMax = yMax;
} }
bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
uint32_t varIdxBase) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
if (instancer && !c->plan->pinned_at_default && varIdxBase != VarIdx::NO_VARIATION)
{
out->xMin = xMin + (int) roundf (instancer (varIdxBase, 0));
out->yMin = yMin + (int) roundf (instancer (varIdxBase, 1));
out->xMax = xMax + (int) roundf (instancer (varIdxBase, 2));
out->yMax = yMax + (int) roundf (instancer (varIdxBase, 3));
}
if (format == 2 && c->plan->all_axes_pinned)
out->format = 1;
return_trace (true);
}
public: public:
HBUINT8 format; /* format = 1(noVar) or 2(Var)*/ HBUINT8 format; /* format = 1(noVar) or 2(Var)*/
FWORD xMin; FWORD xMin;
@ -1310,13 +1524,14 @@ struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
struct ClipBox struct ClipBox
{ {
ClipBox* copy (hb_serialize_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SERIALIZE (this); TRACE_SUBSET (this);
switch (u.format) { switch (u.format) {
case 1: return_trace (reinterpret_cast<ClipBox *> (c->embed (u.format1))); case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION));
case 2: return_trace (reinterpret_cast<ClipBox *> (c->embed (u.format2))); case 2: return_trace (u.format2.subset (c, instancer));
default:return_trace (nullptr); default:return_trace (c->default_return_value ());
} }
} }
@ -1367,13 +1582,15 @@ struct ClipRecord
int cmp (hb_codepoint_t g) const int cmp (hb_codepoint_t g) const
{ return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; } { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; }
ClipRecord* copy (hb_serialize_context_t *c, const void *base) const bool subset (hb_subset_context_t *c,
const void *base,
const VarStoreInstancer &instancer) const
{ {
TRACE_SERIALIZE (this); TRACE_SUBSET (this);
auto *out = c->embed (this); auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (nullptr); if (unlikely (!out)) return_trace (false);
if (!out->clipBox.serialize_copy (c, clipBox, base)) return_trace (nullptr);
return_trace (out); return_trace (out->clipBox.serialize_subset (c, clipBox, base, instancer));
} }
bool sanitize (hb_sanitize_context_t *c, const void *base) const bool sanitize (hb_sanitize_context_t *c, const void *base) const
@ -1400,7 +1617,8 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
struct ClipList struct ClipList
{ {
unsigned serialize_clip_records (hb_serialize_context_t *c, unsigned serialize_clip_records (hb_subset_context_t *c,
const VarStoreInstancer &instancer,
const hb_set_t& gids, const hb_set_t& gids,
const hb_map_t& gid_offset_map) const const hb_map_t& gid_offset_map) const
{ {
@ -1432,7 +1650,7 @@ struct ClipList
record.endGlyphID = prev_gid; record.endGlyphID = prev_gid;
record.clipBox = prev_offset; record.clipBox = prev_offset;
if (!c->copy (record, this)) return_trace (0); if (!record.subset (c, this, instancer)) return_trace (0);
count++; count++;
start_gid = _; start_gid = _;
@ -1446,13 +1664,14 @@ struct ClipList
record.startGlyphID = start_gid; record.startGlyphID = start_gid;
record.endGlyphID = prev_gid; record.endGlyphID = prev_gid;
record.clipBox = prev_offset; record.clipBox = prev_offset;
if (!c->copy (record, this)) return_trace (0); if (!record.subset (c, this, instancer)) return_trace (0);
count++; count++;
} }
return_trace (count); return_trace (count);
} }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this); auto *out = c->serializer->start_embed (*this);
@ -1477,7 +1696,7 @@ struct ClipList
} }
} }
unsigned count = serialize_clip_records (c->serializer, new_gids, new_gid_offset_map); unsigned count = serialize_clip_records (c, instancer, new_gids, new_gid_offset_map);
if (!count) return_trace (false); if (!count) return_trace (false);
return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW));
} }
@ -1611,7 +1830,8 @@ struct BaseGlyphPaintRecord
{ return g < glyphId ? -1 : g > glyphId ? 1 : 0; } { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
const void* src_base, hb_subset_context_t *c) const const void* src_base, hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
auto *out = s->embed (this); auto *out = s->embed (this);
@ -1620,7 +1840,7 @@ struct BaseGlyphPaintRecord
HB_SERIALIZE_ERROR_INT_OVERFLOW)) HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false); return_trace (false);
return_trace (out->paint.serialize_subset (c, paint, src_base)); return_trace (out->paint.serialize_subset (c, paint, src_base, instancer));
} }
bool sanitize (hb_sanitize_context_t *c, const void *base) const bool sanitize (hb_sanitize_context_t *c, const void *base) const
@ -1639,7 +1859,8 @@ struct BaseGlyphPaintRecord
struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord> struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
{ {
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this); auto *out = c->serializer->start_embed (this);
@ -1651,7 +1872,7 @@ struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
unsigned gid = _.glyphId; unsigned gid = _.glyphId;
if (!glyphset->has (gid)) continue; if (!glyphset->has (gid)) continue;
if (_.serialize (c->serializer, c->plan->glyph_map, this, c)) out->len++; if (_.serialize (c->serializer, c->plan->glyph_map, this, c, instancer)) out->len++;
else return_trace (false); else return_trace (false);
} }
@ -1670,7 +1891,8 @@ struct LayerList : Array32OfOffset32To<Paint>
const Paint& get_paint (unsigned i) const const Paint& get_paint (unsigned i) const
{ return this+(*this)[i]; } { return this+(*this)[i]; }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this); auto *out = c->serializer->start_embed (this);
@ -1681,7 +1903,7 @@ struct LayerList : Array32OfOffset32To<Paint>
{ {
auto *o = out->serialize_append (c->serializer); auto *o = out->serialize_append (c->serializer);
if (unlikely (!o) || !o->serialize_subset (c, _.second, this)) if (unlikely (!o) || !o->serialize_subset (c, _.second, this, instancer))
return_trace (false); return_trace (false);
} }
return_trace (true); return_trace (true);
@ -1883,7 +2105,6 @@ struct COLR
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
const hb_set_t& glyphset = c->plan->_glyphset_colred; const hb_set_t& glyphset = c->plan->_glyphset_colred;
@ -1954,7 +2175,12 @@ struct COLR
auto snap = c->serializer->snapshot (); auto snap = c->serializer->snapshot ();
if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false); if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false);
if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this))
VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr,
varIdxMap ? &(this+varIdxMap) : nullptr,
c->plan->normalized_coords.as_array ());
if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
{ {
if (c->serializer->in_error ()) return_trace (false); if (c->serializer->in_error ()) return_trace (false);
//no more COLRv1 glyphs: downgrade to version 0 //no more COLRv1 glyphs: downgrade to version 0
@ -1964,8 +2190,11 @@ struct COLR
if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false);
colr_prime->layerList.serialize_subset (c, layerList, this); colr_prime->layerList.serialize_subset (c, layerList, this, instancer);
colr_prime->clipList.serialize_subset (c, clipList, this); colr_prime->clipList.serialize_subset (c, clipList, this, instancer);
if (!varStore || c->plan->all_axes_pinned)
return_trace (true);
colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this);
colr_prime->varStore.serialize_copy (c->serializer, varStore, this); colr_prime->varStore.serialize_copy (c->serializer, varStore, this);
return_trace (true); return_trace (true);
@ -1984,14 +2213,15 @@ struct COLR
return nullptr; return nullptr;
} }
#ifndef HB_NO_PAINT
bool bool
get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
{ {
if (version != 1) if (version != 1)
return false; return false;
VarStoreInstancer instancer (this+varStore, VarStoreInstancer instancer (&(this+varStore),
this+varIdxMap, &(this+varIdxMap),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
if (get_clip (glyph, extents, instancer)) if (get_clip (glyph, extents, instancer))
@ -2022,6 +2252,7 @@ struct COLR
return ret; return ret;
} }
#endif
bool bool
has_paint_for_glyph (hb_codepoint_t glyph) const has_paint_for_glyph (hb_codepoint_t glyph) const
@ -2045,11 +2276,12 @@ struct COLR
instancer); instancer);
} }
#ifndef HB_NO_PAINT
bool bool
paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
{ {
VarStoreInstancer instancer (this+varStore, VarStoreInstancer instancer (&(this+varStore),
this+varIdxMap, &(this+varIdxMap),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
@ -2060,8 +2292,8 @@ struct COLR
{ {
// COLRv1 glyph // COLRv1 glyph
VarStoreInstancer instancer (this+varStore, VarStoreInstancer instancer (&(this+varStore),
this+varIdxMap, &(this+varIdxMap),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
bool is_bounded = true; bool is_bounded = true;
@ -2131,6 +2363,7 @@ struct COLR
return false; return false;
} }
#endif
protected: protected:
HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 version; /* Table version number (starts at 0). */

View File

@ -55,7 +55,7 @@ struct PairPosFormat1_3
if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4) if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4)
{ {
for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) for (hb_codepoint_t g : glyphs->iter())
{ {
unsigned i = cov.get_coverage (g); unsigned i = cov.get_coverage (g);
if ((this+pairSet[i]).intersects (glyphs, valueFormat)) if ((this+pairSet[i]).intersects (glyphs, valueFormat))

View File

@ -29,6 +29,9 @@ struct Ligature
bool intersects (const hb_set_t *glyphs) const bool intersects (const hb_set_t *glyphs) const
{ return hb_all (component, glyphs); } { return hb_all (component, glyphs); }
bool intersects_lig_glyph (const hb_set_t *glyphs) const
{ return glyphs->has(ligGlyph); }
void closure (hb_closure_context_t *c) const void closure (hb_closure_context_t *c) const
{ {
if (!intersects (c->glyphs)) return; if (!intersects (c->glyphs)) return;

View File

@ -34,6 +34,18 @@ struct LigatureSet
; ;
} }
bool intersects_lig_glyph (const hb_set_t *glyphs) const
{
return
+ hb_iter (ligature)
| hb_map (hb_add (this))
| hb_map ([glyphs] (const Ligature<Types> &_) {
return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs);
})
| hb_any
;
}
void closure (hb_closure_context_t *c) const void closure (hb_closure_context_t *c) const
{ {
+ hb_iter (ligature) + hb_iter (ligature)

View File

@ -130,7 +130,7 @@ struct LigatureSubstFormat1_2
+ hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this))) + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
| hb_filter (glyphset, hb_first) | hb_filter (glyphset, hb_first)
| hb_filter ([&] (const LigatureSet<Types>& _) { | hb_filter ([&] (const LigatureSet<Types>& _) {
return _.intersects (&glyphset); return _.intersects_lig_glyph (&glyphset);
}, hb_second) }, hb_second)
| hb_map (hb_first) | hb_map (hb_first)
| hb_sink (new_coverage); | hb_sink (new_coverage);

View File

@ -87,27 +87,34 @@ struct CompositeGlyphRecord
} }
} }
void transform_points (contour_point_vector_t &points) const void transform_points (contour_point_vector_t &points,
const float (&matrix)[4],
const contour_point_t &trans) const
{ {
float matrix[4]; if (scaled_offsets ())
contour_point_t trans;
if (get_transformation (matrix, trans))
{ {
if (scaled_offsets ()) points.translate (trans);
{ points.transform (matrix);
points.translate (trans); }
points.transform (matrix); else
} {
else points.transform (matrix);
{ points.translate (trans);
points.transform (matrix);
points.translate (trans);
}
} }
} }
unsigned compile_with_deltas (const contour_point_t &p_delta, bool get_points (contour_point_vector_t &points) const
char *out) const {
float matrix[4];
contour_point_t trans;
get_transformation (matrix, trans);
if (unlikely (!points.resize (points.length + 1))) return false;
points[points.length - 1] = trans;
return true;
}
unsigned compile_with_point (const contour_point_t &point,
char *out) const
{ {
const HBINT8 *p = &StructAfter<const HBINT8> (flags); const HBINT8 *p = &StructAfter<const HBINT8> (flags);
#ifndef HB_NO_BEYOND_64K #ifndef HB_NO_BEYOND_64K
@ -121,18 +128,17 @@ struct CompositeGlyphRecord
unsigned len_before_val = (const char *)p - (const char *)this; unsigned len_before_val = (const char *)p - (const char *)this;
if (flags & ARG_1_AND_2_ARE_WORDS) if (flags & ARG_1_AND_2_ARE_WORDS)
{ {
// no overflow, copy and update value with deltas // no overflow, copy value
hb_memcpy (out, this, len); hb_memcpy (out, this, len);
const HBINT16 *px = reinterpret_cast<const HBINT16 *> (p);
HBINT16 *o = reinterpret_cast<HBINT16 *> (out + len_before_val); HBINT16 *o = reinterpret_cast<HBINT16 *> (out + len_before_val);
o[0] = px[0] + roundf (p_delta.x); o[0] = roundf (point.x);
o[1] = px[1] + roundf (p_delta.y); o[1] = roundf (point.y);
} }
else else
{ {
int new_x = p[0] + roundf (p_delta.x); int new_x = roundf (point.x);
int new_y = p[1] + roundf (p_delta.y); int new_y = roundf (point.y);
if (new_x <= 127 && new_x >= -128 && if (new_x <= 127 && new_x >= -128 &&
new_y <= 127 && new_y >= -128) new_y <= 127 && new_y >= -128)
{ {
@ -143,7 +149,7 @@ struct CompositeGlyphRecord
} }
else else
{ {
// int8 overflows after deltas applied // new point value has an int8 overflow
hb_memcpy (out, this, len_before_val); hb_memcpy (out, this, len_before_val);
//update flags //update flags
@ -171,6 +177,7 @@ struct CompositeGlyphRecord
bool scaled_offsets () const bool scaled_offsets () const
{ return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }
public:
bool get_transformation (float (&matrix)[4], contour_point_t &trans) const bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
{ {
matrix[0] = matrix[3] = 1.f; matrix[0] = matrix[3] = 1.f;
@ -225,7 +232,6 @@ struct CompositeGlyphRecord
return tx || ty; return tx || ty;
} }
public:
hb_codepoint_t get_gid () const hb_codepoint_t get_gid () const
{ {
#ifndef HB_NO_BEYOND_64K #ifndef HB_NO_BEYOND_64K
@ -246,6 +252,27 @@ struct CompositeGlyphRecord
StructAfter<HBGlyphID16> (flags) = gid; StructAfter<HBGlyphID16> (flags) = gid;
} }
#ifndef HB_NO_BEYOND_64K
void lower_gid_24_to_16 ()
{
hb_codepoint_t gid = get_gid ();
if (!(flags & GID_IS_24BIT) || gid > 0xFFFFu)
return;
/* Lower the flag and move the rest of the struct down. */
unsigned size = get_size ();
char *end = (char *) this + size;
char *p = &StructAfter<char> (flags);
p += HBGlyphID24::static_size;
flags = flags & ~GID_IS_24BIT;
set_gid (gid);
memmove (p - HBGlyphID24::static_size + HBGlyphID16::static_size, p, end - p);
}
#endif
protected: protected:
HBUINT16 flags; HBUINT16 flags;
HBUINT24 pad; HBUINT24 pad;
@ -304,7 +331,7 @@ struct CompositeGlyph
} }
bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes, bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes,
const contour_point_vector_t &deltas, const contour_point_vector_t &points_with_deltas,
hb_bytes_t &dest_bytes /* OUT */) hb_bytes_t &dest_bytes /* OUT */)
{ {
if (source_bytes.length <= GlyphHeader::static_size || if (source_bytes.length <= GlyphHeader::static_size ||
@ -319,7 +346,7 @@ struct CompositeGlyph
/* try to allocate more memories than source glyph bytes /* try to allocate more memories than source glyph bytes
* in case that there might be an overflow for int8 value * in case that there might be an overflow for int8 value
* and we would need to use int16 instead */ * and we would need to use int16 instead */
char *o = (char *) hb_calloc (source_len + source_len/2, sizeof (char)); char *o = (char *) hb_calloc (source_len * 2, sizeof (char));
if (unlikely (!o)) return false; if (unlikely (!o)) return false;
const CompositeGlyphRecord *c = reinterpret_cast<const CompositeGlyphRecord *> (source_bytes.arrayZ + GlyphHeader::static_size); const CompositeGlyphRecord *c = reinterpret_cast<const CompositeGlyphRecord *> (source_bytes.arrayZ + GlyphHeader::static_size);
@ -329,8 +356,8 @@ struct CompositeGlyph
unsigned i = 0, source_comp_len = 0; unsigned i = 0, source_comp_len = 0;
for (const auto &component : it) for (const auto &component : it)
{ {
/* last 4 points in deltas are phantom points and should not be included */ /* last 4 points in points_with_deltas are phantom points and should not be included */
if (i >= deltas.length - 4) { if (i >= points_with_deltas.length - 4) {
free (o); free (o);
return false; return false;
} }
@ -343,7 +370,7 @@ struct CompositeGlyph
} }
else else
{ {
unsigned new_len = component.compile_with_deltas (deltas[i], p); unsigned new_len = component.compile_with_point (points_with_deltas[i], p);
p += new_len; p += new_len;
} }
i++; i++;

View File

@ -29,7 +29,14 @@ enum phantom_point_index_t
struct Glyph struct Glyph
{ {
enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE, VAR_COMPOSITE }; enum glyph_type_t {
EMPTY,
SIMPLE,
COMPOSITE,
#ifndef HB_NO_VAR_COMPOSITES
VAR_COMPOSITE,
#endif
};
public: public:
composite_iter_t get_composite_iterator () const composite_iter_t get_composite_iterator () const
@ -39,15 +46,23 @@ struct Glyph
} }
var_composite_iter_t get_var_composite_iterator () const var_composite_iter_t get_var_composite_iterator () const
{ {
#ifndef HB_NO_VAR_COMPOSITES
if (type != VAR_COMPOSITE) return var_composite_iter_t (); if (type != VAR_COMPOSITE) return var_composite_iter_t ();
return VarCompositeGlyph (*header, bytes).iter (); return VarCompositeGlyph (*header, bytes).iter ();
#else
return var_composite_iter_t ();
#endif
} }
const hb_bytes_t trim_padding () const const hb_bytes_t trim_padding () const
{ {
switch (type) { switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return VarCompositeGlyph (*header, bytes).trim_padding ();
#endif
case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding ();
case EMPTY: return bytes;
default: return bytes; default: return bytes;
} }
} }
@ -55,27 +70,36 @@ struct Glyph
void drop_hints () void drop_hints ()
{ {
switch (type) { switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return; // No hinting
#endif
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return;
default: return; case EMPTY: return;
} }
} }
void set_overlaps_flag () void set_overlaps_flag ()
{ {
switch (type) { switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return; // No overlaps flag
#endif
case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
default: return; case EMPTY: return;
} }
} }
void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
{ {
switch (type) { switch (type) {
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE: return; // No hinting
#endif
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
default: return; case EMPTY: return;
} }
} }
@ -181,7 +205,7 @@ struct Glyph
hb_bytes_t &dest_start, /* IN/OUT */ hb_bytes_t &dest_start, /* IN/OUT */
hb_bytes_t &dest_end /* OUT */) hb_bytes_t &dest_end /* OUT */)
{ {
contour_point_vector_t all_points, deltas; contour_point_vector_t all_points, points_with_deltas;
unsigned composite_contours = 0; unsigned composite_contours = 0;
head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info; head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info;
unsigned *composite_contours_p = &composite_contours; unsigned *composite_contours_p = &composite_contours;
@ -195,7 +219,7 @@ struct Glyph
composite_contours_p = nullptr; composite_contours_p = nullptr;
} }
if (!get_points (font, glyf, all_points, &deltas, head_maxp_info_p, composite_contours_p, false, false)) if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
return false; return false;
// .notdef, set type to empty so we only update metrics and don't compile bytes for // .notdef, set type to empty so we only update metrics and don't compile bytes for
@ -209,11 +233,20 @@ struct Glyph
} }
//dont compile bytes when pinned at default, just recalculate bounds //dont compile bytes when pinned at default, just recalculate bounds
if (!plan->pinned_at_default) { if (!plan->pinned_at_default)
switch (type) { {
switch (type)
{
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
// TODO
dest_end = hb_bytes_t ();
break;
#endif
case COMPOSITE: case COMPOSITE:
if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start, if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
deltas, points_with_deltas,
dest_end)) dest_end))
return false; return false;
break; break;
@ -223,7 +256,7 @@ struct Glyph
dest_end)) dest_end))
return false; return false;
break; break;
default: case EMPTY:
/* set empty bytes for empty glyph /* set empty bytes for empty glyph
* do not use source glyph's pointers */ * do not use source glyph's pointers */
dest_start = hb_bytes_t (); dest_start = hb_bytes_t ();
@ -247,7 +280,7 @@ struct Glyph
template <typename accelerator_t> template <typename accelerator_t>
bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
contour_point_vector_t &all_points /* OUT */, contour_point_vector_t &all_points /* OUT */,
contour_point_vector_t *deltas = nullptr, /* OUT */ contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
head_maxp_info_t * head_maxp_info = nullptr, /* OUT */ head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
unsigned *composite_contours = nullptr, /* OUT */ unsigned *composite_contours = nullptr, /* OUT */
bool shift_points_hori = true, bool shift_points_hori = true,
@ -287,9 +320,8 @@ struct Glyph
break; break;
case COMPOSITE: case COMPOSITE:
{ {
/* pseudo component points for each component in composite glyph */ for (auto &item : get_composite_iterator ())
unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ()); if (unlikely (!item.get_points (points))) return false;
if (unlikely (!points.resize (num_points))) return false;
break; break;
} }
#ifndef HB_NO_VAR_COMPOSITES #ifndef HB_NO_VAR_COMPOSITES
@ -299,7 +331,7 @@ struct Glyph
if (unlikely (!item.get_points (points))) return false; if (unlikely (!item.get_points (points))) return false;
} }
#endif #endif
default: case EMPTY:
break; break;
} }
@ -327,17 +359,11 @@ struct Glyph
#endif #endif
; ;
phantoms[PHANTOM_LEFT].x = h_delta; phantoms[PHANTOM_LEFT].x = h_delta;
phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
phantoms[PHANTOM_TOP].y = v_orig; phantoms[PHANTOM_TOP].y = v_orig;
phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
} }
if (deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!deltas->resize (points.length))) return false;
deltas->copy_vector (points);
}
#ifndef HB_NO_VAR #ifndef HB_NO_VAR
glyf_accelerator.gvar->apply_deltas_to_points (gid, glyf_accelerator.gvar->apply_deltas_to_points (gid,
coords, coords,
@ -346,13 +372,10 @@ struct Glyph
// mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
// with child glyphs' points // with child glyphs' points
if (deltas != nullptr && depth == 0 && type == COMPOSITE) if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
{ {
for (unsigned i = 0 ; i < points.length; i++) if (unlikely (!points_with_deltas->resize (points.length))) return false;
{ points_with_deltas->copy_vector (points);
deltas->arrayZ[i].x = points.arrayZ[i].x - deltas->arrayZ[i].x;
deltas->arrayZ[i].y = points.arrayZ[i].y - deltas->arrayZ[i].y;
}
} }
switch (type) { switch (type) {
@ -373,7 +396,7 @@ struct Glyph
.get_points (font, .get_points (font,
glyf_accelerator, glyf_accelerator,
comp_points, comp_points,
deltas, points_with_deltas,
head_maxp_info, head_maxp_info,
composite_contours, composite_contours,
shift_points_hori, shift_points_hori,
@ -389,11 +412,12 @@ struct Glyph
for (unsigned int i = 0; i < PHANTOM_COUNT; i++) for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
/* Apply component transformation & translation */ float matrix[4];
item.transform_points (comp_points); contour_point_t default_trans;
item.get_transformation (matrix, default_trans);
/* Apply translation from gvar */ /* Apply component transformation & translation (with deltas applied) */
comp_points.translate (points[comp_index]); item.transform_points (comp_points, matrix, points[comp_index]);
if (item.is_anchored ()) if (item.is_anchored ())
{ {
@ -448,7 +472,7 @@ struct Glyph
.get_points (font, .get_points (font,
glyf_accelerator, glyf_accelerator,
comp_points, comp_points,
deltas, points_with_deltas,
head_maxp_info, head_maxp_info,
nullptr, nullptr,
shift_points_hori, shift_points_hori,
@ -477,7 +501,7 @@ struct Glyph
all_points.extend (phantoms); all_points.extend (phantoms);
} break; } break;
#endif #endif
default: case EMPTY:
all_points.extend (phantoms); all_points.extend (phantoms);
break; break;
} }
@ -503,6 +527,8 @@ struct Glyph
} }
hb_bytes_t get_bytes () const { return bytes; } hb_bytes_t get_bytes () const { return bytes; }
glyph_type_t get_type () const { return type; }
const GlyphHeader *get_header () const { return header; }
Glyph () : bytes (), Glyph () : bytes (),
header (bytes.as<GlyphHeader> ()), header (bytes.as<GlyphHeader> ()),
@ -518,7 +544,9 @@ struct Glyph
int num_contours = header->numberOfContours; int num_contours = header->numberOfContours;
if (unlikely (num_contours == 0)) type = EMPTY; if (unlikely (num_contours == 0)) type = EMPTY;
else if (num_contours > 0) type = SIMPLE; else if (num_contours > 0) type = SIMPLE;
#ifndef HB_NO_VAR_COMPOSITES
else if (num_contours == -2) type = VAR_COMPOSITE; else if (num_contours == -2) type = VAR_COMPOSITE;
#endif
else type = COMPOSITE; /* negative numbers */ else type = COMPOSITE; /* negative numbers */
} }
@ -526,7 +554,7 @@ struct Glyph
hb_bytes_t bytes; hb_bytes_t bytes;
const GlyphHeader *header; const GlyphHeader *header;
hb_codepoint_t gid; hb_codepoint_t gid;
unsigned type; glyph_type_t type;
}; };

View File

@ -34,6 +34,11 @@ struct SimpleGlyph
unsigned int length (unsigned int instruction_len) const unsigned int length (unsigned int instruction_len) const
{ return instruction_len_offset () + 2 + instruction_len; } { return instruction_len_offset () + 2 + instruction_len; }
bool has_instructions_length () const
{
return instruction_len_offset () + 2 <= bytes.length;
}
unsigned int instructions_length () const unsigned int instructions_length () const
{ {
unsigned int instruction_length_offset = instruction_len_offset (); unsigned int instruction_length_offset = instruction_len_offset ();
@ -94,6 +99,7 @@ struct SimpleGlyph
/* zero instruction length */ /* zero instruction length */
void drop_hints () void drop_hints ()
{ {
if (!has_instructions_length ()) return;
GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header); GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header);
(HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0; (HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0;
} }

View File

@ -27,7 +27,12 @@ struct SubsetGlyph
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
hb_bytes_t dest_glyph = dest_start.copy (c); hb_bytes_t dest_glyph = dest_start.copy (c);
dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); hb_bytes_t end_copy = dest_end.copy (c);
if (!end_copy.arrayZ || !dest_glyph.arrayZ) {
return false;
}
dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + end_copy.length);
unsigned int pad_length = use_short_loca ? padding () : 0; unsigned int pad_length = use_short_loca ? padding () : 0;
DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length); DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
@ -41,13 +46,68 @@ struct SubsetGlyph
if (unlikely (!dest_glyph.length)) return_trace (true); if (unlikely (!dest_glyph.length)) return_trace (true);
/* update components gids */ /* update components gids. */
for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
{ {
hb_codepoint_t new_gid; hb_codepoint_t new_gid;
if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
const_cast<CompositeGlyphRecord &> (_).set_gid (new_gid); const_cast<CompositeGlyphRecord &> (_).set_gid (new_gid);
} }
#ifndef HB_NO_VAR_COMPOSITES
for (auto &_ : Glyph (dest_glyph).get_var_composite_iterator ())
{
hb_codepoint_t new_gid;
if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
const_cast<VarCompositeGlyphRecord &> (_).set_gid (new_gid);
}
#endif
#ifndef HB_NO_BEYOND_64K
auto it = Glyph (dest_glyph).get_composite_iterator ();
if (it)
{
/* lower GID24 to GID16 in components if possible.
*
* TODO: VarComposite. Not as critical, since VarComposite supports
* gid24 from the first version. */
char *p = it ? (char *) &*it : nullptr;
char *q = p;
const char *end = dest_glyph.arrayZ + dest_glyph.length;
while (it)
{
auto &rec = const_cast<CompositeGlyphRecord &> (*it);
++it;
q += rec.get_size ();
rec.lower_gid_24_to_16 ();
unsigned size = rec.get_size ();
memmove (p, &rec, size);
p += size;
}
memmove (p, q, end - q);
p += end - q;
/* We want to shorten the glyph, but we can't do that without
* updating the length in the loca table, which is already
* written out :-(. So we just fill the rest of the glyph with
* harmless instructions, since that's what they will be
* interpreted as.
*
* Should move the lowering to _populate_subset_glyphs() to
* fix this issue. */
hb_memset (p, 0x7A /* TrueType instruction ROFF; harmless */, end - p);
p += end - p;
dest_glyph = hb_bytes_t (dest_glyph.arrayZ, p - (char *) dest_glyph.arrayZ);
// TODO: Padding; & trim serialized bytes.
// TODO: Update length in loca. Ugh.
}
#endif
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
Glyph (dest_glyph).drop_hints (); Glyph (dest_glyph).drop_hints ();

View File

@ -71,6 +71,14 @@ struct VarCompositeGlyphRecord
return StructAfter<const HBGlyphID16> (numAxes); return StructAfter<const HBGlyphID16> (numAxes);
} }
void set_gid (hb_codepoint_t gid)
{
if (flags & GID_IS_24BIT)
StructAfter<HBGlyphID24> (numAxes) = gid;
else
StructAfter<HBGlyphID16> (numAxes) = gid;
}
unsigned get_numAxes () const unsigned get_numAxes () const
{ {
return numAxes; return numAxes;
@ -344,6 +352,13 @@ struct VarCompositeGlyph
var_composite_iter_t iter () const var_composite_iter_t iter () const
{ return var_composite_iter_t (bytes, &StructAfter<VarCompositeGlyphRecord, GlyphHeader> (header)); } { return var_composite_iter_t (bytes, &StructAfter<VarCompositeGlyphRecord, GlyphHeader> (header)); }
const hb_bytes_t trim_padding () const
{
unsigned length = GlyphHeader::static_size;
for (auto &comp : iter ())
length += comp.get_size ();
return bytes.sub_array (0, length);
}
}; };

View File

@ -241,6 +241,8 @@ struct glyf_accelerator_t
return true; return true;
} }
public:
#ifndef HB_NO_VAR #ifndef HB_NO_VAR
struct points_aggregator_t struct points_aggregator_t
{ {
@ -304,7 +306,6 @@ struct glyf_accelerator_t
contour_point_t *get_phantoms_sink () { return phantoms; } contour_point_t *get_phantoms_sink () { return phantoms; }
}; };
public:
unsigned unsigned
get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
{ {
@ -346,6 +347,15 @@ struct glyf_accelerator_t
} }
#endif #endif
bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const
{
if (unlikely (gid >= num_glyphs)) return false;
if (is_vertical) return false; // TODO Humm, what to do here?
*lsb = glyph_for_gid (gid).get_header ()->xMin;
return true;
}
public: public:
bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
{ {

View File

@ -464,7 +464,8 @@ enum { DELETED_GLYPH = 0xFFFF };
template <typename T> template <typename T>
struct Entry struct Entry
{ {
bool sanitize (hb_sanitize_context_t *c, unsigned int count) const // This does seem like it's ever called.
bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
/* Note, we don't recurse-sanitize data because we don't access it. /* Note, we don't recurse-sanitize data because we don't access it.
@ -492,7 +493,8 @@ struct Entry
template <> template <>
struct Entry<void> struct Entry<void>
{ {
bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) const // This does seem like it's ever called.
bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (c->check_struct (this)); return_trace (c->check_struct (this));

View File

@ -38,6 +38,10 @@
/* /*
* Atomic integers and pointers. * Atomic integers and pointers.
*
* hb_atomic_int_t and hb_atomic_ptr_t are typedefs to the actual
* atomic type. They are guaranteed to be at least 32 bits wide.
* hb_atomic_int_t is signed.
*/ */

View File

@ -194,7 +194,7 @@ struct hb_bit_set_t
unsigned int end = major_start (m + 1); unsigned int end = major_start (m + 1);
do do
{ {
if (v || page) /* The v check is to optimize out the page check if v is true. */ if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */
page->set (g, v); page->set (g, v);
array = &StructAtOffsetUnaligned<T> (array, stride); array = &StructAtOffsetUnaligned<T> (array, stride);
@ -238,7 +238,7 @@ struct hb_bit_set_t
if (g < last_g) return false; if (g < last_g) return false;
last_g = g; last_g = g;
if (v || page) /* The v check is to optimize out the page check if v is true. */ if (g != INVALID && (v || page)) /* The v check is to optimize out the page check if v is true. */
page->add (g); page->add (g);
array = &StructAtOffsetUnaligned<T> (array, stride); array = &StructAtOffsetUnaligned<T> (array, stride);

View File

@ -40,6 +40,11 @@
* Buffers serve a dual role in HarfBuzz; before shaping, they hold * Buffers serve a dual role in HarfBuzz; before shaping, they hold
* the input characters that are passed to hb_shape(), and after * the input characters that are passed to hb_shape(), and after
* shaping they hold the output glyphs. * shaping they hold the output glyphs.
*
* The input buffer is a sequence of Unicode codepoints, with
* associated attributes such as direction and script. The output
* buffer is a sequence of glyphs, with associated attributes such
* as position and cluster.
**/ **/

View File

@ -30,7 +30,19 @@
#include "hb.hh" #include "hb.hh"
/* Implements a lockfree cache for int->int functions. */ /* Implements a lockfree cache for int->int functions.
*
* The cache is a fixed-size array of 16-bit or 32-bit integers.
* The key is split into two parts: the cache index and the rest.
*
* The cache index is used to index into the array. The rest is used
* to store the key and the value.
*
* The value is stored in the least significant bits of the integer.
* The key is stored in the most significant bits of the integer.
* The key is shifted by cache_bits to the left to make room for the
* value.
*/
template <unsigned int key_bits=16, template <unsigned int key_bits=16,
unsigned int value_bits=8 + 32 - key_bits, unsigned int value_bits=8 + 32 - key_bits,

View File

@ -47,6 +47,12 @@
* More precisely, a font face represents a single face in a binary font file. * More precisely, a font face represents a single face in a binary font file.
* Font faces are typically built from a binary blob and a face index. * Font faces are typically built from a binary blob and a face index.
* Font faces are used to create fonts. * Font faces are used to create fonts.
*
* A font face can be created from a binary blob using hb_face_create().
* The face index is used to select a face from a binary blob that contains
* multiple faces. For example, a binary blob that contains both a regular
* and a bold face can be used to create two font faces, one for each face
* index.
**/ **/
@ -197,7 +203,7 @@ _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
* a face index into that blob. * a face index into that blob.
* *
* The face index is used for blobs of file formats such as TTC and * The face index is used for blobs of file formats such as TTC and
* and DFont that can contain more than one face. Face indices within * DFont that can contain more than one face. Face indices within
* such collections are zero-based. * such collections are zero-based.
* *
* <note>Note: If the blob font format is not a collection, @index * <note>Note: If the blob font format is not a collection, @index

View File

@ -76,7 +76,7 @@ struct hb_face_t
if (unlikely (!reference_table_func)) if (unlikely (!reference_table_func))
return hb_blob_get_empty (); return hb_blob_get_empty ();
blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data); blob = reference_table_func (/*Oh, well.*/const_cast<hb_face_t *> (this), tag, user_data);
if (unlikely (!blob)) if (unlikely (!blob))
return hb_blob_get_empty (); return hb_blob_get_empty ();

View File

@ -59,6 +59,11 @@
* *
* HarfBuzz provides a built-in set of lightweight default * HarfBuzz provides a built-in set of lightweight default
* functions for each method in #hb_font_funcs_t. * functions for each method in #hb_font_funcs_t.
*
* The default font functions are implemented in terms of the
* #hb_font_funcs_t methods of the parent font object. This allows
* client programs to override only the methods they need to, and
* otherwise inherit the parent font's implementation, if any.
**/ **/

View File

@ -290,8 +290,7 @@ struct hb_hashmap_t
uint32_t hash () const uint32_t hash () const
{ {
return return
+ hb_iter (items, size ()) + iter_items ()
| hb_filter (&item_t::is_real)
| hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u) | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u)
; ;
} }
@ -322,41 +321,40 @@ struct hb_hashmap_t
* Iterator * Iterator
*/ */
auto iter_ref () const HB_AUTO_RETURN auto iter_items () const HB_AUTO_RETURN
( (
+ hb_iter (items, size ()) + hb_iter (items, size ())
| hb_filter (&item_t::is_real) | hb_filter (&item_t::is_real)
)
auto iter_ref () const HB_AUTO_RETURN
(
+ iter_items ()
| hb_map (&item_t::get_pair_ref) | hb_map (&item_t::get_pair_ref)
) )
auto iter () const HB_AUTO_RETURN auto iter () const HB_AUTO_RETURN
( (
+ hb_iter (items, size ()) + iter_items ()
| hb_filter (&item_t::is_real)
| hb_map (&item_t::get_pair) | hb_map (&item_t::get_pair)
) )
auto keys_ref () const HB_AUTO_RETURN auto keys_ref () const HB_AUTO_RETURN
( (
+ hb_iter (items, size ()) + iter_items ()
| hb_filter (&item_t::is_real)
| hb_map (&item_t::key) | hb_map (&item_t::key)
) )
auto keys () const HB_AUTO_RETURN auto keys () const HB_AUTO_RETURN
( (
+ hb_iter (items, size ()) + iter_items ()
| hb_filter (&item_t::is_real)
| hb_map (&item_t::key) | hb_map (&item_t::key)
| hb_map (hb_ridentity) | hb_map (hb_ridentity)
) )
auto values_ref () const HB_AUTO_RETURN auto values_ref () const HB_AUTO_RETURN
( (
+ hb_iter (items, size ()) + iter_items ()
| hb_filter (&item_t::is_real)
| hb_map (&item_t::value) | hb_map (&item_t::value)
) )
auto values () const HB_AUTO_RETURN auto values () const HB_AUTO_RETURN
( (
+ hb_iter (items, size ()) + iter_items ()
| hb_filter (&item_t::is_real)
| hb_map (&item_t::value) | hb_map (&item_t::value)
| hb_map (hb_ridentity) | hb_map (hb_ridentity)
) )

View File

@ -413,7 +413,7 @@ hb_ot_get_glyph_extents (hb_font_t *font,
if (ot_face->sbix->get_extents (font, glyph, extents)) return true; if (ot_face->sbix->get_extents (font, glyph, extents)) return true;
if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
#endif #endif
#if !defined(HB_NO_COLOR) #if !defined(HB_NO_COLOR) && !defined(HB_NO_PAINT)
if (ot_face->COLR->get_extents (font, glyph, extents)) return true; if (ot_face->COLR->get_extents (font, glyph, extents)) return true;
#endif #endif
if (ot_face->glyf->get_extents (font, glyph, extents)) return true; if (ot_face->glyf->get_extents (font, glyph, extents)) return true;
@ -633,20 +633,4 @@ hb_ot_font_set_funcs (hb_font_t *font)
_hb_ot_font_destroy); _hb_ot_font_destroy);
} }
#ifndef HB_NO_VAR
bool
_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical,
int *lsb)
{
return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb);
}
unsigned
_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
{
return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical);
}
#endif
#endif #endif

View File

@ -76,7 +76,7 @@ struct DeviceRecord
HBUINT8 maxWidth; /* Maximum width. */ HBUINT8 maxWidth; /* Maximum width. */
UnsizedArrayOf<HBUINT8> widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */ UnsizedArrayOf<HBUINT8> widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */
public: public:
DEFINE_SIZE_ARRAY (2, widthsZ); DEFINE_SIZE_UNBOUNDED (2);
}; };
@ -87,14 +87,6 @@ struct hdmx
unsigned int get_size () const unsigned int get_size () const
{ return min_size + numRecords * sizeDeviceRecord; } { return min_size + numRecords * sizeDeviceRecord; }
const DeviceRecord& operator [] (unsigned int i) const
{
/* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed.
* https://github.com/harfbuzz/harfbuzz/issues/1300 */
if (unlikely (i >= numRecords)) return Null (DeviceRecord);
return StructAtOffset<DeviceRecord> (&this->firstDeviceRecord, i * sizeDeviceRecord);
}
template<typename Iterator, template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))> hb_requires (hb_is_iterator (Iterator))>
bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it)

View File

@ -63,7 +63,25 @@ struct head
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
return_trace (serialize (c->serializer)); head *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
if (c->plan->normalized_coords)
{
if (unlikely (!c->serializer->check_assign (out->xMin, c->plan->head_maxp_info.xMin,
HB_SERIALIZE_ERROR_INT_OVERFLOW)))
return_trace (false);
if (unlikely (!c->serializer->check_assign (out->xMax, c->plan->head_maxp_info.xMax,
HB_SERIALIZE_ERROR_INT_OVERFLOW)))
return_trace (false);
if (unlikely (!c->serializer->check_assign (out->yMin, c->plan->head_maxp_info.yMin,
HB_SERIALIZE_ERROR_INT_OVERFLOW)))
return_trace (false);
if (unlikely (!c->serializer->check_assign (out->yMax, c->plan->head_maxp_info.yMax,
HB_SERIALIZE_ERROR_INT_OVERFLOW)))
return_trace (false);
}
return_trace (true);
} }
enum mac_style_flag_t { enum mac_style_flag_t {

View File

@ -50,6 +50,9 @@ _glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gly
HB_INTERNAL unsigned HB_INTERNAL unsigned
_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); _glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
HB_INTERNAL bool
_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb);
namespace OT { namespace OT {
@ -92,7 +95,7 @@ struct hmtxvmtx
unsigned int length; unsigned int length;
H *table = (H *) hb_blob_get_data (dest_blob, &length); H *table = (H *) hb_blob_get_data (dest_blob, &length);
table->numberOfLongMetrics = num_hmetrics; c->serializer->check_assign (table->numberOfLongMetrics, num_hmetrics, HB_SERIALIZE_ERROR_INT_OVERFLOW);
#ifndef HB_NO_VAR #ifndef HB_NO_VAR
if (c->plan->normalized_coords) if (c->plan->normalized_coords)
@ -165,12 +168,19 @@ struct hmtxvmtx
lm.sb = _.second; lm.sb = _.second;
if (unlikely (!c->embed<LongMetric> (&lm))) return; if (unlikely (!c->embed<LongMetric> (&lm))) return;
} }
else else if (idx < 0x10000u)
{ {
FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size); FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size);
if (unlikely (!sb)) return; if (unlikely (!sb)) return;
*sb = _.second; *sb = _.second;
} }
else
{
// TODO: This does not do tail optimization.
UFWORD *adv = c->allocate_size<UFWORD> (UFWORD::static_size);
if (unlikely (!adv)) return;
*adv = _.first;
}
idx++; idx++;
} }
} }
@ -189,7 +199,7 @@ struct hmtxvmtx
/* Determine num_long_metrics to encode. */ /* Determine num_long_metrics to encode. */
auto& plan = c->plan; auto& plan = c->plan;
num_long_metrics = plan->num_output_glyphs (); num_long_metrics = hb_min (plan->num_output_glyphs (), 0xFFFFu);
unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx); unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx);
while (num_long_metrics > 1 && while (num_long_metrics > 1 &&
last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx)) last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx))
@ -208,7 +218,8 @@ struct hmtxvmtx
if (!c->plan->old_gid_for_new_gid (_, &old_gid)) if (!c->plan->old_gid_for_new_gid (_, &old_gid))
return hb_pair (0u, 0); return hb_pair (0u, 0);
int lsb = 0; int lsb = 0;
(void) _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb); if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
(void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb);
return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb);
} }
return mtx_map->get (_); return mtx_map->get (_);

View File

@ -64,6 +64,8 @@ using OT::Layout::GPOS;
* @include: hb-ot.h * @include: hb-ot.h
* *
* Functions for querying OpenType Layout features in the font face. * Functions for querying OpenType Layout features in the font face.
* See the <ulink url="http://www.microsoft.com/typography/otspec/">OpenType
* specification</ulink> for details.
**/ **/

View File

@ -100,7 +100,7 @@ struct maxp
maxp *maxp_prime = c->serializer->embed (this); maxp *maxp_prime = c->serializer->embed (this);
if (unlikely (!maxp_prime)) return_trace (false); if (unlikely (!maxp_prime)) return_trace (false);
maxp_prime->numGlyphs = c->plan->num_output_glyphs (); maxp_prime->numGlyphs = hb_min (c->plan->num_output_glyphs (), 0xFFFFu);
if (maxp_prime->version.major == 1) if (maxp_prime->version.major == 1)
{ {
const maxpV1Tail *src_v1 = &StructAfter<maxpV1Tail> (*this); const maxpV1Tail *src_v1 = &StructAfter<maxpV1Tail> (*this);

View File

@ -181,6 +181,4 @@ hb_ot_name_get_utf32 (hb_face_t *face,
return hb_ot_name_get_utf<hb_utf32_t> (face, name_id, language, text_size, text); return hb_ot_name_get_utf<hb_utf32_t> (face, name_id, language, text_size, text);
} }
#include "hb-ot-name-language-static.hh"
#endif #endif

View File

@ -1067,12 +1067,15 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
base = i; base = i;
while (base < end && is_halant (info[base])) while (base < end && is_halant (info[base]))
base++; base++;
info[base].indic_position() = POS_BASE_C; if (base < end)
info[base].indic_position() = POS_BASE_C;
try_pref = false; try_pref = false;
} }
break; break;
} }
if (base == end)
break;
} }
/* For Malayalam, skip over unformed below- (but NOT post-) forms. */ /* For Malayalam, skip over unformed below- (but NOT post-) forms. */
if (buffer->props.script == HB_SCRIPT_MALAYALAM) if (buffer->props.script == HB_SCRIPT_MALAYALAM)

View File

@ -222,18 +222,20 @@ struct DeltaSetIndexMap
struct VarStoreInstancer struct VarStoreInstancer
{ {
VarStoreInstancer (const VariationStore &varStore, VarStoreInstancer (const VariationStore *varStore,
const DeltaSetIndexMap &varIdxMap, const DeltaSetIndexMap *varIdxMap,
hb_array_t<int> coords) : hb_array_t<int> coords) :
varStore (varStore), varIdxMap (varIdxMap), coords (coords) {} varStore (varStore), varIdxMap (varIdxMap), coords (coords) {}
operator bool () const { return bool (coords); } operator bool () const { return varStore && bool (coords); }
/* according to the spec, if colr table has varStore but does not have
* varIdxMap, then an implicit identity mapping is used */
float operator() (uint32_t varIdx, unsigned short offset = 0) const float operator() (uint32_t varIdx, unsigned short offset = 0) const
{ return varStore.get_delta (varIdxMap.map (VarIdx::add (varIdx, offset)), coords); } { return varStore->get_delta (varIdxMap ? varIdxMap->map (VarIdx::add (varIdx, offset)) : varIdx + offset, coords); }
const VariationStore &varStore; const VariationStore *varStore;
const DeltaSetIndexMap &varIdxMap; const DeltaSetIndexMap *varIdxMap;
hb_array_t<int> coords; hb_array_t<int> coords;
}; };
@ -358,9 +360,12 @@ struct TupleVariationData
{ {
unsigned total_size = min_size; unsigned total_size = min_size;
unsigned count = tupleVarCount; unsigned count = tupleVarCount;
const TupleVariationHeader& tuple_var_header = get_tuple_var_header(); const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header());
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
total_size += tuple_var_header.get_size (axis_count) + tuple_var_header.get_data_size (); {
total_size += tuple_var_header->get_size (axis_count) + tuple_var_header->get_data_size ();
tuple_var_header = &tuple_var_header->get_next (axis_count);
}
return total_size; return total_size;
} }

View File

@ -265,6 +265,9 @@ struct HVARVVAR
rsbMap.sanitize (c, this)); rsbMap.sanitize (c, this));
} }
const VariationStore& get_var_store () const
{ return this+varStore; }
void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const
{ {
index_maps.push (&(this+advMap)); index_maps.push (&(this+advMap));

View File

@ -616,7 +616,7 @@ typedef enum {
HB_PAINT_COMPOSITE_MODE_HSL_HUE, HB_PAINT_COMPOSITE_MODE_HSL_HUE,
HB_PAINT_COMPOSITE_MODE_HSL_SATURATION, HB_PAINT_COMPOSITE_MODE_HSL_SATURATION,
HB_PAINT_COMPOSITE_MODE_HSL_COLOR, HB_PAINT_COMPOSITE_MODE_HSL_COLOR,
HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY, HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY
} hb_paint_composite_mode_t; } hb_paint_composite_mode_t;
/** /**

View File

@ -29,7 +29,16 @@
#include "hb.hh" #include "hb.hh"
/* Memory pool for persistent allocation of small objects. */ /* Memory pool for persistent allocation of small objects.
*
* Some AI musings on this, not necessarily true:
*
* This is a very simple implementation, but it's good enough for our
* purposes. It's not thread-safe. It's not very fast. It's not
* very memory efficient. It's not very cache efficient. It's not
* very anything efficient. But it's simple and it works. And it's
* good enough for our purposes. If you need something more
* sophisticated, use a real allocator. Or use a real language. */
template <typename T, unsigned ChunkLen = 32> template <typename T, unsigned ChunkLen = 32>
struct hb_pool_t struct hb_pool_t

View File

@ -35,6 +35,12 @@
* *
* Priority queue implemented as a binary heap. Supports extract minimum * Priority queue implemented as a binary heap. Supports extract minimum
* and insert operations. * and insert operations.
*
* The priority queue is implemented as a binary heap, which is a complete
* binary tree. The root of the tree is the minimum element. The heap
* property is that the priority of a node is less than or equal to the
* priority of its children. The heap is stored in an array, with the
* children of node i stored at indices 2i + 1 and 2i + 2.
*/ */
struct hb_priority_queue_t struct hb_priority_queue_t
{ {

View File

@ -36,9 +36,11 @@
#include "OT/Color/COLR/COLR.hh" #include "OT/Color/COLR/COLR.hh"
#include "hb-ot-glyf-table.hh" #include "hb-ot-glyf-table.hh"
#include "hb-ot-head-table.hh" #include "hb-ot-head-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-maxp-table.hh" #include "hb-ot-maxp-table.hh"
#ifndef HB_NO_VISIBILITY #ifndef HB_NO_VISIBILITY
#include "hb-ot-name-language-static.hh"
uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {};
/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; /*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {};
@ -108,4 +110,26 @@ hb_face_t::load_upem () const
} }
#ifndef HB_NO_VAR
bool
_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical,
int *lsb)
{
return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb);
}
unsigned
_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
{
return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical);
}
#endif
bool
_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb)
{
return face->table.glyf->get_leading_bearing_without_var_unscaled (gid, is_vertical, lsb);
}
#endif #endif

View File

@ -125,7 +125,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false)
return result_t{}; // No overlap return result_t{}; // No overlap
/* case 2: Only the peak and outermost bound fall outside the new limit; /* case 2: Only the peak and outermost bound fall outside the new limit;
* we keep the deltaset, update peak and outermost bound and and scale deltas * we keep the deltaset, update peak and outermost bound and scale deltas
* by the scalar value for the restricted axis at the new limit, and solve * by the scalar value for the restricted axis at the new limit, and solve
* recursively. * recursively.
* *

View File

@ -36,6 +36,7 @@
#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-cff1-table.hh" #include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "OT/Color/COLR/COLR.hh" #include "OT/Color/COLR/COLR.hh"
#include "OT/Color/COLR/colrv1-closure.hh" #include "OT/Color/COLR/colrv1-closure.hh"
#include "OT/Color/CPAL/CPAL.hh" #include "OT/Color/CPAL/CPAL.hh"
@ -294,7 +295,7 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
feature_record_cond_idx_map, feature_record_cond_idx_map,
feature_substitutes_map); feature_substitutes_map);
if (table_tag == HB_OT_TAG_GSUB) if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE))
hb_ot_layout_lookups_substitute_closure (plan->source, hb_ot_layout_lookups_substitute_closure (plan->source,
&lookup_indices, &lookup_indices,
gids_to_retain); gids_to_retain);
@ -346,7 +347,10 @@ _get_hb_font_with_variations (const hb_subset_plan_t *plan)
hb_font_t *font = hb_font_create (plan->source); hb_font_t *font = hb_font_create (plan->source);
hb_vector_t<hb_variation_t> vars; hb_vector_t<hb_variation_t> vars;
vars.alloc (plan->user_axes_location.get_population ()); if (!vars.alloc (plan->user_axes_location.get_population ())) {
hb_font_destroy (font);
return nullptr;
}
for (auto _ : plan->user_axes_location) for (auto _ : plan->user_axes_location)
{ {
@ -382,7 +386,13 @@ _collect_layout_variation_indices (hb_subset_plan_t* plan)
bool collect_delta = plan->pinned_at_default ? false : true; bool collect_delta = plan->pinned_at_default ? false : true;
if (collect_delta) if (collect_delta)
{ {
font = _get_hb_font_with_variations (plan); if (unlikely (!plan->check_success (font = _get_hb_font_with_variations (plan)))) {
hb_font_destroy (font);
gdef.destroy ();
gpos.destroy ();
return;
}
if (gdef->has_var_store ()) if (gdef->has_var_store ())
{ {
var_store = &(gdef->get_var_store ()); var_store = &(gdef->get_var_store ());
@ -556,9 +566,12 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
if (plan->codepoint_to_glyph->has (cp)) if (plan->codepoint_to_glyph->has (cp))
continue; continue;
hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; hb_codepoint_t *gid;
plan->codepoint_to_glyph->set (cp, gid); if (!unicode_glyphid_map->has(cp, &gid))
plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); continue;
plan->codepoint_to_glyph->set (cp, *gid);
plan->unicode_to_new_gid_list.push (hb_pair (cp, *gid));
} }
plan->unicode_to_new_gid_list.qsort (); plan->unicode_to_new_gid_list.qsort ();
} }
@ -610,7 +623,7 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
gids_to_retain->add (gid); gids_to_retain->add (gid);
for (auto item : glyf.glyph_for_gid (gid).get_composite_iterator ()) for (auto &item : glyf.glyph_for_gid (gid).get_composite_iterator ())
operation_count = operation_count =
_glyf_add_gid_and_children (glyf, _glyf_add_gid_and_children (glyf,
item.get_gid (), item.get_gid (),
@ -618,6 +631,18 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
operation_count, operation_count,
depth); depth);
#ifndef HB_NO_VAR_COMPOSITES
for (auto &item : glyf.glyph_for_gid (gid).get_var_composite_iterator ())
{
operation_count =
_glyf_add_gid_and_children (glyf,
item.get_gid (),
gids_to_retain,
operation_count,
depth);
}
#endif
return operation_count; return operation_count;
} }
@ -632,9 +657,10 @@ _nameid_closure (hb_subset_plan_t* plan,
if (!plan->all_axes_pinned) if (!plan->all_axes_pinned)
plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->name_ids); plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->name_ids);
#endif #endif
#ifndef HB_NO_COLOR
if (!drop_tables->has (HB_OT_TAG_CPAL)) if (!drop_tables->has (HB_OT_TAG_CPAL))
plan->source->table.CPAL->collect_name_ids (&plan->colr_palettes, &plan->name_ids); plan->source->table.CPAL->collect_name_ids (&plan->colr_palettes, &plan->name_ids);
#endif
#ifndef HB_NO_SUBSET_LAYOUT #ifndef HB_NO_SUBSET_LAYOUT
if (!drop_tables->has (HB_OT_TAG_GPOS)) if (!drop_tables->has (HB_OT_TAG_GPOS))
@ -801,12 +827,15 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
bool has_avar = face->table.avar->has_data (); bool has_avar = face->table.avar->has_data ();
const OT::SegmentMaps *seg_maps = nullptr; const OT::SegmentMaps *seg_maps = nullptr;
unsigned avar_axis_count = 0;
if (has_avar) if (has_avar)
{
seg_maps = face->table.avar->get_segment_maps (); seg_maps = face->table.avar->get_segment_maps ();
avar_axis_count = face->table.avar->get_axis_count();
}
bool axis_not_pinned = false; bool axis_not_pinned = false;
unsigned old_axis_idx = 0, new_axis_idx = 0; unsigned old_axis_idx = 0, new_axis_idx = 0;
unsigned int i = 0;
for (const auto& axis : axes) for (const auto& axis : axes)
{ {
hb_tag_t axis_tag = axis.get_axis_tag (); hb_tag_t axis_tag = axis.get_axis_tag ();
@ -821,7 +850,7 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
else else
{ {
int normalized_v = axis.normalize_axis_value (plan->user_axes_location.get (axis_tag)); int normalized_v = axis.normalize_axis_value (plan->user_axes_location.get (axis_tag));
if (has_avar && old_axis_idx < face->table.avar->get_axis_count ()) if (has_avar && old_axis_idx < avar_axis_count)
{ {
normalized_v = seg_maps->map (normalized_v); normalized_v = seg_maps->map (normalized_v);
} }
@ -829,17 +858,99 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
if (normalized_v != 0) if (normalized_v != 0)
plan->pinned_at_default = false; plan->pinned_at_default = false;
plan->normalized_coords[i] = normalized_v; plan->normalized_coords[old_axis_idx] = normalized_v;
} }
if (has_avar)
seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
old_axis_idx++; old_axis_idx++;
i++; if (has_avar && old_axis_idx < avar_axis_count)
seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
} }
plan->all_axes_pinned = !axis_not_pinned; plan->all_axes_pinned = !axis_not_pinned;
} }
static void
_update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
{
if (!plan->normalized_coords) return;
OT::cff2::accelerator_t cff2 (plan->source);
if (!cff2.is_valid ()) return;
hb_font_t *font = nullptr;
if (unlikely (!plan->check_success (font = _get_hb_font_with_variations (plan))))
{
hb_font_destroy (font);
return;
}
hb_glyph_extents_t extents = {0x7FFF, -0x7FFF};
OT::hmtx_accelerator_t _hmtx (plan->source);
float *hvar_store_cache = nullptr;
if (_hmtx.has_data () && _hmtx.var_table.get_length ())
hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache ();
OT::vmtx_accelerator_t _vmtx (plan->source);
float *vvar_store_cache = nullptr;
if (_vmtx.has_data () && _vmtx.var_table.get_length ())
vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache ();
for (auto p : *plan->glyph_map)
{
hb_codepoint_t old_gid = p.first;
hb_codepoint_t new_gid = p.second;
if (!cff2.get_extents (font, old_gid, &extents)) continue;
bool has_bounds_info = true;
if (extents.x_bearing == 0 && extents.width == 0 &&
extents.height == 0 && extents.y_bearing == 0)
has_bounds_info = false;
if (has_bounds_info)
{
plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing);
plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width);
plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing);
plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height);
}
if (_hmtx.has_data ())
{
int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid);
if (_hmtx.var_table.get_length ())
hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
hvar_store_cache));
int lsb = extents.x_bearing;
if (!has_bounds_info)
{
if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
continue;
}
plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
plan->bounds_width_map.set (new_gid, extents.width);
}
if (_vmtx.has_data ())
{
int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid);
if (_vmtx.var_table.get_length ())
vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
vvar_store_cache));
int tsb = extents.y_bearing;
if (!has_bounds_info)
{
if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb))
continue;
}
plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
plan->bounds_height_map.set (new_gid, extents.height);
}
}
hb_font_destroy (font);
if (hvar_store_cache)
_hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache);
if (vvar_store_cache)
_vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache);
}
#endif #endif
hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
@ -902,6 +1013,8 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
_populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this);
_populate_gids_to_retain (this, input->sets.drop_tables); _populate_gids_to_retain (this, input->sets.drop_tables);
if (unlikely (in_error ()))
return;
_create_old_gid_to_new_gid_map (face, _create_old_gid_to_new_gid_map (face,
input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS, input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
@ -926,6 +1039,10 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
if (unlikely (in_error ())) if (unlikely (in_error ()))
return; return;
#ifndef HB_NO_VAR
_update_instance_metrics_map_from_cff2 (this);
#endif
if (attach_accelerator_data) if (attach_accelerator_data)
{ {
hb_multimap_t gid_to_unicodes; hb_multimap_t gid_to_unicodes;

View File

@ -211,7 +211,7 @@ struct hb_subset_plan_t
template<typename T> template<typename T>
hb_blob_ptr_t<T> source_table() hb_blob_ptr_t<T> source_table()
{ {
hb_lock_t (accelerator ? &accelerator->sanitized_table_cache_lock : nullptr); hb_lock_t lock (accelerator ? &accelerator->sanitized_table_cache_lock : nullptr);
auto *cache = accelerator ? &accelerator->sanitized_table_cache : &sanitized_table_cache; auto *cache = accelerator ? &accelerator->sanitized_table_cache : &sanitized_table_cache;
if (cache if (cache

View File

@ -637,8 +637,3 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
end: end:
return success ? hb_face_reference (plan->dest) : nullptr; return success ? hb_face_reference (plan->dest) : nullptr;
} }
#ifndef HB_NO_VISIBILITY
/* If NO_VISIBILITY, libharfbuzz has this. */
#include "hb-ot-name-language-static.hh"
#endif

View File

@ -71,6 +71,8 @@ typedef struct hb_subset_plan_t hb_subset_plan_t;
* in the final subset. * in the final subset.
* @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in
* OS/2 will not be recalculated. * OS/2 will not be recalculated.
* @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set don't perform glyph closure on layout
* substitution rules (GSUB).
* *
* List of boolean properties that can be configured on the subset input. * List of boolean properties that can be configured on the subset input.
* *
@ -87,6 +89,7 @@ typedef enum { /*< flags >*/
HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u, HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u,
HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u, HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u,
HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u,
HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u,
} hb_subset_flags_t; } hb_subset_flags_t;
/** /**

View File

@ -137,5 +137,29 @@ main (int argc, char **argv)
assert (s.has (HB_SET_VALUE_INVALID)); assert (s.has (HB_SET_VALUE_INVALID));
} }
/* Adding HB_SET_VALUE_INVALID */
{
hb_set_t s;
s.add(HB_SET_VALUE_INVALID);
assert(!s.has(HB_SET_VALUE_INVALID));
s.clear();
assert(!s.add_range(HB_SET_VALUE_INVALID - 2, HB_SET_VALUE_INVALID));
assert(!s.has(HB_SET_VALUE_INVALID));
hb_codepoint_t array[] = {(unsigned) HB_SET_VALUE_INVALID, 0, 2};
s.clear();
s.add_array(array, 3);
assert(!s.has(HB_SET_VALUE_INVALID));
assert(s.has(2));
hb_codepoint_t sorted_array[] = {0, 2, (unsigned) HB_SET_VALUE_INVALID};
s.clear();
s.add_sorted_array(sorted_array, 3);
assert(!s.has(HB_SET_VALUE_INVALID));
assert(s.has(2));
}
return 0; return 0;
} }

View File

@ -10,6 +10,7 @@ EXTRA_DIST += \
$(DISABLED_TESTS) \ $(DISABLED_TESTS) \
expected/32bit_var_store \ expected/32bit_var_store \
expected/basics \ expected/basics \
expected/preprocess \
expected/full-font \ expected/full-font \
expected/glyf_bug_3131 \ expected/glyf_bug_3131 \
expected/cff-full-font \ expected/cff-full-font \
@ -43,6 +44,7 @@ EXTRA_DIST += \
expected/layout.duplicate_features \ expected/layout.duplicate_features \
expected/layout.unsorted_featurelist \ expected/layout.unsorted_featurelist \
expected/layout.drop_feature \ expected/layout.drop_feature \
expected/no_layout_closure \
expected/cmap \ expected/cmap \
expected/cmap14 \ expected/cmap14 \
expected/sbix \ expected/sbix \
@ -68,6 +70,8 @@ EXTRA_DIST += \
expected/post_apply_mvar_delta \ expected/post_apply_mvar_delta \
expected/apply_cvar_delta \ expected/apply_cvar_delta \
expected/collect_name_ids \ expected/collect_name_ids \
expected/instantiate_colrv1 \
expected/instantiate_cff2_update_metrics \
fonts \ fonts \
profiles \ profiles \
$(NULL) $(NULL)

View File

@ -1,6 +1,7 @@
TESTS = \ TESTS = \
tests/32bit_var_store.tests \ tests/32bit_var_store.tests \
tests/basics.tests \ tests/basics.tests \
tests/preprocess.tests \
tests/cbdt.tests \ tests/cbdt.tests \
tests/cff-full-font.tests \ tests/cff-full-font.tests \
tests/cff-japanese.tests \ tests/cff-japanese.tests \
@ -42,6 +43,7 @@ TESTS = \
tests/layout.duplicate_features.tests \ tests/layout.duplicate_features.tests \
tests/layout.unsorted_featurelist.tests \ tests/layout.unsorted_featurelist.tests \
tests/layout.drop_feature.tests \ tests/layout.drop_feature.tests \
tests/no_layout_closure.tests \
tests/sbix.tests \ tests/sbix.tests \
tests/variable.tests \ tests/variable.tests \
tests/glyph_names.tests \ tests/glyph_names.tests \
@ -59,6 +61,8 @@ TESTS = \
tests/post_apply_mvar_delta.tests \ tests/post_apply_mvar_delta.tests \
tests/apply_cvar_delta.tests \ tests/apply_cvar_delta.tests \
tests/collect_name_ids.tests \ tests/collect_name_ids.tests \
tests/instantiate_colrv1.tests \
tests/instantiate_cff2_update_metrics.tests \
$(NULL) $(NULL)
# TODO: re-enable once colrv1 subsetting is stabilized. # TODO: re-enable once colrv1 subsetting is stabilized.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,2 @@
--no-layout-closure
--gids=74,77,446

View File

@ -0,0 +1,3 @@
--no-layout-closure
--gids=74,87,88,443,448
--layout-features+=dlig

View File

@ -1,5 +1,6 @@
FONTS: FONTS:
Comfortaa-Regular-new.ttf Comfortaa-Regular-new.ttf
Muli-ABC.ttf
PROFILES: PROFILES:
default.txt default.txt

View File

@ -0,0 +1,15 @@
FONTS:
Cantarell-VF-ABC.otf
PROFILES:
default.txt
retain-gids.txt
SUBSETS:
*
INSTANCES:
wght=800
OPTIONS:
no_fonttools

View File

@ -0,0 +1,14 @@
FONTS:
Foldit.ttf
PROFILES:
default.txt
SUBSETS:
*
INSTANCES:
wght=900
OPTIONS:
no_fonttools

View File

@ -0,0 +1,9 @@
FONTS:
Roboto-Regular.ttf
PROFILES:
no-layout-closure-gids.txt
no-layout-closure-gids2.txt
SUBSETS:
no-unicodes

View File

@ -0,0 +1,8 @@
FONTS:
Roboto-Regular.ttf
PROFILES:
gids.txt
SUBSETS:
abcベ

View File

@ -47,8 +47,10 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
args.extend(["--drop-tables+=DSIG", args.extend(["--drop-tables+=DSIG",
"--drop-tables-=sbix", "--drop-tables-=sbix",
"--no-harfbuzz-repacker", # disable harfbuzz repacker so we aren't comparing to ourself. "--no-harfbuzz-repacker", # disable harfbuzz repacker so we aren't comparing to ourself.
"--unicodes=%s" % unicodes,
"--output-file=%s" % fonttools_path]) "--output-file=%s" % fonttools_path])
if unicodes != "":
args.extend(["--unicodes=%s" % unicodes,])
args.extend(profile_flags) args.extend(profile_flags)
if not no_fonttools: if not no_fonttools:
check_call(args) check_call(args)
@ -63,9 +65,10 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
hb_subset, hb_subset,
"--font-file=" + input_file, "--font-file=" + input_file,
"--output-file=" + harfbuzz_path, "--output-file=" + harfbuzz_path,
"--unicodes=%s" % unicodes,
"--drop-tables+=DSIG", "--drop-tables+=DSIG",
"--drop-tables-=sbix"] "--drop-tables-=sbix"]
if unicodes != "":
args.extend(["--unicodes=%s" % unicodes,])
args.extend(profile_flags) args.extend(profile_flags)
if instance_flags: if instance_flags:
args.extend(["--instance=%s" % ','.join(instance_flags)]) args.extend(["--instance=%s" % ','.join(instance_flags)])

View File

@ -1,5 +1,6 @@
tests = [ tests = [
'basics', 'basics',
'preprocess',
'full-font', 'full-font',
'cff-full-font', 'cff-full-font',
'japanese', 'japanese',
@ -33,6 +34,7 @@ tests = [
'layout.duplicate_features', 'layout.duplicate_features',
'layout.unsorted_featurelist', 'layout.unsorted_featurelist',
'layout.drop_feature', 'layout.drop_feature',
'no_layout_closure',
'cmap', 'cmap',
'cmap14', 'cmap14',
'sbix', 'sbix',
@ -61,6 +63,8 @@ tests = [
'post_apply_mvar_delta', 'post_apply_mvar_delta',
'apply_cvar_delta', 'apply_cvar_delta',
'collect_name_ids', 'collect_name_ids',
'instantiate_colrv1',
'instantiate_cff2_update_metrics',
] ]
repack_tests = [ repack_tests = [

View File

@ -16,6 +16,8 @@ class Test:
import re import re
if self.subset == '*': if self.subset == '*':
return self.subset[0] return self.subset[0]
elif self.subset == "no-unicodes":
return ""
elif re.match("^U\+", self.subset): elif re.match("^U\+", self.subset):
s = re.sub (r"U\+", "", self.subset) s = re.sub (r"U\+", "", self.subset)
return s return s
@ -49,6 +51,11 @@ class Test:
profile_name, profile_name,
self.instance_name(), self.instance_name(),
font_base_name_parts[1]) font_base_name_parts[1])
elif self.unicodes() == "":
return "%s.%s.no-unicodes%s%s" % (font_base_name_parts[0],
profile_name,
self.instance_name(),
font_base_name_parts[1])
else: else:
return "%s.%s.%s%s%s" % (font_base_name_parts[0], return "%s.%s.%s%s%s" % (font_base_name_parts[0],
profile_name, profile_name,

View File

@ -941,6 +941,7 @@ subset_main_t::add_options ()
{"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG>, "Set the overlaps flag on each glyph.", nullptr}, {"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG>, "Set the overlaps flag on each glyph.", nullptr},
{"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NOTDEF_OUTLINE>, "Keep the outline of \'.notdef\' glyph", nullptr}, {"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NOTDEF_OUTLINE>, "Keep the outline of \'.notdef\' glyph", nullptr},
{"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES>, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr}, {"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES>, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr},
{"no-layout-closure", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE>, "Don't perform glyph closure for layout substitution (GSUB).", nullptr},
{"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_GLYPH_NAMES>, "Keep PS glyph names in TT-flavored fonts. ", nullptr}, {"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_GLYPH_NAMES>, "Keep PS glyph names in TT-flavored fonts. ", nullptr},
{"passthrough-tables", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED>, "Do not drop tables that the tool does not know how to subset.", nullptr}, {"passthrough-tables", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED>, "Do not drop tables that the tool does not know how to subset.", nullptr},
{"preprocess-face", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &this->preprocess, {"preprocess-face", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &this->preprocess,