lib/pdf/src/filter/model.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const pdf = @import("../root.zig");
4
5 const object = pdf.object;
6
7 pub const Bounds = struct {
8 input_bytes: usize,
9 decoded_bytes: usize,
10 };
11
12 pub const DecodeError = error{
13 UnsupportedFilter,
14 DecodeFailed,
15 };
16
17 pub const Exhaustion = error{
18 InputByteCapacityExceeded,
19 DecodedByteCapacityExceeded,
20 };
21
22 pub const Error = DecodeError || Exhaustion || error{DecodeStorageInUse};
23
24 pub const Predictor = struct {
25 predictor: u32 = 1,
26 colors: u32 = 1,
27 bits: u32 = 8,
28 columns: u32 = 1,
29
30 pub fn fromDict(dict: object.Dict) DecodeError!Predictor {
31 return .{
32 .predictor = try directUnsigned(dict, "Predictor", 1),
33 .colors = try directUnsigned(dict, "Colors", 1),
34 .bits = try directUnsigned(dict, "BitsPerComponent", 8),
35 .columns = try directUnsigned(dict, "Columns", 1),
36 };
37 }
38
39 pub fn rowLength(self: Predictor) DecodeError!usize {
40 if (self.columns == 0 or self.colors == 0) return error.DecodeFailed;
41 const valid_bits = self.bits == 1 or self.bits == 2 or self.bits == 4 or
42 self.bits == 8 or self.bits == 16;
43 if (!valid_bits) return error.DecodeFailed;
44 const channel_bits = std.math.mul(u64, self.colors, self.bits) catch
45 return error.DecodeFailed;
46 const row_bits = std.math.mul(u64, self.columns, channel_bits) catch
47 return error.DecodeFailed;
48 return std.math.cast(usize, (row_bits + 7) / 8) orelse error.DecodeFailed;
49 }
50
51 pub fn sampleLength(self: Predictor) DecodeError!usize {
52 const sample_bits = std.math.mul(u64, self.colors, self.bits) catch
53 return error.DecodeFailed;
54 const bytes = std.math.cast(usize, (sample_bits + 7) / 8) orelse
55 return error.DecodeFailed;
56 return @max(1, bytes);
57 }
58
59 fn directUnsigned(dict: object.Dict, key: []const u8, default: u32) DecodeError!u32 {
60 const value = dict.get(key) orelse return default;
61 return switch (value) {
62 .integer => |raw| if (raw >= 0 and raw <= std.math.maxInt(u32))
63 @intCast(raw)
64 else
65 error.UnsupportedFilter,
66 else => error.UnsupportedFilter,
67 };
68 }
69 };
70
71 pub fn single(value: object.Value) DecodeError!object.Value {
72 return switch (value) {
73 .array => |items| switch (items.len) {
74 0 => .null,
75 1 => items[0],
76 else => error.UnsupportedFilter,
77 },
78 else => value,
79 };
80 }
81
82 pub fn nameOf(value: object.Value) DecodeError!?[]const u8 {
83 return switch (value) {
84 .null => null,
85 .name => |name| name,
86 else => error.UnsupportedFilter,
87 };
88 }
89
90 pub fn paramsOf(value: object.Value) DecodeError!Predictor {
91 return switch (value) {
92 .null => .{},
93 .dict => |dict| Predictor.fromDict(dict),
94 else => error.UnsupportedFilter,
95 };
96 }
97
98 test "PDF filter shape helpers unwrap values" {
99 const name_value = object.Value{ .name = "FlateDecode" };
100 try std.testing.expectEqualStrings("FlateDecode", (try nameOf(try single(name_value))).?);
101 var items = [_]object.Value{name_value};
102 try std.testing.expectEqualStrings(
103 "FlateDecode",
104 (try nameOf(try single(.{ .array = &items }))).?,
105 );
106 try std.testing.expectEqual(@as(?[]const u8, null), try nameOf(.null));
107 try std.testing.expectError(error.UnsupportedFilter, nameOf(.{ .integer = 4 }));
108 var two = [_]object.Value{ name_value, name_value };
109 try std.testing.expectError(error.UnsupportedFilter, single(.{ .array = &two }));
110 }