@@ -39,14 +39,7 @@ using ift::encoder::SubsetDefinition;
3939
4040namespace 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
765742Status 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(
881859template <typename CallbackT>
882860Status 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
11921170StatusOr<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 ()) {
0 commit comments