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 }