diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 324a9603..471817f7 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1259,9 +1259,9 @@ test "PDF section rows preserve DFS ordinals and sanitise hierarchy" { const rows = try PdfSectionRows.render(std.testing.allocator, "/tmp/manual.pdf", &entries); defer std.testing.allocator.free(rows); try std.testing.expectEqualStrings( - "/tmp/manual.pdf:3:1 [untitled]\n" ++ - "/tmp/manual.pdf:3:2 Child Title Café Next\n" ++ - "/tmp/manual.pdf:1:3 [empty title]\n" ++ + "manual.pdf:3:1 [untitled]\n" ++ + "manual.pdf:3:2 Child Title Café Next\n" ++ + "manual.pdf:1:3 [empty title]\n" ++ "https://example.com/manual External\n" ++ "https://example.com/deep Linked branch\n" ++ "https://example.com/deep Deep link\n", @@ -1373,7 +1373,7 @@ test "PdfSections output, Look, and n/N share exact cached outline destinations" "{s}:2:2 Café 子\n" ++ "{s}:3:3 [empty title]\n" ++ "https://example.com/manual External\n", - .{ path, path, path }, + .{ "outline.pdf", "outline.pdf", "outline.pdf" }, ); defer gpa.free(expected); try std.testing.expectEqualStrings(expected, output.file.?.content); @@ -3229,7 +3229,7 @@ pub const PdfSectionRows = if (pdf_enabled) struct { /// Resolve every rendered row in one DFS pass. `resolve` above stays the /// public single-ordinal policy used by Look; bulk materialisation avoids /// rescanning the same descendant chain for every structural ancestor. - /// The one-usize-per-entry table is transient (32 KiB at MuPDF's 4096 + /// The one-usize-per-entry table is transient (64 KiB at MuPDF's 8192 /// outline-item limit) and stores source ordinals, so URI slices continue /// to borrow from the document-owned outline instead of being copied. fn resolveOrdinals( @@ -3310,6 +3310,10 @@ pub const PdfSectionRows = if (pdf_enabled) struct { path: []const u8, entries: []const pdf_impl.OutlineEntry, ) ![]u8 { + // The output pane's directory is the PDF's directory, so a basename + // resolves to the same document while leaving room for the actual + // chapter title. An absolute prefix can consume the entire picker. + const target = std.fs.path.basename(path); const ordinals = try resolveOrdinals(gpa, entries); defer gpa.free(ordinals); var out: std.ArrayList(u8) = .empty; @@ -3320,7 +3324,7 @@ pub const PdfSectionRows = if (pdf_enabled) struct { const destination = usableDestination(entries[resolved].destination) orelse unreachable; switch (destination) { .internal => |internal| try out.print(gpa, "{s}:{d}:{d} ", .{ - path, + target, internal.page + 1, ordinal + 1, }), @@ -10552,17 +10556,28 @@ pub const Pardes = struct { if (at.line == 0 or at.col == 0 or at.end_line != 0 or !look.isPdfPath(path)) return false; + // Internal section rows use the basename so their titles remain + // visible. Resolve that token from +PdfSections' document directory + // before matching an open PDF or opening it again. + var joined_path: [4096]u8 = undefined; + const output_dir = paneDir(output); + const separator = if (std.mem.endsWith(u8, output_dir, "/")) "" else "/"; + const target_path = if (std.fs.path.isAbsolute(path)) + path + else + std.fmt.bufPrint(&joined_path, "{s}{s}{s}", .{ output_dir, separator, path }) catch return true; + var target_id: ?usize = null; for (p.panes, 0..) |slot, i| { const candidate = slot orelse continue; - if (candidate.pdf) |pv| if (std.mem.eql(u8, pv.path, path)) { + if (candidate.pdf) |pv| if (std.mem.eql(u8, pv.path, target_path)) { target_id = i; break; }; } if (target_id == null) { const free = p.freeSlot() orelse return true; - const fresh = p.openPdfView(free, path, 0) catch return true; + const fresh = p.openPdfView(free, target_path, 0) catch return true; p.placeDoc(output_id, free, fresh); p.computeGeom(); target_id = free; |
