11#include " ift/dep_graph/dependency_graph.h"
22
33#include < cstdint>
4+ #include < functional>
45#include < optional>
56#include < utility>
7+ #include < vector>
68
79#include " absl/log/log.h"
810#include " ift/common/font_data.h"
@@ -82,7 +84,7 @@ static StatusOr<GlyphSet> GetContextSet(
8284}
8385
8486// Tracks the details of an inprogress traversal.
85- template <typename CallbackT>
87+ template <typename CallbackT>
8688class TraversalContext {
8789 public:
8890 hb_depend_t * depend = nullptr ;
@@ -173,7 +175,6 @@ class TraversalContext {
173175
174176 // Returns the next node to be visited.
175177
176-
177178 // This checks all pending edges and if any have their constraints satisfied
178179 // then they are traversed. Returns true if there are now more nodes in the
179180 // next queue.
@@ -363,11 +364,8 @@ class TraversalContext {
363364
364365 void Pending (PendingEdge edge) { pending_edges_.push_back (edge); }
365366
366- bool ShouldFollow (
367- Node node,
368- std::optional<hb_tag_t > table_tag,
369- std::optional<hb_tag_t > layout_feature) const {
370-
367+ bool ShouldFollow (Node node, std::optional<hb_tag_t > table_tag,
368+ std::optional<hb_tag_t > layout_feature) const {
371369 if (!node.Matches (node_type_filter)) {
372370 return false ;
373371 }
@@ -412,7 +410,8 @@ void DependencyGraph::ClosureState::VisitGsub(Node dest, hb_tag_t feature) {
412410 Reached (dest);
413411}
414412
415- void DependencyGraph::ClosureState::VisitContextual (Node dest, hb_tag_t feature, GlyphSet context_glyphs) {
413+ void DependencyGraph::ClosureState::VisitContextual (Node dest, hb_tag_t feature,
414+ GlyphSet context_glyphs) {
416415 traversal.VisitContextual (dest, feature, context_glyphs);
417416 Reached (dest);
418417}
@@ -436,21 +435,23 @@ bool DependencyGraph::ClosureState::Reached(Node node) {
436435 return true ;
437436}
438437
439- void DependencyGraph::ClosureState::SetStartNodes (const absl::btree_set<Node>& start) {
438+ void DependencyGraph::ClosureState::SetStartNodes (
439+ const absl::btree_set<Node>& start) {
440440 for (Node node : start) {
441441 Reached (node);
442442 }
443443}
444444
445- template <typename CallbackT>
446- static Status DoTraversal (const PendingEdge& edge, TraversalContext<CallbackT>& context) {
445+ template <typename CallbackT>
446+ static Status DoTraversal (const PendingEdge& edge,
447+ TraversalContext<CallbackT>& context) {
447448 if (edge.table_tag == GSUB && edge.required_feature .has_value ()) {
448449 if (edge.required_context_set_index .has_value ()) {
449450 GlyphSet context_glyphs =
450451 TRY (GetContextSet (context.depend , context.full_closure ,
451452 *edge.required_context_set_index ));
452453 context.callback .VisitContextual (edge.dest , *edge.required_feature ,
453- context_glyphs);
454+ context_glyphs);
454455 } else {
455456 context.callback .VisitGsub (edge.dest , *edge.required_feature );
456457 }
@@ -462,8 +463,9 @@ static Status DoTraversal(const PendingEdge& edge, TraversalContext<CallbackT>&
462463 return absl::OkStatus ();
463464}
464465
465- template <typename CallbackT>
466- StatusOr<bool > TraversalContext<CallbackT>::CheckPending(hb_depend_t * depend_graph) {
466+ template <typename CallbackT>
467+ StatusOr<bool > TraversalContext<CallbackT>::CheckPending(
468+ hb_depend_t * depend_graph) {
467469 bool did_work = false ;
468470 auto it = pending_edges_.begin ();
469471 while (it != pending_edges_.end ()) {
@@ -482,9 +484,9 @@ StatusOr<bool> TraversalContext<CallbackT>::CheckPending(hb_depend_t* depend_gra
482484 return did_work;
483485}
484486
485- template <typename CallbackT>
487+ template <typename CallbackT>
486488Status TraversalContext<CallbackT>::TraverseEdgeTo(Node dest, PendingEdge edge,
487- hb_tag_t table_tag) {
489+ hb_tag_t table_tag) {
488490 if (!ShouldFollow (dest, table_tag, edge.required_feature )) {
489491 return absl::OkStatus ();
490492 }
@@ -500,6 +502,123 @@ Status TraversalContext<CallbackT>::TraverseEdgeTo(Node dest, PendingEdge edge,
500502 return absl::OkStatus ();
501503}
502504
505+ template <typename CallbackT>
506+ absl::Status DependencyGraph::HandleOutgoingEdges (
507+ Node node, TraversalContext<CallbackT>* context) const {
508+ if (node.IsGlyph ()) {
509+ TRYV (HandleGlyphOutgoingEdges (node.Id (), context));
510+ } else if (node.IsUnicode ()) {
511+ TRYV (HandleUnicodeOutgoingEdges (node.Id (), context));
512+ } else if (node.IsSegment ()) {
513+ HandleSegmentOutgoingEdges (node.Id (), context);
514+ } else if (node.IsFeature ()) {
515+ TRYV (HandleFeatureOutgoingEdges (node.Id (), context));
516+ } else if (node.IsInitFont ()) {
517+ HandleSubsetDefinitionOutgoingEdges (segmentation_info_->InitFontSegment (),
518+ context);
519+ }
520+ return absl::OkStatus ();
521+ }
522+
523+ StatusOr<std::vector<Node>> DependencyGraph::TopologicalSorting () const {
524+ struct TopoCallback {
525+ std::vector<Node> edges;
526+ void Visit (Node dest) { edges.push_back (dest); }
527+ void Visit (Node dest, hb_tag_t ) { edges.push_back (dest); }
528+ void VisitGsub (Node dest, hb_tag_t ) { edges.push_back (dest); }
529+ void VisitContextual (Node dest, hb_tag_t , ift::common::GlyphSet) {
530+ edges.push_back (dest);
531+ }
532+ };
533+
534+ CodepointSet non_init_font_codepoints =
535+ segmentation_info_->FullDefinition ().codepoints ;
536+ non_init_font_codepoints.subtract (
537+ segmentation_info_->InitFontSegment ().codepoints );
538+
539+ flat_hash_set<hb_tag_t > non_init_font_features = full_feature_set_;
540+ for (hb_tag_t tag : segmentation_info_->InitFontSegment ().feature_tags ) {
541+ non_init_font_features.erase (tag);
542+ }
543+
544+ GlyphSet non_init_font_glyphs = segmentation_info_->NonInitFontGlyphs ();
545+
546+ TraversalContext<TopoCallback> context;
547+ context.depend = dependency_graph_.get ();
548+ context.full_closure = &segmentation_info_->FullClosure ();
549+ context.enforce_context = false ;
550+ context.unicode_filter = &non_init_font_codepoints;
551+ context.glyph_filter = &non_init_font_glyphs;
552+ context.feature_filter = &non_init_font_features;
553+
554+ // DFS topological sorting algorithm from:
555+ // https://en.wikipedia.org/wiki/Topological_sorting
556+ // Modified to use a stack instead of recursion.
557+ flat_hash_set<Node> visited;
558+ flat_hash_set<Node> visiting;
559+ std::vector<Node> post_order;
560+ std::vector<Node> dfs_stack;
561+ auto process_node = [&](Node start_node) -> Status {
562+ dfs_stack.push_back (start_node);
563+ while (!dfs_stack.empty ()) {
564+ Node node = dfs_stack.back ();
565+ if (visited.contains (node)) {
566+ dfs_stack.pop_back ();
567+ continue ;
568+ }
569+ if (visiting.contains (node)) {
570+ visiting.erase (node);
571+ visited.insert (node);
572+ post_order.push_back (node);
573+ dfs_stack.pop_back ();
574+ continue ;
575+ }
576+
577+ visiting.insert (node);
578+ context.callback .edges .clear ();
579+ TRYV (HandleOutgoingEdges (node, &context));
580+
581+ std::vector<Node> current_edges = std::move (context.callback .edges );
582+ for (Node dest : current_edges) {
583+ if (visiting.contains (dest)) {
584+ return absl::InvalidArgumentError (
585+ absl::StrCat (" Cycle detected during topological sort in "
586+ " dependency graph involving node: " ,
587+ dest.ToString ()));
588+ }
589+ if (!visited.contains (dest)) {
590+ dfs_stack.push_back (dest);
591+ }
592+ }
593+ }
594+ return absl::OkStatus ();
595+ };
596+
597+ /* ### Run process_node for every possible, non init font node. #### */
598+ for (segment_index_t s : segmentation_info_->NonEmptySegments ()) {
599+ TRYV (process_node (Node::Segment (s)));
600+ }
601+
602+ for (hb_codepoint_t u : non_init_font_codepoints) {
603+ TRYV (process_node (Node::Unicode (u)));
604+ }
605+
606+ for (hb_tag_t tag : non_init_font_features) {
607+ TRYV (process_node (Node::Feature (tag)));
608+ }
609+
610+ for (glyph_id_t gid : non_init_font_glyphs) {
611+ TRYV (process_node (Node::Glyph (gid)));
612+ }
613+
614+ std::vector<Node> topo_order;
615+ topo_order.reserve (post_order.size ());
616+ for (auto it = post_order.rbegin (); it != post_order.rend (); ++it) {
617+ topo_order.push_back (*it);
618+ }
619+
620+ return topo_order;
621+ }
503622
504623StatusOr<Traversal> DependencyGraph::TraverseGraph (
505624 TraversalContext<ClosureState>* context) const {
@@ -508,34 +627,16 @@ StatusOr<Traversal> DependencyGraph::TraverseGraph(
508627 while (true ) {
509628 std::optional<Node> next = context->callback .GetNext ();
510629 if (!next.has_value ()) {
511- if (TRY (context->CheckPending (dependency_graph_.get ())) && !context->callback .next .empty ()) {
630+ if (TRY (context->CheckPending (dependency_graph_.get ())) &&
631+ !context->callback .next .empty ()) {
512632 continue ;
513633 } else {
514634 // nothing left to traverse.
515635 break ;
516636 }
517637 }
518638
519- if (next->IsGlyph ()) {
520- TRYV (HandleGlyphOutgoingEdges (next->Id (), context));
521- }
522-
523- if (next->IsUnicode ()) {
524- TRYV (HandleUnicodeOutgoingEdges (next->Id (), context));
525- }
526-
527- if (next->IsSegment ()) {
528- HandleSegmentOutgoingEdges (next->Id (), context);
529- }
530-
531- if (next->IsFeature ()) {
532- TRYV (HandleFeatureOutgoingEdges (next->Id (), context));
533- }
534-
535- if (next->IsInitFont ()) {
536- HandleSubsetDefinitionOutgoingEdges (segmentation_info_->InitFontSegment (),
537- context);
538- }
639+ TRYV (HandleOutgoingEdges (*next, context));
539640 }
540641
541642 for (const auto & edge : context->pending_edges ()) {
@@ -676,7 +777,7 @@ Status DependencyGraph::ClosureSubTraversal(
676777 return absl::OkStatus ();
677778}
678779
679- template <typename CallbackT>
780+ template <typename CallbackT>
680781Status DependencyGraph::HandleUnicodeOutgoingEdges (
681782 hb_codepoint_t unicode, TraversalContext<CallbackT>* context) const {
682783 {
@@ -704,7 +805,7 @@ Status DependencyGraph::HandleUnicodeOutgoingEdges(
704805 return absl::OkStatus ();
705806}
706807
707- template <typename CallbackT>
808+ template <typename CallbackT>
708809Status DependencyGraph::HandleGlyphOutgoingEdges (
709810 glyph_id_t gid, TraversalContext<CallbackT>* context) const {
710811 hb_codepoint_t index = 0 ;
@@ -735,7 +836,7 @@ Status DependencyGraph::HandleGlyphOutgoingEdges(
735836 return absl::OkStatus ();
736837}
737838
738- template <typename CallbackT>
839+ template <typename CallbackT>
739840Status DependencyGraph::HandleGsubGlyphOutgoingEdges (
740841 glyph_id_t source_gid, glyph_id_t dest_gid, hb_tag_t layout_tag,
741842 hb_codepoint_t ligature_set, hb_codepoint_t context_set,
@@ -751,7 +852,7 @@ Status DependencyGraph::HandleGsubGlyphOutgoingEdges(
751852 }
752853}
753854
754- template <typename CallbackT>
855+ template <typename CallbackT>
755856Status DependencyGraph::HandleFeatureOutgoingEdges (
756857 hb_tag_t feature_tag, TraversalContext<CallbackT>* context) const {
757858 if (!context->table_filter .contains (GSUB)) {
@@ -778,7 +879,7 @@ Status DependencyGraph::HandleFeatureOutgoingEdges(
778879 return absl::OkStatus ();
779880}
780881
781- template <typename CallbackT>
882+ template <typename CallbackT>
782883void DependencyGraph::HandleSegmentOutgoingEdges (
783884 segment_index_t id, TraversalContext<CallbackT>* context) const {
784885 if (id >= segmentation_info_->Segments ().size ()) {
@@ -790,9 +891,10 @@ void DependencyGraph::HandleSegmentOutgoingEdges(
790891 HandleSubsetDefinitionOutgoingEdges (s.Definition (), context);
791892}
792893
793- template <typename CallbackT>
894+ template <typename CallbackT>
794895void DependencyGraph::HandleSubsetDefinitionOutgoingEdges (
795- const SubsetDefinition& subset_def, TraversalContext<CallbackT>* context) const {
896+ const SubsetDefinition& subset_def,
897+ TraversalContext<CallbackT>* context) const {
796898 for (hb_codepoint_t u : subset_def.codepoints ) {
797899 context->TraverseEdgeTo (Node::Unicode (u));
798900 }
@@ -969,4 +1071,6 @@ DependencyGraph::ComputeFeatureEdges() const {
9691071 return out;
9701072}
9711073
1074+ void PrintTo (const Node& node, std::ostream* os) { *os << node.ToString (); }
1075+
9721076} // namespace ift::dep_graph
0 commit comments