mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-10 04:22:34 +00:00
* A hyperlink can point at a text anchor (#1446) A frame or a text range could link to a URL, an email address or a page number. A page number is the wrong thing to store for "this thumbnail links to that photo": the photo moves and the link does not. InDesign stores the destination, and IDML files do too — a `HyperlinkPageItemSource` pointing at a `HyperlinkTextDestination` is how a report links its thumbnails to the full-size pictures (#1446). `HyperlinkDest::Anchor(id)` is a text anchor, the same destination a cross-reference targets, and the PDF export resolves it the same way: through the cross-reference index, to whichever page the anchor's text has landed on. A link to an anchor that no longer exists is left out of the file rather than pointing somewhere wrong. `hyperlink.create` and `hyperlink.edit` take `anchor` — an anchor's id or its name — and a link that is given no name of its own takes the anchor's. Measured: a thumbnail on page 1 linked to an anchor in text on page 3 exports `/Dest [<page 3> 0 R /XYZ 0 300 0]`; insert a page in front and the same link exports page 4, with no edit to the link. This is the groundwork for the IDML half of #1446 (import and export of page-item and text hyperlinks), which is the next change and needs a destination to put them in. * An anchor a hyperlink cannot mean is refused, not ignored This PR added anchors as link destinations and did not check that the anchor exists, so the feature was wrong on its own and review was right not to take the fix from a PR stacked above it. `dest_param` returns `Result<Option<_>>` now. `None` still means no destination was given, which `hyperlink.edit` needs for an edit that only renames; an `anchor` that is given and names none is an error. Two behaviours follow. An unknown *id* was accepted outright -- `as_u64` answered for any number -- so a link was stored to an anchor nobody has and the export quietly left it out. And on `hyperlink.edit` the `None` read as "no destination given", so the command reported success and kept the old destination, which is the case review reported. An unknown *name* did already fail on `hyperlink.create`, but with "give `url`, `email`, `page` or `anchor`", which named the wrong problem; it says "no text anchor X (anchor.list names them)" now. The test covers the id, the name, both on `edit`, that a refused edit leaves the destination, the name and the exported page alone, and that a rename naming no destination still works -- that last one is what `None` is for and it must not have become an error on the way. The page-number validation #1564 adds stays there; this is the anchor half, which belongs with the anchor feature. * A name two anchors share cannot pick one of them `anchor.create` does not require an anchor's name to be unique, and an IDML import can bring a document that already has two of the same. The lookup took the first match, so a link made on a shared name went to whichever anchor came first -- a different page, with nothing to say so (noted in review). An id still names one anchor or none, and is checked as before. A name that two anchors answer to is refused, with their ids in the message so the caller can say which one they meant. Not uniqueness at creation, which was the other way offered: a document can already hold duplicates, so enforcing it there would refuse to open files that are fine today, and the ambiguity only matters where a name is being resolved. The test builds two anchors of one name on different pages, asserts both `hyperlink.create` and `hyperlink.edit` refuse it and say what to do instead, that the refused create made no link and the refused edit left the destination alone, and that each anchor is still reachable by its id. It is a test of its own because the twin anchor outlives the deletion the other test ends with. * Test: two anchors sharing a name come from a file, now that anchor.create refuses a name in use main's anchor.create now refuses a second anchor of a name, so the test makes the first and renames the second in the document, as a file opened with two can hold them. The comment on the guard says the same.