@@ -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,33 @@ 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+ if (table_tags.contains (DependencyGraph::kClosurePhaseTable [phase])) {
737+ TRYV (ClosureSubTraversal (&base_context, phase, traversal_full));
738+ }
760739 }
761740
762741 return traversal_full;
763742}
764743
765744Status DependencyGraph::ClosureSubTraversal (
766- const TraversalContext<ClosureState>* base_context, hb_tag_t table ,
745+ const TraversalContext<ClosureState>* base_context, uint32_t phase_index ,
767746 Traversal& traversal_full) const {
768747 btree_set<Node> start_nodes;
769- for (glyph_id_t gid : traversal_full.ReachedGlyphs ()) {
770- start_nodes.insert (Node::Glyph (gid));
748+
749+ if (DependencyGraph::kClosurePhaseStartNodes [phase_index] & Node::NodeType::GLYPH) {
750+ for (glyph_id_t gid : traversal_full.ReachedGlyphs ()) {
751+ start_nodes.insert (Node::Glyph (gid));
752+ }
771753 }
772754
773- if (table == GSUB) {
774- // For GSUB we also need to consider reached features as starting nodes
775- // since those have outgoing GSUB edges.
755+ if (DependencyGraph::kClosurePhaseStartNodes [phase_index] & Node::NodeType::FEATURE) {
776756 for (hb_tag_t feature : traversal_full.ReachedLayoutFeatures ()) {
777757 start_nodes.insert (Node::Feature (feature));
778758 }
@@ -781,8 +761,8 @@ Status DependencyGraph::ClosureSubTraversal(
781761 TraversalContext<ClosureState> context = *base_context;
782762 context.SetReached (start_nodes);
783763 context.callback .SetStartNodes (start_nodes);
784- context.table_filter = {table };
785- context.node_type_filter = Node::NodeType::GLYPH ;
764+ context.table_filter = {DependencyGraph:: kClosurePhaseTable [phase_index] };
765+ context.node_type_filter = DependencyGraph:: kClosurePhaseNodeFilter [phase_index] ;
786766 traversal_full.Merge (TRY (TraverseGraph (&context)));
787767 return absl::OkStatus ();
788768}
@@ -881,7 +861,7 @@ Status DependencyGraph::HandleGsubGlyphOutgoingEdges(
881861template <typename CallbackT>
882862Status DependencyGraph::HandleFeatureOutgoingEdges (
883863 hb_tag_t feature_tag, TraversalContext<CallbackT>* context) const {
884- if (!context->table_filter .contains (GSUB )) {
864+ if (!context->table_filter .contains (FontHelper:: kGSUB )) {
885865 // All feature edges are GSUB edges so we can skip
886866 // this if GSUB is being filtered out.
887867 return absl::OkStatus ();
@@ -1164,7 +1144,7 @@ DependencyGraph::ComputeFeatureEdges() const {
11641144 while (hb_depend_get_glyph_entry (
11651145 dependency_graph_.get (), gid, index++, &table_tag, &dest_gid,
11661146 &layout_tag, &ligature_set, &context_set, nullptr /* flags */ )) {
1167- if (table_tag != GSUB || layout_tag == HB_CODEPOINT_INVALID) {
1147+ if (table_tag != FontHelper:: kGSUB || layout_tag == HB_CODEPOINT_INVALID) {
11681148 continue ;
11691149 }
11701150
@@ -1190,7 +1170,9 @@ DependencyGraph::ComputeFeatureEdges() const {
11901170}
11911171
11921172StatusOr<flat_hash_map<Node, btree_set<EdgeConditonsCnf>>>
1193- DependencyGraph::CollectIncomingEdges () const {
1173+ DependencyGraph::CollectIncomingEdges (
1174+ const flat_hash_set<hb_tag_t >& table_filter,
1175+ uint32_t node_type_filter) const {
11941176 struct IncomingEdgeCollector {
11951177 flat_hash_map<Node, btree_set<EdgeConditonsCnf>>* incoming_edges = nullptr ;
11961178 const DependencyGraph* graph = nullptr ;
@@ -1219,9 +1201,13 @@ DependencyGraph::CollectIncomingEdges() const {
12191201 context.glyph_filter = &segmentation_info_->FullClosure ();
12201202 context.unicode_filter = &segmentation_info_->FullDefinition ().codepoints;
12211203 context.enforce_context = false ;
1204+ context.table_filter = table_filter;
1205+ context.node_type_filter = node_type_filter;
12221206 context.callback.incoming_edges = &incoming_edges;
12231207 context.callback.graph = this ;
12241208
1209+ // TODO(garretrieger): we have a few places iterating all nodes now, consider
1210+ // adding a function to generate the all nodes set or an all nodes iterator.
12251211 btree_set<Node> all_nodes;
12261212 all_nodes.insert(Node::InitFont());
12271213 for (segment_index_t s : segmentation_info_->NonEmptySegments ()) {
0 commit comments