Skip to documentation
SLOP

tiny.pdf.object

Reference tiny.pdf object

Defined in tiny.pdf.

API (19)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsprivate sourcelib.pdf.src.object.ParserparseKeywordValueobject.ParserstreamStartprivate sourcelib.pdf.src.xrefparseSectionobject.ParserskipWhitespaceobjectdelimiterOrWhitespaceobject.ParseratKeyword
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pdf.src.document.ObjectAccessparseObjectprivate sourcelib.pdf.src.xrefparseStreamSectionobject.ParserskipWhitespaceobject.ParserexpectKeyword
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callscmapparseAlloccontentextractTextAppendprivate sourcelib.pdf.src.document.ObjectAccessloadContainerprivate sourcelib.pdf.src.document.ObjectAccessobjectStreamprivate sourcelib.pdf.src.document.ObjectAccessparseObject+4 moreobject.Parserinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pdf.src.document.ObjectAccessloadContainerprivate sourcelib.pdf.src.document.ObjectAccessparseObjectprivate sourcelib.pdf.src.xrefparseStreamSectionprivate sourcelib.pdf.src.xrefstartxrefobject.ParserskipWhitespaceobject.ParserparseUnsigned
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallscmapparseAlloccontentextractTextAppendprivate sourcelib.pdf.src.document.ObjectAccessparseObjectprivate sourcelib.pdf.src.object.ParserparseArrayprivate sourcelib.pdf.src.object.ParserparseDict+2 moreprivate sourcelib.pdf.src.object.ParserparseArrayprivate sourcelib.pdf.src.object.ParserparseDictprivate sourcelib.pdf.src.object.ParserparseHexStringprivate sourcelib.pdf.src.object.ParserparseKeywordValueprivate sourcelib.pdf.src.object.ParserparseLiteralString+3 moreobject.ParserparseValue
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallscmapparseAlloccontentextractTextAppendobject.ParseratKeywordobject.ParserexpectKeywordprivate sourcelib.pdf.src.object.ParserparseArray+6 moreobjectwhitespaceobject.ParserskipWhitespace
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pdf.src.document.ObjectAccessobjectStreamprivate sourcelib.pdf.src.xrefparseStreamSectionobject.ParseratKeywordobject.ParserstreamStart
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.pdf.src.contentappendStringtest sourcelib.pdf.src.objecttest: literal and hex decoding handle...objectdecodeHexAppend
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.pdf.src.contentappendStringtest sourcelib.pdf.src.objecttest: literal and hex decoding handle...objectdecodeLiteralAppend
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallscmapparseAlloccontentextractTextAppendobject.ParseratKeywordprivate sourcelib.pdf.src.object.ParserparseNameprivate sourcelib.pdf.src.object.ParsertryReferenceSuffixobjectwhitespaceobjectdelimiterOrWhitespace
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsobject.ParserskipWhitespaceobjectdelimiterOrWhitespaceprivate sourcelib.pdf.src.xrefparseTableSectionobjectwhitespace
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/pdf/src/object.zig

