Skip to content

Commit 9261f9a

Browse files
committed
Update ExtractAllGlyphConditions() to follow closure phases.
So that produced conditions respect constraints from closure phase order.
1 parent c82db2c commit 9261f9a

File tree

7 files changed

+284
-93
lines changed

7 files changed

+284
-93
lines changed

ift/common/font_helper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class FontHelper {
5151
constexpr static hb_tag_t kCFF2 = HB_TAG('C', 'F', 'F', '2');
5252
constexpr static hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
5353
constexpr static hb_tag_t kGPOS = HB_TAG('G', 'P', 'O', 'S');
54+
constexpr static hb_tag_t kCmap = HB_TAG('c', 'm', 'a', 'p');
55+
constexpr static hb_tag_t kCOLR = HB_TAG('C', 'O', 'L', 'R');
56+
constexpr static hb_tag_t kMATH = HB_TAG('M', 'A', 'T', 'H');
5457

5558
template <typename int_type_t>
5659
static bool WillIntOverflow(int64_t value) {

ift/dep_graph/dependency_graph.cc

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,7 @@ using ift::encoder::SubsetDefinition;
3939

4040
namespace ift::dep_graph {
4141

42-
static constexpr hb_tag_t cmap = HB_TAG('c', 'm', 'a', 'p');
43-
static constexpr hb_tag_t glyf = HB_TAG('g', 'l', 'y', 'f');
44-
static constexpr hb_tag_t GSUB = HB_TAG('G', 'S', 'U', 'B');
45-
static constexpr hb_tag_t COLR = HB_TAG('C', 'O', 'L', 'R');
46-
static constexpr hb_tag_t MATH = HB_TAG('M', 'A', 'T', 'H');
47-
static constexpr hb_tag_t CFF = HB_TAG('C', 'F', 'F', ' ');
48-
49-
static StatusOr<GlyphSet> GetContextSet(
42+
StatusOr<GlyphSet> GetContextSet(
5043
hb_depend_t* depend, const ift::common::GlyphSet* full_closure,
5144
hb_codepoint_t context_set_id) {
5245
// the context set is actually a set of sets.
@@ -89,7 +82,7 @@ class TraversalContext {
8982
hb_depend_t* depend = nullptr;
9083

9184
// Only edges from these tables will be followed.
92-
flat_hash_set<hb_tag_t> table_filter = {cmap, glyf, GSUB, COLR, MATH, CFF};
85+
flat_hash_set<hb_tag_t> table_filter = {FontHelper::kCmap, FontHelper::kGlyf, FontHelper::kGSUB, FontHelper::kCOLR, FontHelper::kMATH, FontHelper::kCFF};
9386

9487
// Only edges that originate from and end at glyphs from this filter will be
9588
// followed.
@@ -226,14 +219,14 @@ class TraversalContext {
226219

227220
PendingEdge edge = PendingEdge::Uvs(a, b, gid);
228221
Node dest = Node::Glyph(gid);
229-
return TraverseEdgeTo(dest, edge, cmap);
222+
return TraverseEdgeTo(dest, edge, FontHelper::kCmap);
230223
}
231224

232225
Status TraverseGsubEdgeTo(glyph_id_t source_gid, glyph_id_t dest_gid,
233226
hb_tag_t feature) {
234227
PendingEdge edge = PendingEdge::Gsub(source_gid, feature, dest_gid);
235228
Node dest = Node::Glyph(dest_gid);
236-
return TraverseEdgeTo(dest, edge, GSUB);
229+
return TraverseEdgeTo(dest, edge, FontHelper::kGSUB);
237230
}
238231

239232
Status TraverseContextualEdgeTo(glyph_id_t source_gid, glyph_id_t dest_gid,
@@ -247,7 +240,7 @@ class TraversalContext {
247240
PendingEdge edge =
248241
PendingEdge::Context(source_gid, feature, dest_gid, context_set);
249242
Node dest = Node::Glyph(dest_gid);
250-
return TraverseEdgeTo(dest, edge, GSUB);
243+
return TraverseEdgeTo(dest, edge, FontHelper::kGSUB);
251244
}
252245

253246
Status TraverseLigatureEdgeTo(glyph_id_t source_gid, glyph_id_t dest_gid,
@@ -263,7 +256,7 @@ class TraversalContext {
263256
PendingEdge edge =
264257
PendingEdge::Ligature(source_gid, feature, dest_gid, liga_set_index);
265258
Node dest = Node::Glyph(dest_gid);
266-
return TraverseEdgeTo(dest, edge, GSUB);
259+
return TraverseEdgeTo(dest, edge, FontHelper::kGSUB);
267260
}
268261

269262
void Reached(Node node) {
@@ -456,7 +449,7 @@ static Status DoTraversal(const PendingEdge& edge,
456449
TraversalContext<CallbackT>& context) {
457450
context.callback.VisitPending(edge);
458451

459-
if (edge.table_tag == GSUB && edge.required_feature.has_value()) {
452+
if (edge.table_tag == FontHelper::kGSUB && edge.required_feature.has_value()) {
460453
if (edge.required_context_set_index.has_value()) {
461454
GlyphSet context_glyphs =
462455
TRY(GetContextSet(context.depend, context.full_closure,
@@ -531,7 +524,10 @@ absl::Status DependencyGraph::HandleOutgoingEdges(
531524
return absl::OkStatus();
532525
}
533526

534-
StatusOr<std::vector<Node>> DependencyGraph::TopologicalSorting() const {
527+
StatusOr<std::vector<Node>> DependencyGraph::TopologicalSorting(
528+
const flat_hash_set<hb_tag_t>& table_filter,
529+
uint32_t node_type_filter
530+
) const {
535531
struct TopoCallback {
536532
std::vector<Node> edges;
537533
void Visit(Node dest) {}
@@ -560,6 +556,8 @@ StatusOr<std::vector<Node>> DependencyGraph::TopologicalSorting() const {
560556
context.unicode_filter = &non_init_font_codepoints;
561557
context.glyph_filter = &non_init_font_glyphs;
562558
context.feature_filter = &full_feature_set_;
559+
context.table_filter = table_filter;
560+
context.node_type_filter = node_type_filter;
563561

564562
// DFS topological sorting algorithm from:
565563
// https://en.wikipedia.org/wiki/Topological_sorting
@@ -703,6 +701,8 @@ StatusOr<Traversal> DependencyGraph::ClosureTraversal(
703701
// 6. glyf closure
704702
// 7. CFF closure
705703
//
704+
// TODO(garretrieger): can we encode the closure phases in a table/array?
705+
//
706706
// Reference for the phases and ordering:
707707
// _populate_gids_to_retain() from
708708
// https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-subset-plan.cc#L439
@@ -726,53 +726,31 @@ StatusOr<Traversal> DependencyGraph::ClosureTraversal(
726726
TraversalContext context = base_context;
727727
context.SetReached(nodes);
728728
context.callback.SetStartNodes(nodes);
729-
context.table_filter = {cmap};
730-
context.node_type_filter = Node::NodeType::INIT_FONT |
731-
Node::NodeType::SEGMENT |
732-
Node::NodeType::UNICODE | Node::NodeType::GLYPH |
733-
Node::NodeType::FEATURE;
729+
context.table_filter = {DependencyGraph::kClosurePhaseTable[0]};
730+
context.node_type_filter = DependencyGraph::kClosurePhaseNodeFilter[0];
734731
traversal_full = TRY(TraverseGraph(&context));
735732
}
736733

737-
/* ### Phase 3: GSUB ### */
738-
if (table_tags.contains(GSUB)) {
739-
TRYV(ClosureSubTraversal(&base_context, GSUB, traversal_full));
740-
}
741-
742-
/* ### Phase 4: MATH ### */
743-
if (table_tags.contains(MATH)) {
744-
TRYV(ClosureSubTraversal(&base_context, MATH, traversal_full));
745-
}
746-
747-
/* ### Phase 5: COLR ### */
748-
if (table_tags.contains(COLR)) {
749-
TRYV(ClosureSubTraversal(&base_context, COLR, traversal_full));
750-
}
751-
752-
/* ### Phase 6: glyf ### */
753-
if (table_tags.contains(glyf)) {
754-
TRYV(ClosureSubTraversal(&base_context, glyf, traversal_full));
755-
}
756-
757-
/* ### Phase 7: CFF ### */
758-
if (table_tags.contains(CFF)) {
759-
TRYV(ClosureSubTraversal(&base_context, CFF, traversal_full));
734+
/* ### Remaining Phases ### */
735+
for (unsigned phase = 1; phase < DependencyGraph::kNumberOfClosurePhases; phase++) {
736+
TRYV(ClosureSubTraversal(&base_context, phase, traversal_full));
760737
}
761738

762739
return traversal_full;
763740
}
764741

765742
Status DependencyGraph::ClosureSubTraversal(
766-
const TraversalContext<ClosureState>* base_context, hb_tag_t table,
743+
const TraversalContext<ClosureState>* base_context, uint32_t phase_index,
767744
Traversal& traversal_full) const {
768745
btree_set<Node> start_nodes;
769-
for (glyph_id_t gid : traversal_full.ReachedGlyphs()) {
770-
start_nodes.insert(Node::Glyph(gid));
746+
747+
if (DependencyGraph::kClosurePhaseStartNodes[phase_index] & Node::NodeType::GLYPH) {
748+
for (glyph_id_t gid : traversal_full.ReachedGlyphs()) {
749+
start_nodes.insert(Node::Glyph(gid));
750+
}
771751
}
772752

773-
if (table == GSUB) {
774-
// For GSUB we also need to consider reached features as starting nodes
775-
// since those have outgoing GSUB edges.
753+
if (DependencyGraph::kClosurePhaseStartNodes[phase_index] & Node::NodeType::FEATURE) {
776754
for (hb_tag_t feature : traversal_full.ReachedLayoutFeatures()) {
777755
start_nodes.insert(Node::Feature(feature));
778756
}
@@ -781,8 +759,8 @@ Status DependencyGraph::ClosureSubTraversal(
781759
TraversalContext<ClosureState> context = *base_context;
782760
context.SetReached(start_nodes);
783761
context.callback.SetStartNodes(start_nodes);
784-
context.table_filter = {table};
785-
context.node_type_filter = Node::NodeType::GLYPH;
762+
context.table_filter = {DependencyGraph::kClosurePhaseTable[phase_index]};
763+
context.node_type_filter = DependencyGraph::kClosurePhaseNodeFilter[phase_index];
786764
traversal_full.Merge(TRY(TraverseGraph(&context)));
787765
return absl::OkStatus();
788766
}
@@ -881,7 +859,7 @@ Status DependencyGraph::HandleGsubGlyphOutgoingEdges(
881859
template <typename CallbackT>
882860
Status DependencyGraph::HandleFeatureOutgoingEdges(
883861
hb_tag_t feature_tag, TraversalContext<CallbackT>* context) const {
884-
if (!context->table_filter.contains(GSUB)) {
862+
if (!context->table_filter.contains(FontHelper::kGSUB)) {
885863
// All feature edges are GSUB edges so we can skip
886864
// this if GSUB is being filtered out.
887865
return absl::OkStatus();
@@ -1164,7 +1142,7 @@ DependencyGraph::ComputeFeatureEdges() const {
11641142
while (hb_depend_get_glyph_entry(
11651143
dependency_graph_.get(), gid, index++, &table_tag, &dest_gid,
11661144
&layout_tag, &ligature_set, &context_set, nullptr /* flags */)) {
1167-
if (table_tag != GSUB || layout_tag == HB_CODEPOINT_INVALID) {
1145+
if (table_tag != FontHelper::kGSUB || layout_tag == HB_CODEPOINT_INVALID) {
11681146
continue;
11691147
}
11701148

@@ -1190,7 +1168,9 @@ DependencyGraph::ComputeFeatureEdges() const {
11901168
}
11911169

11921170
StatusOr<flat_hash_map<Node, btree_set<EdgeConditonsCnf>>>
1193-
DependencyGraph::CollectIncomingEdges() const {
1171+
DependencyGraph::CollectIncomingEdges(
1172+
const flat_hash_set<hb_tag_t>& table_filter,
1173+
uint32_t node_type_filter) const {
11941174
struct IncomingEdgeCollector {
11951175
flat_hash_map<Node, btree_set<EdgeConditonsCnf>>* incoming_edges = nullptr;
11961176
const DependencyGraph* graph = nullptr;
@@ -1219,9 +1199,13 @@ DependencyGraph::CollectIncomingEdges() const {
12191199
context.glyph_filter = &segmentation_info_->FullClosure();
12201200
context.unicode_filter = &segmentation_info_->FullDefinition().codepoints;
12211201
context.enforce_context = false;
1202+
context.table_filter = table_filter;
1203+
context.node_type_filter = node_type_filter;
12221204
context.callback.incoming_edges = &incoming_edges;
12231205
context.callback.graph = this;
12241206

1207+
// TODO(garretrieger): we have a few places iterating all nodes now, consider
1208+
// adding a function to generate the all nodes set or an all nodes iterator.
12251209
btree_set<Node> all_nodes;
12261210
all_nodes.insert(Node::InitFont());
12271211
for (segment_index_t s : segmentation_info_->NonEmptySegments()) {

ift/dep_graph/dependency_graph.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#ifndef IFT_DEP_GRAPH_DEPENDENCY_GRAPH_H_
22
#define IFT_DEP_GRAPH_DEPENDENCY_GRAPH_H_
33

4+
#include <cstdint>
45
#include <memory>
56

67
#include "absl/container/btree_set.h"
78
#include "absl/container/flat_hash_map.h"
89
#include "absl/status/statusor.h"
910
#include "hb.h"
1011
#include "ift/common/font_data.h"
12+
#include "ift/common/font_helper.h"
1113
#include "ift/common/hb_set_unique_ptr.h"
1214
#include "ift/common/int_set.h"
1315
#include "ift/common/try.h"
@@ -36,6 +38,37 @@ class TraversalContext;
3638
*/
3739
class DependencyGraph {
3840
public:
41+
static constexpr uint32_t kNumberOfClosurePhases = 6;
42+
43+
static constexpr hb_tag_t kClosurePhaseTable[] = {
44+
common::FontHelper::kCmap,
45+
common::FontHelper::kGSUB,
46+
common::FontHelper::kMATH,
47+
common::FontHelper::kCOLR,
48+
common::FontHelper::kGlyf,
49+
common::FontHelper::kCFF,
50+
};
51+
52+
static constexpr hb_tag_t kClosurePhaseNodeFilter[] = {
53+
/* cmap */ 0xFFFFFFFF,
54+
/* GSUB */ Node::NodeType::GLYPH,
55+
/* MATH */ Node::NodeType::GLYPH,
56+
/* COLR */ Node::NodeType::GLYPH,
57+
/* glyf */ Node::NodeType::GLYPH,
58+
/* CFF */ Node::NodeType::GLYPH,
59+
};
60+
61+
static constexpr hb_tag_t kClosurePhaseStartNodes[] = {
62+
/* cmap */ Node::NodeType::SEGMENT,
63+
// For GSUB we also need to consider reached features as starting nodes
64+
// since those have outgoing GSUB edges.
65+
/* GSUB */ Node::NodeType::GLYPH | Node::NodeType::FEATURE,
66+
/* MATH */ Node::NodeType::GLYPH,
67+
/* COLR */ Node::NodeType::GLYPH,
68+
/* glyf */ Node::NodeType::GLYPH,
69+
/* CFF */ Node::NodeType::GLYPH,
70+
};
71+
3972
static absl::StatusOr<DependencyGraph> Create(
4073
const ift::encoder::RequestedSegmentationInformation* segmentation_info,
4174
hb_face_t* face) {
@@ -83,15 +116,15 @@ class DependencyGraph {
83116

84117
// Returns a topological sorting of the dependency graph's nodes, excluding
85118
// any nodes that are in the init font.
86-
absl::StatusOr<std::vector<Node>> TopologicalSorting() const;
119+
absl::StatusOr<std::vector<Node>> TopologicalSorting(const absl::flat_hash_set<hb_tag_t>& table_filter, uint32_t node_type_filter) const;
87120

88121
// Computes the incoming edges for every node in the dependency graph, taking
89122
// into account all context requirements and implicit dependencies.
90123
//
91124
// The return value is a map from each node to each unique edge requirement.
92125
// Edge requirements are represented as a CNF expression over other nodes.
93126
absl::StatusOr<absl::flat_hash_map<Node, absl::btree_set<EdgeConditonsCnf>>>
94-
CollectIncomingEdges() const;
127+
CollectIncomingEdges(const absl::flat_hash_set<hb_tag_t>& table_filter, uint32_t node_type_filter) const;
95128

96129
private:
97130
DependencyGraph(
@@ -128,7 +161,7 @@ class DependencyGraph {
128161
TraversalContext<ClosureState>* context) const;
129162

130163
absl::Status ClosureSubTraversal(
131-
const TraversalContext<ClosureState>* base_context, hb_tag_t table,
164+
const TraversalContext<ClosureState>* base_context, uint32_t phase_index,
132165
Traversal& traversal) const;
133166

134167
template <typename CallbackT>

0 commit comments

Comments
 (0)