11#include " ift/dep_graph/dependency_graph.h"
22
33#include < cstdint>
4- #include < functional>
54#include < optional>
65#include < utility>
76#include < vector>
@@ -192,12 +191,15 @@ class TraversalContext {
192191
193192 // Traverse an edge with no special context and/or additional information
194193 // other than table tag
195- void TraverseEdgeTo (Node dest,
194+ void TraverseEdgeTo (Node source, Node dest,
196195 std::optional<hb_tag_t > table_tag = std::nullopt ) {
197196 if (!ShouldFollow (dest, table_tag, std::nullopt )) {
198197 return ;
199198 }
200199
200+ callback.VisitPending (PendingEdge::Disjunctive (
201+ source, dest, table_tag.value_or (HB_TAG (' ' , ' ' , ' ' , ' ' ))));
202+
201203 if (table_tag.has_value ()) {
202204 callback.Visit (dest, *table_tag);
203205 } else {
@@ -332,8 +334,15 @@ class TraversalContext {
332334 const PendingEdge& edge, const CodepointSet& reached_unicodes,
333335 const GlyphSet& reached_glyphs,
334336 const flat_hash_set<hb_tag_t >& reached_features) {
335- if (edge.required_glyph .has_value () &&
336- !reached_glyphs.contains (*edge.required_glyph )) {
337+ if (edge.source .IsFeature () &&
338+ !reached_features.contains (edge.source .Id ())) {
339+ return false ;
340+ }
341+ if (edge.source .IsUnicode () &&
342+ !reached_unicodes.contains (edge.source .Id ())) {
343+ return false ;
344+ }
345+ if (edge.source .IsGlyph () && !reached_glyphs.contains (edge.source .Id ())) {
337346 return false ;
338347 }
339348
@@ -436,7 +445,7 @@ bool DependencyGraph::ClosureState::Reached(Node node) {
436445}
437446
438447void DependencyGraph::ClosureState::SetStartNodes (
439- const absl:: btree_set<Node>& start) {
448+ const btree_set<Node>& start) {
440449 for (Node node : start) {
441450 Reached (node);
442451 }
@@ -445,6 +454,8 @@ void DependencyGraph::ClosureState::SetStartNodes(
445454template <typename CallbackT>
446455static Status DoTraversal (const PendingEdge& edge,
447456 TraversalContext<CallbackT>& context) {
457+ context.callback .VisitPending (edge);
458+
448459 if (edge.table_tag == GSUB && edge.required_feature .has_value ()) {
449460 if (edge.required_context_set_index .has_value ()) {
450461 GlyphSet context_glyphs =
@@ -514,21 +525,20 @@ absl::Status DependencyGraph::HandleOutgoingEdges(
514525 } else if (node.IsFeature ()) {
515526 TRYV (HandleFeatureOutgoingEdges (node.Id (), context));
516527 } else if (node.IsInitFont ()) {
517- HandleSubsetDefinitionOutgoingEdges (segmentation_info_-> InitFontSegment (),
518- context);
528+ HandleSubsetDefinitionOutgoingEdges (
529+ node, segmentation_info_-> InitFontSegment (), context);
519530 }
520531 return absl::OkStatus ();
521532}
522533
523534StatusOr<std::vector<Node>> DependencyGraph::TopologicalSorting () const {
524535 struct TopoCallback {
525536 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- }
537+ void Visit (Node dest) {}
538+ void Visit (Node dest, hb_tag_t ) {}
539+ void VisitGsub (Node dest, hb_tag_t ) {}
540+ void VisitContextual (Node dest, hb_tag_t , ift::common::GlyphSet) {}
541+ void VisitPending (const PendingEdge& edge) { edges.push_back (edge.dest ); }
532542 };
533543
534544 CodepointSet non_init_font_codepoints =
@@ -549,7 +559,7 @@ StatusOr<std::vector<Node>> DependencyGraph::TopologicalSorting() const {
549559 context.enforce_context = false ;
550560 context.unicode_filter = &non_init_font_codepoints;
551561 context.glyph_filter = &non_init_font_glyphs;
552- context.feature_filter = &non_init_font_features ;
562+ context.feature_filter = &full_feature_set_ ;
553563
554564 // DFS topological sorting algorithm from:
555565 // https://en.wikipedia.org/wiki/Topological_sorting
@@ -659,7 +669,7 @@ StatusOr<Traversal> DependencyGraph::ClosureTraversal(
659669}
660670
661671StatusOr<Traversal> DependencyGraph::ClosureTraversal (
662- const absl:: btree_set<Node>& nodes, const GlyphSet* glyph_filter_ptr,
672+ const btree_set<Node>& nodes, const GlyphSet* glyph_filter_ptr,
663673 const CodepointSet* unicode_filter_ptr, bool enforce_context) const {
664674 // TODO(garretrieger): context edges don't have edges for each participating
665675 // glyph, so for full correctness in matching closure we should introduce
@@ -783,7 +793,7 @@ Status DependencyGraph::HandleUnicodeOutgoingEdges(
783793 {
784794 auto it = unicode_to_gid_.find (unicode);
785795 if (it != unicode_to_gid_.end ()) {
786- context->TraverseEdgeTo (Node::Glyph (it->second ));
796+ context->TraverseEdgeTo (Node::Unicode (unicode), Node:: Glyph (it->second ));
787797 }
788798 }
789799
@@ -799,7 +809,7 @@ Status DependencyGraph::HandleUnicodeOutgoingEdges(
799809 auto unicode_funcs = hb_unicode_funcs_get_default ();
800810 hb_codepoint_t mirror = hb_unicode_mirroring (unicode_funcs, unicode);
801811 if (mirror != unicode) {
802- context->TraverseEdgeTo (Node::Unicode (mirror));
812+ context->TraverseEdgeTo (Node::Unicode (unicode), Node::Unicode ( mirror));
803813 }
804814
805815 return absl::OkStatus ();
@@ -830,7 +840,7 @@ Status DependencyGraph::HandleGlyphOutgoingEdges(
830840 continue ;
831841 }
832842
833- context->TraverseEdgeTo (dest, table_tag);
843+ context->TraverseEdgeTo (Node::Glyph (gid), dest, table_tag);
834844 }
835845
836846 auto it = context_glyph_implied_edges_.find (gid);
@@ -904,19 +914,20 @@ void DependencyGraph::HandleSegmentOutgoingEdges(
904914 }
905915
906916 const Segment& s = segmentation_info_->Segments ().at (id);
907- HandleSubsetDefinitionOutgoingEdges (s.Definition (), context);
917+ HandleSubsetDefinitionOutgoingEdges (Node::Segment (id), s.Definition (),
918+ context);
908919}
909920
910921template <typename CallbackT>
911922void DependencyGraph::HandleSubsetDefinitionOutgoingEdges (
912- const SubsetDefinition& subset_def,
923+ Node source, const SubsetDefinition& subset_def,
913924 TraversalContext<CallbackT>* context) const {
914925 for (hb_codepoint_t u : subset_def.codepoints ) {
915- context->TraverseEdgeTo (Node::Unicode (u));
926+ context->TraverseEdgeTo (source, Node::Unicode (u));
916927 }
917928
918929 for (hb_tag_t f : subset_def.feature_tags ) {
919- context->TraverseEdgeTo (Node::Feature (f));
930+ context->TraverseEdgeTo (source, Node::Feature (f));
920931 }
921932}
922933
@@ -984,8 +995,9 @@ StatusOr<flat_hash_set<hb_tag_t>> DependencyGraph::InitFeatureSet(
984995StatusOr<GlyphSet> DependencyGraph::RequiredGlyphsFor (
985996 const PendingEdge& edge) const {
986997 GlyphSet out;
987- if (edge.required_glyph .has_value ()) {
988- out.insert (*edge.required_glyph );
998+
999+ if (edge.source .IsGlyph ()) {
1000+ out.insert (edge.source .Id ());
9891001 }
9901002
9911003 if (edge.required_liga_set_index .has_value ()) {
@@ -1011,6 +1023,64 @@ StatusOr<GlyphSet> DependencyGraph::GetLigaSet(
10111023 return glyphs;
10121024}
10131025
1026+ StatusOr<EdgeConditonsCnf> DependencyGraph::ExtractRequirements (
1027+ const PendingEdge& edge) const {
1028+ EdgeConditonsCnf requirements;
1029+
1030+ requirements.insert ({edge.source });
1031+
1032+ if (edge.required_feature .has_value ()) {
1033+ requirements.insert ({Node::Feature (*edge.required_feature )});
1034+ }
1035+
1036+ if (edge.required_codepoints .has_value ()) {
1037+ requirements.insert ({Node::Unicode (edge.required_codepoints ->first )});
1038+ requirements.insert ({Node::Unicode (edge.required_codepoints ->second )});
1039+ }
1040+
1041+ if (edge.required_liga_set_index .has_value ()) {
1042+ GlyphSet gids = TRY (GetLigaSet (*edge.required_liga_set_index ));
1043+ for (glyph_id_t gid : gids) {
1044+ requirements.insert ({Node::Glyph (gid)});
1045+ }
1046+ }
1047+
1048+ // TODO(garretrieger): take contextual lookup flags into consideration. See
1049+ // dep graph documentation for more details. They can be used to
1050+ // decide if the resulting conditions are exact or an over approximation.
1051+ if (edge.required_context_set_index .has_value ()) {
1052+ // the context set is actually a set of sets.
1053+ if (!hb_depend_get_set_from_index (dependency_graph_.get (),
1054+ *edge.required_context_set_index ,
1055+ scratch_set_.get ())) {
1056+ return absl::InternalError (" Context set lookup failed." );
1057+ }
1058+
1059+ hb_codepoint_t set_id = HB_CODEPOINT_INVALID;
1060+ while (hb_set_next (scratch_set_.get (), &set_id)) {
1061+ btree_set<Node> req;
1062+ if (set_id < 0x80000000 ) {
1063+ req.insert (Node::Glyph (set_id));
1064+ } else {
1065+ hb_codepoint_t actual_set_id = set_id & 0x7FFFFFFF ;
1066+ if (!hb_depend_get_set_from_index (dependency_graph_.get (),
1067+ actual_set_id,
1068+ scratch_set_aux_.get ())) {
1069+ return absl::InternalError (" Context sub set lookup failed." );
1070+ }
1071+ for (hb_codepoint_t gid : GlyphSet (scratch_set_aux_.get ())) {
1072+ req.insert (Node::Glyph (gid));
1073+ }
1074+ }
1075+ if (!req.empty ()) {
1076+ requirements.insert (std::move (req));
1077+ }
1078+ }
1079+ }
1080+
1081+ return requirements;
1082+ }
1083+
10141084flat_hash_map<glyph_id_t , std::vector<DependencyGraph::LayoutFeatureEdge>>
10151085DependencyGraph::ComputeContextGlyphEdges () const {
10161086 flat_hash_map<glyph_id_t , btree_set<LayoutFeatureEdge>> edges;
@@ -1119,6 +1189,62 @@ DependencyGraph::ComputeFeatureEdges() const {
11191189 return out;
11201190}
11211191
1192+ StatusOr<flat_hash_map<Node, btree_set<EdgeConditonsCnf>>>
1193+ DependencyGraph::CollectIncomingEdges () const {
1194+ struct IncomingEdgeCollector {
1195+ flat_hash_map<Node, btree_set<EdgeConditonsCnf>>* incoming_edges = nullptr ;
1196+ const DependencyGraph* graph = nullptr ;
1197+ Status had_error = absl::OkStatus();
1198+
1199+ void Visit (Node dest) {}
1200+ void Visit (Node dest, hb_tag_t ) {}
1201+ void VisitGsub (Node, hb_tag_t ) {}
1202+ void VisitContextual (Node, hb_tag_t , ift::common::GlyphSet) {}
1203+ void VisitPending (const PendingEdge& pe) {
1204+ auto reqs = graph->ExtractRequirements (pe);
1205+ had_error.Update (reqs.status ());
1206+ if (!reqs.ok ()) {
1207+ return ;
1208+ }
1209+
1210+ (*incoming_edges)[pe.dest ].insert (std::move (*reqs));
1211+ }
1212+ };
1213+
1214+ flat_hash_map<Node, btree_set<EdgeConditonsCnf>> incoming_edges;
1215+ TraversalContext<IncomingEdgeCollector> context;
1216+ context.depend = dependency_graph_.get();
1217+ context.full_closure = &segmentation_info_->FullClosure ();
1218+ context.feature_filter = &full_feature_set_;
1219+ context.glyph_filter = &segmentation_info_->FullClosure ();
1220+ context.unicode_filter = &segmentation_info_->FullDefinition ().codepoints;
1221+ context.enforce_context = false ;
1222+ context.callback.incoming_edges = &incoming_edges;
1223+ context.callback.graph = this ;
1224+
1225+ btree_set<Node> all_nodes;
1226+ all_nodes.insert(Node::InitFont());
1227+ for (segment_index_t s : segmentation_info_->NonEmptySegments ()) {
1228+ all_nodes.insert (Node::Segment (s));
1229+ }
1230+ for (hb_codepoint_t u : segmentation_info_->FullDefinition ().codepoints) {
1231+ all_nodes.insert (Node::Unicode (u));
1232+ }
1233+ for (hb_tag_t f : full_feature_set_) {
1234+ all_nodes.insert (Node::Feature (f));
1235+ }
1236+ for (glyph_id_t g : segmentation_info_->FullClosure ()) {
1237+ all_nodes.insert (Node::Glyph (g));
1238+ }
1239+
1240+ for (Node n : all_nodes) {
1241+ TRYV (HandleOutgoingEdges (n, &context));
1242+ TRYV (context.callback .had_error );
1243+ }
1244+
1245+ return incoming_edges;
1246+ }
1247+
11221248void PrintTo (const Node& node, std::ostream* os) { *os << node.ToString (); }
11231249
11241250} // namespace ift::dep_graph
0 commit comments