zig
const std = @import("std");pub const ParseError = error{    UnexpectedEnd,    UnexpectedToken,    BadNumber,    BadName,    BadString,    BadDictionary,    OutOfMemory,};pub const Reference = struct {    number: u32,    generation: u16,};pub const Entry = struct {    key: []const u8,    value: Value,};pub const Dict = struct {    entries: []Entry = &.{},    pub fn get(self: Dict, key: []const u8) ?Value {        for (self.entries) |entry| {            if (std.mem.eql(u8, entry.key, key)) return entry.value;        }        return null;    }};pub const StringKind = enum {    literal,    hex,};pub const String = struct {    kind: StringKind,    raw: []const u8,};pub const Value = union(enum) {    null,    boolean: bool,    integer: i64,    real: f64,    name: []const u8,    string: String,    array: []Value,    dict: Dict,    reference: Reference,};const NumberToken = union(enum) {    integer: i64,    real: f64,};pub const Parser = struct {    bytes: []const u8,    pos: usize,    pub fn init(bytes: []const u8, pos: usize) Parser {        return .{ .bytes = bytes, .pos = pos };    }    pub fn skipWhitespace(self: *Parser) void {        if (self.pos >= self.bytes.len) return;        for (self.bytes[self.pos..]) |_| {            if (self.pos >= self.bytes.len) return;            const byte = self.bytes[self.pos];            if (whitespace(byte)) {                self.pos += 1;                continue;            }            if (byte == '%') {                while (self.pos < self.bytes.len and self.bytes[self.pos] != '\n' and self.bytes[self.pos] != '\r') self.pos += 1;                continue;            }            return;        }    }    pub fn parseValue(self: *Parser, arena: std.mem.Allocator) ParseError!Value {        self.skipWhitespace();        if (self.pos >= self.bytes.len) return error.UnexpectedEnd;        const byte = self.bytes[self.pos];        switch (byte) {            '/' => return .{ .name = try self.parseName() },            '(' => return .{ .string = .{ .kind = .literal, .raw = try self.parseLiteralString() } },            '[' => return .{ .array = try self.parseArray(arena) },            '<' => {                if (self.pos + 1 < self.bytes.len and self.bytes[self.pos + 1] == '<') {                    return .{ .dict = try self.parseDict(arena) };                }                return .{ .string = .{ .kind = .hex, .raw = try self.parseHexString() } };            },            't', 'f', 'n' => return try self.parseKeywordValue(),            else => return try self.parseNumberOrReference(),        }    }    pub fn expectKeyword(self: *Parser, keyword: []const u8) ParseError!void {        self.skipWhitespace();        if (self.pos + keyword.len > self.bytes.len) return error.UnexpectedEnd;        if (!std.mem.eql(u8, self.bytes[self.pos .. self.pos + keyword.len], keyword)) return error.UnexpectedToken;        self.pos += keyword.len;    }    pub fn atKeyword(self: *Parser, keyword: []const u8) bool {        self.skipWhitespace();        if (self.pos + keyword.len > self.bytes.len) return false;        if (!std.mem.eql(u8, self.bytes[self.pos .. self.pos + keyword.len], keyword)) return false;        const end = self.pos + keyword.len;        if (end < self.bytes.len and !delimiterOrWhitespace(self.bytes[end])) return false;        return true;    }    pub fn streamStart(self: *Parser) ParseError!usize {        if (!self.atKeyword("stream")) return error.UnexpectedToken;        self.pos += "stream".len;        if (self.pos < self.bytes.len and self.bytes[self.pos] == '\r') self.pos += 1;        if (self.pos < self.bytes.len and self.bytes[self.pos] == '\n') self.pos += 1;        return self.pos;    }    pub fn parseUnsigned(self: *Parser, comptime T: type) ParseError!T {        self.skipWhitespace();        const start = self.pos;        while (self.pos < self.bytes.len and std.ascii.isDigit(self.bytes[self.pos])) self.pos += 1;        if (self.pos == start) return error.BadNumber;        return std.fmt.parseUnsigned(T, self.bytes[start..self.pos], 10) catch error.BadNumber;    }    fn parseName(self: *Parser) ParseError![]const u8 {        self.pos += 1;        const start = self.pos;        while (self.pos < self.bytes.len and !delimiterOrWhitespace(self.bytes[self.pos])) self.pos += 1;        if (self.pos == start) return error.BadName;        return self.bytes[start..self.pos];    }    fn parseLiteralString(self: *Parser) ParseError![]const u8 {        self.pos += 1;        const start = self.pos;        var depth: usize = 1;        for (self.bytes[self.pos..]) |_| {            if (self.pos >= self.bytes.len) break;            const byte = self.bytes[self.pos];            if (byte == '\\') {                self.pos += @min(2, self.bytes.len - self.pos);                continue;            }            if (byte == '(') depth += 1;            if (byte == ')') {                depth -= 1;                if (depth == 0) {                    const raw = self.bytes[start..self.pos];                    self.pos += 1;                    return raw;                }            }            self.pos += 1;        }        return error.BadString;    }    fn parseHexString(self: *Parser) ParseError![]const u8 {        self.pos += 1;        const start = self.pos;        while (self.pos < self.bytes.len and self.bytes[self.pos] != '>') self.pos += 1;        if (self.pos >= self.bytes.len) return error.BadString;        const raw = self.bytes[start..self.pos];        self.pos += 1;        return raw;    }    fn parseArray(self: *Parser, arena: std.mem.Allocator) ParseError![]Value {        self.pos += 1;        var items: std.ArrayList(Value) = .empty;        for (self.bytes[self.pos..]) |_| {            self.skipWhitespace();            if (self.pos >= self.bytes.len) return error.UnexpectedEnd;            if (self.bytes[self.pos] == ']') {                self.pos += 1;                return try items.toOwnedSlice(arena);            }            try items.append(arena, try self.parseValue(arena));        }        return error.UnexpectedEnd;    }    fn parseDict(self: *Parser, arena: std.mem.Allocator) ParseError!Dict {        self.pos += 2;        var entries: std.ArrayList(Entry) = .empty;        for (self.bytes[self.pos..]) |_| {            self.skipWhitespace();            if (self.pos + 1 < self.bytes.len and self.bytes[self.pos] == '>' and self.bytes[self.pos + 1] == '>') {                self.pos += 2;                return .{ .entries = try entries.toOwnedSlice(arena) };            }            if (self.pos >= self.bytes.len) return error.UnexpectedEnd;            if (self.bytes[self.pos] != '/') return error.BadDictionary;            const key = try self.parseName();            const value = try self.parseValue(arena);            try entries.append(arena, .{ .key = key, .value = value });        }        return error.UnexpectedEnd;    }    fn parseKeywordValue(self: *Parser) ParseError!Value {        if (self.atKeyword("true")) {            self.pos += 4;            return .{ .boolean = true };        }        if (self.atKeyword("false")) {            self.pos += 5;            return .{ .boolean = false };        }        if (self.atKeyword("null")) {            self.pos += 4;            return .null;        }        return error.UnexpectedToken;    }    fn parseNumberOrReference(self: *Parser) ParseError!Value {        const first = try self.parseNumberToken();        switch (first) {            .real => |value| return .{ .real = value },            .integer => |value| {                const saved = self.pos;                if (self.tryReferenceSuffix()) |generation| {                    if (value >= 0 and value <= std.math.maxInt(u32)) {                        return .{ .reference = .{ .number = @intCast(value), .generation = generation } };                    }                }                self.pos = saved;                return .{ .integer = value };            },        }    }    fn parseNumberToken(self: *Parser) ParseError!NumberToken {        self.skipWhitespace();        const start = self.pos;        var saw_dot = false;        if (self.pos < self.bytes.len and (self.bytes[self.pos] == '+' or self.bytes[self.pos] == '-')) self.pos += 1;        for (self.bytes[self.pos..]) |byte| {            if (std.ascii.isDigit(byte)) {                self.pos += 1;                continue;            }            if (byte == '.' and !saw_dot) {                saw_dot = true;                self.pos += 1;                continue;            }            break;        }        const text = self.bytes[start..self.pos];        if (text.len == 0 or std.mem.eql(u8, text, "+") or std.mem.eql(u8, text, "-")) return error.BadNumber;        if (saw_dot) {            return .{ .real = std.fmt.parseFloat(f64, text) catch return error.BadNumber };        }        return .{ .integer = std.fmt.parseInt(i64, text, 10) catch return error.BadNumber };    }    fn tryReferenceSuffix(self: *Parser) ?u16 {        const saved = self.pos;        self.skipWhitespace();        const generation_start = self.pos;        while (self.pos < self.bytes.len and std.ascii.isDigit(self.bytes[self.pos])) self.pos += 1;        if (self.pos == generation_start) {            self.pos = saved;            return null;        }        const generation = std.fmt.parseUnsigned(u16, self.bytes[generation_start..self.pos], 10) catch {            self.pos = saved;            return null;        };        self.skipWhitespace();        if (self.pos < self.bytes.len and self.bytes[self.pos] == 'R') {            const after = self.pos + 1;            if (after >= self.bytes.len or delimiterOrWhitespace(self.bytes[after])) {                self.pos = after;                return generation;            }        }        self.pos = saved;        return null;    }};pub fn whitespace(byte: u8) bool {    return byte == ' ' or byte == '\t' or byte == '\r' or byte == '\n' or byte == '\x0C' or byte == 0;}pub fn delimiterOrWhitespace(byte: u8) bool {    return whitespace(byte) or switch (byte) {        '(', ')', '<', '>', '[', ']', '{', '}', '/', '%' => true,        else => false,    };}pub fn decodeLiteralAppend(allocator: std.mem.Allocator, out: *std.ArrayList(u8), raw: []const u8) !void {    var index: usize = 0;    for (raw) |_| {        if (index >= raw.len) break;        const byte = raw[index];        if (byte != '\\') {            try out.append(allocator, byte);            index += 1;            continue;        }        index += 1;        if (index >= raw.len) break;        const escaped = raw[index];        switch (escaped) {            'n' => try out.append(allocator, '\n'),            'r' => try out.append(allocator, '\r'),            't' => try out.append(allocator, '\t'),            'b' => try out.append(allocator, 0x08),            'f' => try out.append(allocator, 0x0C),            '(', ')', '\\' => try out.append(allocator, escaped),            '\n' => {},            '\r' => {                if (index + 1 < raw.len and raw[index + 1] == '\n') index += 1;            },            '0'...'7' => {                var code: u16 = 0;                var digits: usize = 0;                while (digits < 3 and index < raw.len and raw[index] >= '0' and raw[index] <= '7') {                    code = code * 8 + (raw[index] - '0');                    index += 1;                    digits += 1;                }                try out.append(allocator, @truncate(code));                continue;            },            else => try out.append(allocator, escaped),        }        index += 1;    }}pub fn decodeHexAppend(allocator: std.mem.Allocator, out: *std.ArrayList(u8), raw: []const u8) !void {    var high: ?u8 = null;    for (raw) |byte| {        const nibble: u8 = switch (byte) {            '0'...'9' => byte - '0',            'a'...'f' => byte - 'a' + 10,            'A'...'F' => byte - 'A' + 10,            else => continue,        };        if (high) |value| {            try out.append(allocator, (value << 4) | nibble);            high = null;        } else {            high = nibble;        }    }    if (high) |value| try out.append(allocator, value << 4);}test "parser reads scalar values names strings and references" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const arena = arena_state.allocator();    const source =        "% lead comment\n" ++        "<< /Type /Page /Count 3 /Rot -90 /Scale 1.5 /Parent 2 0 R " ++        "/Label (hi \\(there\\)) /Id <48690A> /Flags [true false null] >>";    var parser = Parser.init(source, 0);    const value = try parser.parseValue(arena);    const dict = value.dict;    try std.testing.expectEqualStrings("Page", dict.get("Type").?.name);    try std.testing.expectEqual(@as(i64, 3), dict.get("Count").?.integer);    try std.testing.expectEqual(@as(i64, -90), dict.get("Rot").?.integer);    try std.testing.expectEqual(@as(f64, 1.5), dict.get("Scale").?.real);    try std.testing.expectEqual(@as(u32, 2), dict.get("Parent").?.reference.number);    try std.testing.expectEqualStrings("hi \\(there\\)", dict.get("Label").?.string.raw);    try std.testing.expectEqual(StringKind.hex, dict.get("Id").?.string.kind);    try std.testing.expectEqual(@as(usize, 3), dict.get("Flags").?.array.len);    var literal = Parser.init("(", 0);    try std.testing.expectError(error.BadString, literal.parseValue(arena));    var array = Parser.init("[1", 0);    try std.testing.expectError(error.UnexpectedEnd, array.parseValue(arena));    var truncated_dict = Parser.init("<< /A 1", 0);    try std.testing.expectError(error.UnexpectedEnd, truncated_dict.parseValue(arena));    var comment = Parser.init("% trailing comment", 0);    comment.skipWhitespace();    try std.testing.expectEqual(comment.bytes.len, comment.pos);}test "literal and hex decoding handle escapes and odd digits" {    var out = std.ArrayList(u8).empty;    defer out.deinit(std.testing.allocator);    try decodeLiteralAppend(std.testing.allocator, &out, "A\\nB\\051C\\\\D");    try std.testing.expectEqualStrings("A\nB)C\\D", out.items);    out.clearRetainingCapacity();    try decodeLiteralAppend(std.testing.allocator, &out, "A\\");    try std.testing.expectEqualStrings("A", out.items);    out.clearRetainingCapacity();    try decodeHexAppend(std.testing.allocator, &out, "48 65 6C 6C 6F 2");    try std.testing.expectEqualStrings("Hello ", out.items);}

Source: lib/pdf/src/root.zig:15

zig
pub const object = @import("object.zig");

Complete caller list for object.Parser.init

9 direct callers.

Complete caller list for object.Parser.parseValue

7 direct callers.

Complete call list for object.Parser.parseValue

8 direct calls.

Complete caller list for object.Parser.skipWhitespace

11 direct callers.

Audit

Definitions17
Public names17
Members15
Version26.7.0
Revisiondaab053ee433