tiny.filigree.FallbackContext
Defined in tiny.filigree.
API (13)
Actions
Public operations.
deinitinitresetscriptRunStatusshapeRunshapeRunSegmented: Reported segments grow through segment_allocator and must be released through it.workspaceStatus
Types and contracts
Public types and contracts.
Fields and members
Public fields and members.
Source
Source: lib/filigree/src/fallback/core.zig:47
zig
pub const Context = struct { allocator: Allocator, shaper: shape.Context, script_context: unicode_data.ScriptRunContext, segment_output: shape.Output, workspace: workspace_mod.Workspace, pub const Options: type = ContextOptions; pub fn init(allocator: Allocator, options: Options) !Context { var workspace = try workspace_mod.Workspace.init(allocator, .{ .max_source_units = options.script_runs.max_source_units, }); errdefer workspace.deinit(allocator); workspace.activate(); var script_context = try unicode_data.ScriptRunContext.init(allocator, options.script_runs); errdefer script_context.deinit(allocator); script_context.activate(); return .{ .allocator = allocator, .shaper = shape.Context.init(allocator, .{}), .script_context = script_context, .segment_output = try shape.Output.init(allocator, options.output), .workspace = workspace, }; } pub fn deinit(self: *Context) void { self.workspace.deinit(self.allocator); self.segment_output.deinit(self.allocator); self.script_context.deinit(self.allocator); self.shaper.deinit(); self.* = undefined; } pub fn reset(self: *Context) void { self.workspace.reset(); self.segment_output.clearRetainingCapacity(); self.script_context.reset(); self.shaper.reset(); } pub fn scriptRunStatus(self: *const Context) unicode_data.ScriptRunStatus { return self.script_context.status(); } pub fn workspaceStatus(self: *const Context) workspace_mod.Status { return self.workspace.status(); } pub fn shapeRun(self: *Context, input: Input, output: *shape.Output) !void { return self.shapeRunWithSegments(input, output, self.allocator, null); } /// Reported segments grow through segment_allocator and must be released through it. pub fn shapeRunSegmented( self: *Context, input: Input, output: *shape.Output, segment_allocator: Allocator, segments: *std.ArrayListUnmanaged(Segment), ) !void { return self.shapeRunWithSegments(input, output, segment_allocator, segments); } fn shapeRunWithSegments( self: *Context, input: Input, output: *shape.Output, segment_allocator: Allocator, segments: ?*std.ArrayListUnmanaged(Segment), ) !void { const automatic_script = usesAutomaticScript(input.base); if (input.candidates.len == 0 and !automatic_script) { if (segments) |items| items.clearRetainingCapacity(); try self.shaper.shapeRun(input.base, output); if (segments != null) { try appendOutputSegment(segment_allocator, segments, try sourceRange(input.base), 0, false, 0, output.glyphs.items.len); } return; } const source_byte_len = try input.base.text.byteLen(); if (source_byte_len > std.math.maxInt(u32)) return error.SourceTooLong; const remaining_source_bytes: usize = std.math.maxInt(u32) - input.base.source_offset; if (source_byte_len > remaining_source_bytes) return error.SourceTooLong; try self.workspace.begin(input.base.text.scalarCapacityHint()); output.clearRetainingCapacity(); self.segment_output.clearRetainingCapacity(); self.script_context.reset(); self.shaper.reset(); if (segments) |items| items.clearRetainingCapacity(); errdefer output.clearRetainingCapacity(); errdefer self.reset(); errdefer if (segments) |items| items.clearRetainingCapacity(); output.direction = input.base.direction; output.writing_mode = input.base.writing_mode; output.output_order = input.base.output_order; if (source_byte_len == 0) return; try self.collectClusters(input.base.text); if (automatic_script) try self.assignScripts(input.base.text); const uses_fallback = self.assignCandidates(input); if (!uses_fallback and !automatic_script) { try self.shaper.shapeRun(input.base, output); try appendOutputSegment( segment_allocator, segments, .{ .start = input.base.source_offset, .end = @intCast(input.base.source_offset + source_byte_len), }, 0, false, 0, output.glyphs.items.len, ); return; } try self.buildRanges(automatic_script); if (input.base.direction == .rtl and input.base.output_order == .visual) { var range_index = self.workspace.ranges.items.len; while (range_index > 0) { range_index -= 1; try self.shapeSegment(input, self.workspace.ranges.items[range_index], output, segment_allocator, segments); } } else { for (self.workspace.ranges.items) |range| { try self.shapeSegment(input, range, output, segment_allocator, segments); } } } fn assignScripts(self: *Context, source: shape.Source) !void { const runs = try self.script_context.itemize(.{ .text = source }); var run_index: usize = 0; for (self.workspace.clusters.items) |*cluster| { while (run_index + 1 < runs.len and cluster.source.start >= runs[run_index].source.end) run_index += 1; cluster.script = if (run_index < runs.len and cluster.source.start >= runs[run_index].source.start and cluster.source.end <= runs[run_index].source.end) runs[run_index].script else .unknown; } } fn collectClusters(self: *Context, source: shape.Source) !void { self.workspace.scalars.clearRetainingCapacity(); self.workspace.clusters.clearRetainingCapacity(); var grapheme_state: unicode_data.GraphemeState = .{}; var iterator = try unicode_data.SourceIterator.init(source, 0); while (try iterator.next()) |scalar| { try self.appendScalar(.{ .codepoint = scalar.codepoint, .start = scalar.source.start, .end = scalar.source.end, }, &grapheme_state); } self.mergeJoiningClusters(); } fn appendScalar(self: *Context, scalar: Scalar, grapheme_state: *unicode_data.GraphemeState) !void { const scalar_index: u32 = @intCast(self.workspace.scalars.items.len); std.debug.assert(self.workspace.scalars.items.len < self.workspace.scalars.capacity); self.workspace.scalars.appendAssumeCapacity(scalar); const scalar_end = scalar_index + 1; const merge = grapheme_state.consume(scalar.codepoint); if (self.workspace.clusters.items.len == 0 or !merge) { std.debug.assert(self.workspace.clusters.items.len < self.workspace.clusters.capacity); self.workspace.clusters.appendAssumeCapacity(.{ .source = .{ .start = scalar.start, .end = scalar.end }, .scalars = .{ .start = scalar_index, .end = scalar_end }, }); return; } const last = &self.workspace.clusters.items[self.workspace.clusters.items.len - 1]; last.source.end = scalar.end; last.scalars.end = scalar_end; } fn mergeJoiningClusters(self: *Context) void { if (self.workspace.clusters.items.len < 2) return; var write_index: usize = 0; var read_index: usize = 1; while (read_index < self.workspace.clusters.items.len) : (read_index += 1) { if (self.clustersJoin(self.workspace.clusters.items[write_index], self.workspace.clusters.items[read_index])) { self.workspace.clusters.items[write_index].source.end = self.workspace.clusters.items[read_index].source.end; self.workspace.clusters.items[write_index].scalars.end = self.workspace.clusters.items[read_index].scalars.end; } else { write_index += 1; if (write_index != read_index) self.workspace.clusters.items[write_index] = self.workspace.clusters.items[read_index]; } } self.workspace.clusters.shrinkRetainingCapacity(write_index + 1); } fn clustersJoin(self: *const Context, left: SourceCluster, right: SourceCluster) bool { const previous = self.lastJoiningType(left) orelse return false; const next = self.firstJoiningType(right) orelse return false; return shape.joiningTypesConnect(previous, next); } fn firstJoiningType(self: *const Context, cluster: SourceCluster) ?shape.JoiningType { var scalar_index: usize = @intCast(cluster.scalars.start); const scalar_end: usize = @intCast(cluster.scalars.end); while (scalar_index < scalar_end) : (scalar_index += 1) { const found = shape.joiningType(self.workspace.scalars.items[scalar_index].codepoint); if (found != .transparent) return found; } return null; } fn lastJoiningType(self: *const Context, cluster: SourceCluster) ?shape.JoiningType { var scalar_index: usize = @intCast(cluster.scalars.end); const scalar_start: usize = @intCast(cluster.scalars.start); while (scalar_index > scalar_start) { scalar_index -= 1; const found = shape.joiningType(self.workspace.scalars.items[scalar_index].codepoint); if (found != .transparent) return found; } return null; } fn assignCandidates(self: *Context, input: Input) bool { var uses_fallback = false; for (self.workspace.clusters.items) |*cluster| { const selected = self.bestCandidate(input, cluster.*); cluster.candidate_index = selected.candidate_index; cluster.missing_everywhere = selected.missing_everywhere; uses_fallback = uses_fallback or cluster.candidate_index != 0 or cluster.missing_everywhere; } return uses_fallback; } fn bestCandidate(self: *const Context, input: Input, cluster: SourceCluster) CandidateSelection { if (self.clusterCoveredBy(candidateAt(input, 0), cluster)) return .{ .candidate_index = 0, .missing_everywhere = false }; for (input.candidates, 0..) |_, candidate_index| { const selected_index = candidate_index + 1; if (self.clusterCoveredBy(candidateAt(input, selected_index), cluster)) { return .{ .candidate_index = selected_index, .missing_everywhere = false }; } } return .{ .candidate_index = 0, .missing_everywhere = true }; } fn clusterCoveredBy(self: *const Context, candidate: SelectedCandidate, cluster: SourceCluster) bool { var scalar_index: usize = @intCast(cluster.scalars.start); const scalar_end: usize = @intCast(cluster.scalars.end); while (scalar_index < scalar_end) { const scalar = self.workspace.scalars.items[scalar_index]; if (scalar_index + 1 < scalar_end) { const next = self.workspace.scalars.items[scalar_index + 1]; if (isVariationSelector(next.codepoint)) { if (candidate.font.face.glyphIdForVariation(scalar.codepoint, next.codepoint)) |glyph_id| { if (glyph_id == 0) return false; scalar_index += 2; continue; } } } if (!coverageIgnoresScalar(scalar.codepoint) and candidate.font.face.glyphId(scalar.codepoint) == 0) return false; scalar_index += 1; } return true; } fn buildRanges(self: *Context, split_by_script: bool) !void { self.workspace.ranges.clearRetainingCapacity(); var cluster_start: usize = 0; while (cluster_start < self.workspace.clusters.items.len) { const candidate_index = self.workspace.clusters.items[cluster_start].candidate_index; const missing_everywhere = self.workspace.clusters.items[cluster_start].missing_everywhere; const range_script = self.workspace.clusters.items[cluster_start].script; var cluster_end = cluster_start + 1; while (cluster_end < self.workspace.clusters.items.len and self.workspace.clusters.items[cluster_end].candidate_index == candidate_index and self.workspace.clusters.items[cluster_end].missing_everywhere == missing_everywhere and (!split_by_script or self.workspace.clusters.items[cluster_end].script == range_script)) { cluster_end += 1; } std.debug.assert(self.workspace.ranges.items.len < self.workspace.ranges.capacity); self.workspace.ranges.appendAssumeCapacity(.{ .cluster_start = @intCast(cluster_start), .cluster_end = @intCast(cluster_end), .candidate_index = candidate_index, .missing_everywhere = missing_everywhere, .script = range_script, }); cluster_start = cluster_end; } } fn shapeSegment( self: *Context, input: Input, range: SegmentRange, output: *shape.Output, segment_allocator: Allocator, segments: ?*std.ArrayListUnmanaged(Segment), ) !void { const cluster_start: usize = @intCast(range.cluster_start); const cluster_end: usize = @intCast(range.cluster_end); const source_start = self.workspace.clusters.items[cluster_start].source.start; const source_end = self.workspace.clusters.items[cluster_end - 1].source.end; const selected = candidateAt(input, range.candidate_index); const glyph_start = output.glyphs.items.len; var segment_input = input.base; segment_input.font = selected.font; segment_input.variations = selected.variations; segment_input.text = sliceSource(input.base.text, source_start, source_end); segment_input.source_offset = try addSourceOffset(input.base.source_offset, source_start); segment_input.pre_context = null; segment_input.post_context = null; if (usesAutomaticScript(input.base)) { const script_tags = unicode_data.scriptOpenTypeTags(range.script); segment_input.script = script_tags.tags[0]; segment_input.script_tags = script_tags.slice(); try self.shaper.shapeRun(segment_input, &self.segment_output); } else { try self.shaper.shapeRun(segment_input, &self.segment_output); } try appendSegmentOutput(output, &self.segment_output); try appendOutputSegment( segment_allocator, segments, .{ .start = try addSourceOffset(input.base.source_offset, source_start), .end = try addSourceOffset(input.base.source_offset, source_end), }, range.candidate_index, range.missing_everywhere, glyph_start, output.glyphs.items.len, ); }};Source: lib/filigree/src/root.zig:40
zig
pub const FallbackContext = fallback.Context;Also reachable as
Complete caller list for FallbackContext.deinit
16 direct callers.
lib.choir.src.core.test.test_Choir_parse_round-trips_atomic_memory_operations_and_their_orderings[function] — test source atlib/choir/src/core/test.zig:72in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_Choir_parse_round-trips_dumped_module_operations[function] — test source atlib/choir/src/core/test.zig:30in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_Choir_parse_round-trips_list_attributes[function] — test source atlib/choir/src/core/test.zig:178in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_pattern_rewriter_materialize_uses_type_converter[function] — test source atlib/choir/src/core/test.zig:2061in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_type_converter_addConversion_overwrites_existing_mapping[function] — test source atlib/choir/src/core/test.zig:2092in nearest public ownerlib.choir.src.core.testlib.filigree.src.fallback.core.test_Context_accepts_exact_fallback_source_capacity_and_rejects_max_plus_one_transactionally[function] — test source atlib/filigree/src/fallback/core.zig:661in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_itemizes_script_before_shaping_without_fallback_candidates[function] — test source atlib/filigree/src/fallback/core.zig:912in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_Arabic_joining_sequences_on_one_fallback_font[function] — test source atlib/filigree/src/fallback/core.zig:869in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_fallback_selection_on_grapheme_boundaries[function] — test source atlib/filigree/src/fallback/core.zig:786in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_variation_sequences_on_one_fallback_font[function] — test source atlib/filigree/src/fallback/core.zig:828in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_reports_a_final_notdef_segment_when_every_face_is_missing[function] — test source atlib/filigree/src/fallback/core.zig:625in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_reports_segmented_fallback_font_selection[function] — test source atlib/filigree/src/fallback/core.zig:583in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf16_clusters[function] — test source atlib/filigree/src/fallback/core.zig:708in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf32_clusters[function] — test source atlib/filigree/src/fallback/core.zig:747in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf8_clusters[function] — test source atlib/filigree/src/fallback/core.zig:545in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Filigree_fallback_exposes_script_source_unit_capacity[function] — test source atlib/filigree/src/fallback/core.zig:525in nearest public ownerlib.filigree.src.fallback.core
Complete caller list for FallbackContext.init
16 direct callers.
lib.choir.src.core.test.test_Choir_parse_round-trips_atomic_memory_operations_and_their_orderings[function] — test source atlib/choir/src/core/test.zig:72in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_Choir_parse_round-trips_dumped_module_operations[function] — test source atlib/choir/src/core/test.zig:30in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_Choir_parse_round-trips_list_attributes[function] — test source atlib/choir/src/core/test.zig:178in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_pattern_rewriter_materialize_uses_type_converter[function] — test source atlib/choir/src/core/test.zig:2061in nearest public ownerlib.choir.src.core.testlib.choir.src.core.test.test_type_converter_addConversion_overwrites_existing_mapping[function] — test source atlib/choir/src/core/test.zig:2092in nearest public ownerlib.choir.src.core.testlib.filigree.src.fallback.core.test_Context_accepts_exact_fallback_source_capacity_and_rejects_max_plus_one_transactionally[function] — test source atlib/filigree/src/fallback/core.zig:661in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_itemizes_script_before_shaping_without_fallback_candidates[function] — test source atlib/filigree/src/fallback/core.zig:912in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_Arabic_joining_sequences_on_one_fallback_font[function] — test source atlib/filigree/src/fallback/core.zig:869in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_fallback_selection_on_grapheme_boundaries[function] — test source atlib/filigree/src/fallback/core.zig:786in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_variation_sequences_on_one_fallback_font[function] — test source atlib/filigree/src/fallback/core.zig:828in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_reports_a_final_notdef_segment_when_every_face_is_missing[function] — test source atlib/filigree/src/fallback/core.zig:625in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_reports_segmented_fallback_font_selection[function] — test source atlib/filigree/src/fallback/core.zig:583in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf16_clusters[function] — test source atlib/filigree/src/fallback/core.zig:708in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf32_clusters[function] — test source atlib/filigree/src/fallback/core.zig:747in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf8_clusters[function] — test source atlib/filigree/src/fallback/core.zig:545in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Filigree_fallback_exposes_script_source_unit_capacity[function] — test source atlib/filigree/src/fallback/core.zig:525in nearest public ownerlib.filigree.src.fallback.core
Complete caller list for FallbackContext.shapeRun
7 direct callers.
lib.filigree.src.fallback.core.test_Context_itemizes_script_before_shaping_without_fallback_candidates[function] — test source atlib/filigree/src/fallback/core.zig:912in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_Arabic_joining_sequences_on_one_fallback_font[function] — test source atlib/filigree/src/fallback/core.zig:869in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_fallback_selection_on_grapheme_boundaries[function] — test source atlib/filigree/src/fallback/core.zig:786in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_keeps_variation_sequences_on_one_fallback_font[function] — test source atlib/filigree/src/fallback/core.zig:828in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf16_clusters[function] — test source atlib/filigree/src/fallback/core.zig:708in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf32_clusters[function] — test source atlib/filigree/src/fallback/core.zig:747in nearest public ownerlib.filigree.src.fallback.corelib.filigree.src.fallback.core.test_Context_uses_fallback_font_for_missing_utf8_clusters[function] — test source atlib/filigree/src/fallback/core.zig:545in nearest public ownerlib.filigree.src.fallback.core
Audit
| Definitions | 8 |
|---|---|
| Public names | 16 |
| Members | 5 |
| Version | 26.7.0 |
| Revision | daab053ee433 |