diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-03 10:02:00 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-10 09:17:07 -0300 |
| commit | 7452ff1caf51942ba9561acf2aa284d50435b8c3 (patch) | |
| tree | 5be0d58e16b3ee9810377131f1d83c06f10b69bf /test/image_harness.zig | |
| parent | cd4a5b991104a2dc96a4bb7390451fb384376a9a (diff) | |
| download | pardes-7452ff1caf51942ba9561acf2aa284d50435b8c3.tar.gz pardes-7452ff1caf51942ba9561acf2aa284d50435b8c3.zip | |
optimize and harness PDF sections navigation
Diffstat (limited to 'test/image_harness.zig')
| -rw-r--r-- | test/image_harness.zig | 93 |
1 files changed, 85 insertions, 8 deletions
diff --git a/test/image_harness.zig b/test/image_harness.zig index 4b1c527c..73142459 100644 --- a/test/image_harness.zig +++ b/test/image_harness.zig @@ -126,11 +126,11 @@ fn writePdf(arena: std.mem.Allocator, path: [:0]const u8) !void { ; var out: std.ArrayList(u8) = .empty; - var offsets: [12]usize = @splat(0); + var offsets: [16]usize = @splat(0); try out.appendSlice(arena, "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n"); offsets[1] = out.items.len; - try out.appendSlice(arena, "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n"); + try out.appendSlice(arena, "1 0 obj\n<< /Type /Catalog /Pages 2 0 R /Outlines 12 0 R /PageMode /UseOutlines >>\nendobj\n"); offsets[2] = out.items.len; try out.appendSlice(arena, "2 0 obj\n<< /Type /Pages /Count 4 /Kids [3 0 R 5 0 R 7 0 R 9 0 R] >>\nendobj\n"); offsets[3] = out.items.len; @@ -151,12 +151,20 @@ fn writePdf(arena: std.mem.Allocator, path: [:0]const u8) !void { try appendFmt(arena, &out, "10 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ fourth_stream.len, fourth_stream }); offsets[11] = out.items.len; try out.appendSlice(arena, "11 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n"); + offsets[12] = out.items.len; + try out.appendSlice(arena, "12 0 obj\n<< /Type /Outlines /First 13 0 R /Last 15 0 R /Count 3 >>\nendobj\n"); + offsets[13] = out.items.len; + try out.appendSlice(arena, "13 0 obj\n<< /Title (Upper) /Parent 12 0 R /First 14 0 R /Last 14 0 R /Next 15 0 R /Count 1 /Dest [3 0 R /XYZ 4 8 null] >>\nendobj\n"); + offsets[14] = out.items.len; + try out.appendSlice(arena, "14 0 obj\n<< /Title (Lower) /Parent 13 0 R /Dest [3 0 R /XYZ 4 28 null] >>\nendobj\n"); + offsets[15] = out.items.len; + try out.appendSlice(arena, "15 0 obj\n<< /Title (Page two) /Parent 12 0 R /Prev 13 0 R /Dest [5 0 R /XYZ 4 38 null] >>\nendobj\n"); const xref = out.items.len; - try out.appendSlice(arena, "xref\n0 12\n0000000000 65535 f \n"); + try out.appendSlice(arena, "xref\n0 16\n0000000000 65535 f \n"); for (offsets[1..]) |offset| try appendFmt(arena, &out, "{d:0>10} 00000 n \n", .{offset}); - try appendFmt(arena, &out, "trailer\n<< /Size 12 /Root 1 0 R >>\nstartxref\n{d}\n%%EOF\n", .{xref}); + try appendFmt(arena, &out, "trailer\n<< /Size 16 /Root 1 0 R >>\nstartxref\n{d}\n%%EOF\n", .{xref}); try eh.writeFile(path, out.items); } @@ -213,8 +221,12 @@ fn runKitty(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !vo fn runKittyPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !void { _ = unsetenv("PARDES_TEST"); _ = unsetenv("PARDES_TEST_CAPTURE_DIR"); - const pdf_path = try std.fmt.allocPrintSentinel(arena, "{s}/pages.pdf", .{base}, 0); + _ = base; + // Keep the generated location token short enough that the 38-column PDF + // pane can visibly include each outline title after `path:page:ordinal`. + const pdf_path = try std.fmt.allocPrintSentinel(arena, "/tmp/p{d}.pdf", .{libc.getpid()}, 0); try writePdf(arena, pdf_path); + defer _ = libc.unlink(pdf_path); var h = try eh.Harness.initArgs(gpa, exe, 16, 80, pdf_path); defer h.deinit(); @@ -308,7 +320,8 @@ fn runKittyPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) try h.send("\x02"); try h.expectWaitFor("pdf 1/4 width PdfFit", 5000, "continuous page-up did not return to PDF page one"); try h.send("/SECOND\r"); - try h.expectWaitFor("pages.pdf:2:1 SECOND", 5000, "PDF search did not preserve the exact page-two query occurrence"); + const search_row = try std.fmt.allocPrint(arena, "{s}:2:1 SECOND", .{std.fs.path.basename(pdf_path)}); + try h.expectWaitFor(search_row, 5000, "PDF search did not preserve the exact page-two query occurrence"); const before_look = std.mem.count(u8, h.raw.items, transmit); try h.send("n"); const look_deadline = eh.nowMs() + 5000; @@ -337,7 +350,47 @@ fn runKittyPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) if (std.mem.count(u8, h.raw.items, transmit) != before_fit) return error.KittyPdfFitRetransmitted; - std.debug.print("PDF harness ok: Kitty continuous boundary + exact cache + search/fit\n", .{}); + // The generated PDF carries a real nested outline. PdfSections must show + // all three DFS rows in its output pane while leaving n/N ownership on + // the PDF. The first two destinations share page one but have distinct + // page-space y coordinates; their placement APCs must differ while the + // second reveal reuses exactly the rasters made visible by the first. + try h.send(" ts"); + try h.expectWaitFor("Upper", 5000, "PdfSections did not expose the root outline row"); + try h.expectContains("Lower", "PdfSections did not expose the nested outline row"); + try h.expectContains("Page two", "PdfSections did not expose the other-page outline row"); + + const upper_start = h.raw.items.len; + try h.send("n"); + try h.expectWaitFor("pdf 1/4 height PdfFit", 5000, "first PdfSections n did not reveal page one"); + const upper_deadline = eh.nowMs() + 3000; + while (eh.nowMs() < upper_deadline and kittyPlacementApc(h.raw.items, upper_start) == null) + _ = try h.pumpOnce(50); + const upper_apc = kittyPlacementApc(h.raw.items, upper_start) orelse + return error.KittyPdfSectionsUpperPlacementMissing; + const upper_copy = try arena.dupe(u8, upper_apc); + try h.pump(150); + const same_page_transmits = std.mem.count(u8, h.raw.items, "\x1b_Gf=32,"); + + const lower_start = h.raw.items.len; + try h.send("n"); + try h.expectWaitFor("pdf 1/4 height PdfFit", 5000, "nested PdfSections n left page one"); + const lower_deadline = eh.nowMs() + 3000; + while (eh.nowMs() < lower_deadline and kittyPlacementApc(h.raw.items, lower_start) == null) + _ = try h.pumpOnce(50); + const lower_apc = kittyPlacementApc(h.raw.items, lower_start) orelse + return error.KittyPdfSectionsLowerPlacementMissing; + if (std.mem.eql(u8, upper_copy, lower_apc)) + return error.KittyPdfSectionsSamePagePlacementUnchanged; + if (std.mem.count(u8, h.raw.items, "\x1b_Gf=32,") != same_page_transmits) + return error.KittyPdfSectionsSamePageRetransmitted; + + try h.send("n"); + try h.expectWaitFor("pdf 2/4 height PdfFit", 5000, "third PdfSections n did not reveal page two"); + try h.send("N"); + try h.expectWaitFor("pdf 1/4 height PdfFit", 5000, "PdfSections N did not return to the nested page-one row"); + + std.debug.print("PDF harness ok: Kitty continuous cache + search/fit + nested PdfSections n/N\n", .{}); } fn kittyPlacementApc(raw: []const u8, start: usize) ?[]const u8 { @@ -465,7 +518,31 @@ fn runGuiPdf(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !v defer gpa.free(final_capture); const final_pixels = ppmPixels(final_capture) orelse return error.BadCapture; if (countColor(final_pixels, pdf_red) != 0) return error.SdlPdfOffscreenPageStillDrawn; - std.debug.print("PDF harness ok: SDL continuous strip + theme gap + red->green ({d}->{d} pixels)\n", .{ fit_width.count, green.count }); + + // Exercise the actual PdfSections output + Look stepping path in SDL too. + // The first two nested outline rows stay on red page one at different y + // positions, the third moves to green page two, and N returns exactly to + // the nested page-one destination. + _ = libc.unlink(capture_path); + try h.send("gg"); + _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000); + _ = libc.unlink(capture_path); + try h.send(" ts"); + _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 500, 5000); + _ = libc.unlink(capture_path); + try h.send("n"); + _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 200, 5000); + _ = libc.unlink(capture_path); + try h.send("n"); + _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 100, 5000); + _ = libc.unlink(capture_path); + try h.send("n"); + _ = try waitForCaptureBounds(&h, capture_path, pdf_green, 200, 5000); + _ = libc.unlink(capture_path); + try h.send("N"); + _ = try waitForCaptureBounds(&h, capture_path, pdf_red, 100, 5000); + + std.debug.print("PDF harness ok: SDL continuous strip + nested PdfSections red->green->red ({d}->{d} pixels)\n", .{ fit_width.count, green.count }); } const ColorBounds = struct { |
