diff options
Diffstat (limited to 'src/pdf.zig')
| -rw-r--r-- | src/pdf.zig | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/src/pdf.zig b/src/pdf.zig index 052d34db..712750dc 100644 --- a/src/pdf.zig +++ b/src/pdf.zig @@ -976,6 +976,20 @@ pub const Document = struct { highlights: []const Highlight, rgba: []u8, ) !void { + return document.renderIntoAtWithPaper(page, request, raster, band, highlights, rgba, false); + } + + /// Preserve the PDF's content coverage when the host paints paper separately. + pub fn renderIntoAtWithPaper( + document: *Document, + page: usize, + request: RenderRequest, + raster: Raster, + band: Raster.Band, + highlights: []const Highlight, + rgba: []u8, + transparent_paper: bool, + ) !void { if (rgba.len != band.len) return error.BadPixmap; if (band.height == 0 or band.y + band.height > raster.height) return error.BadPixmap; @@ -999,6 +1013,7 @@ pub const Document = struct { @intCast(raster.stride), @intCast(band.y), @intCast(band.height), + @intFromBool(transparent_paper), ) != c.PARDES_PDF_OK) return error.RenderFailed; } @@ -1832,6 +1847,7 @@ test "RGBA allocation and buffer-validation failures leave the document renderab raw_layout.stride, 0, raw_layout.height, + 0, )); for (scratch) |byte| try std.testing.expectEqual(@as(u8, 0xa5), byte); @@ -2074,3 +2090,35 @@ test "offset crop and rotation keep normalized highlights pixel-aligned" { marked.rgba[left + 1] < 10 and marked.rgba[left + 2] > 240); try std.testing.expectEqualSlices(u8, plain.rgba[right .. right + 4], marked.rgba[right .. right + 4]); } + +test "transparent PDF paper preserves ink and band coverage" { + var document = try Document.open("docs/9p.pdf"); + defer document.deinit(); + const request: RenderRequest = .{ .dpi = 96, .max_dimension = 1200 }; + const raster = try document.measureRenderAt(0, request); + const flattened = try std.testing.allocator.alloc(u8, raster.len); + defer std.testing.allocator.free(flattened); + const content = try std.testing.allocator.alloc(u8, raster.len); + defer std.testing.allocator.free(content); + try document.renderIntoAt(0, request, raster, raster.wholePage(), &.{}, flattened); + try document.renderIntoAtWithPaper(0, request, raster, raster.wholePage(), &.{}, content, true); + var paper: usize = 0; + var ink: usize = 0; + var edges: usize = 0; + for (std.mem.bytesAsSlice([4]u8, content), std.mem.bytesAsSlice([4]u8, flattened)) |pixel, reference| { + const a: u32 = pixel[3]; + paper += @intFromBool(a == 0); + ink += @intFromBool(a == 255); + edges += @intFromBool(a > 0 and a < 255); + for (0..3) |channel| { + const over_white = (a * pixel[channel] + (255 - a) * 255 + 127) / 255; + try std.testing.expect(@abs(@as(i32, @intCast(over_white)) - @as(i32, reference[channel])) <= 2); + } + } + try std.testing.expect(paper > 100 and ink > 100 and edges > 100); + const band = raster.band(raster.height / 3, raster.height / 4); + const rows = try std.testing.allocator.alloc(u8, band.len); + defer std.testing.allocator.free(rows); + try document.renderIntoAtWithPaper(0, request, raster, band, &.{}, rows, true); + try std.testing.expectEqualSlices(u8, content[band.y * raster.stride ..][0..band.len], rows); +} |
