Skip to documentation
SLOP

tiny.filigree.FallbackContext

Reference tiny.filigree FallbackContext

Defined in tiny.filigree.

API (13)

Actions

Public operations.

Types and contracts

Public types and contracts.

Fields and members

Public fields and members.

No direct callersNo direct callstiny.filigreeFallbackContext
Static calls · unresolved targets: unknown · external targets: unknown.

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;
Called byCallsNo direct callstest sourcelib.choir.src.core.testtest: Choir parse round-trips atomic ...test sourcelib.choir.src.core.testtest: Choir parse round-trips dumped ...test sourcelib.choir.src.core.testtest: Choir parse round-trips list at...test sourcelib.choir.src.core.testtest: pattern rewriter materialize us...test sourcelib.choir.src.core.testtest: type converter addConversion ov...+11 moreFallbackContextdeinit
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsNo direct callstest sourcelib.choir.src.core.testtest: Choir parse round-trips atomic ...test sourcelib.choir.src.core.testtest: Choir parse round-trips dumped ...test sourcelib.choir.src.core.testtest: Choir parse round-trips list at...test sourcelib.choir.src.core.testtest: pattern rewriter materialize us...test sourcelib.choir.src.core.testtest: type converter addConversion ov...+11 moreFallbackContextinit
Static calls · unresolved targets: 0 · external targets: 8.
Called byCallsNo direct callsprivate sourcelib.filigree.src.fallback.core.ContextshapeRunWithSegmentsFallbackContextreset
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsNo direct callstest sourcelib.filigree.src.fallback.coretest: Filigree fallback exposes scrip...FallbackContextscriptRunStatus
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.filigree.src.fallback.coretest: Context itemizes script before ...test sourcelib.filigree.src.fallback.coretest: Context keeps Arabic joining se...test sourcelib.filigree.src.fallback.coretest: Context keeps fallback selectio...test sourcelib.filigree.src.fallback.coretest: Context keeps variation sequenc...test sourcelib.filigree.src.fallback.coretest: Context uses fallback font for ...+2 moreprivate sourcelib.filigree.src.fallback.core.ContextshapeRunWithSegmentsFallbackContextshapeRun
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.filigree.src.fallback.coretest: Context accepts exact fallback ...test sourcelib.filigree.src.fallback.coretest: Context reports a final notdef ...test sourcelib.filigree.src.fallback.coretest: Context reports segmented fallb...private sourcelib.filigree.src.fallback.core.ContextshapeRunWithSegmentsFallbackContextshapeRunSegmented
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.filigree.src.fallback.coretest: Context accepts exact fallback ...FallbackContextworkspaceStatus
Static calls · unresolved targets: 0 · external targets: 1.

Also reachable as

fallback.Context.

Complete caller list for FallbackContext.deinit

16 direct callers.

Complete caller list for FallbackContext.init

16 direct callers.

Complete caller list for FallbackContext.shapeRun

7 direct callers.

Audit

Definitions8
Public names16
Members5
Version26.7.0
Revisiondaab053ee433