diff --git a/crates/print/README.md b/crates/print/README.md
--- a/crates/print/README.md
+++ b/crates/print/README.md
@@ -27,6 +27,8 @@ spool::submit(&pdf, &spool::Job { printer: None, copies: 2, ..Default::default()
 - **Poster**: tile scale, overlap shared by neighbouring tiles, cut marks.
 - **Comments & forms**: document, + markups, + stamps, or form fields only; annotations print
   only with their Print flag, drawn from their appearance streams (Algorithm 8.1).
+  Existing Link appearances are document content in the first three modes; Link appearances
+  are not synthesized when absent, and form fields only excludes them.
 - **Properties… on CUPS** reads the queue's PPD and its current defaults (`lpoptions -l`) off
   the GUI thread, and gives `lpoptions` ten seconds: a CUPS that has stopped answering leaves
   the PPD's own defaults in force instead of holding the dialog (#1818; `spool::output_within`
diff --git a/crates/print/src/imposition.rs b/crates/print/src/imposition.rs
--- a/crates/print/src/imposition.rs
+++ b/crates/print/src/imposition.rs
@@ -99,7 +99,9 @@ pub(crate) fn prints(doc: &Document, d: &Dict, content: Content) -> bool {
     let subtype = subtype_obj.as_deref().and_then(Object::as_name).unwrap_or(b"");
     match subtype {
         b"Widget" => true,
-        b"Link" | b"Popup" => false,
+        // Existing Link appearances are part of the document, not markup or fields.
+        b"Link" => content != Content::FormFieldsOnly,
+        b"Popup" => false,
         b"Stamp" => content != Content::Document && content != Content::FormFieldsOnly,
         // ISO 32000-2 Table 167: an Invisible annotation of a type that isn't standard (Table
         // 171) is not printed even with its Print flag set.
diff --git a/crates/print/src/tests/appearances.rs b/crates/print/src/tests/appearances.rs
--- a/crates/print/src/tests/appearances.rs
+++ b/crates/print/src/tests/appearances.rs
@@ -85,6 +85,44 @@ fn xobject(doc: &Document, owner: &Dict, name: &[u8]) -> ObjRef {
     objects.as_dict().unwrap().get(name).unwrap().as_ref().unwrap()
 }
 
+/// Existing Link artwork prints as document content only when its flags allow printing.
+#[test]
+fn printable_link_appearance_survives_vector_print() {
+    for flags in [0, 2, 4, 6, 5] {
+        for content in [Content::Document, Content::DocumentAndMarkups, Content::DocumentAndStamps, Content::FormFieldsOnly] {
+            let (mut doc, _, annot) = widget_fixture(Some(Object::name("Form")), None);
+            doc.update_dict(annot, |d| {
+                d.set(b"Subtype".to_vec(), Object::name("Link"));
+                d.set(b"F".to_vec(), Object::Int(flags));
+            })
+            .unwrap();
+            let before = saved(&doc);
+            let expected = flags & 4 != 0 && flags & 2 == 0 && content != Content::FormFieldsOnly;
+            let printed = render(impose(&doc, &print_settings(content)).unwrap());
+            let color = if expected { [0, 255, 0, 255] } else { [255, 255, 255, 255] };
+            assert_eq!(pixel(&printed, 30, 70), color, "flags={flags}, content={content:?}");
+            let mut raster_source = doc.clone();
+            keep_printed(&mut raster_source, content);
+            let page = &pdfcraft_model::pages(&raster_source)[0];
+            let annots = raster_source.resolve(page.dict.get(b"Annots").unwrap());
+            assert_eq!(annots.as_array().unwrap().len(), usize::from(expected), "raster flags={flags}, content={content:?}");
+            assert_eq!(saved(&doc), before, "source remains unchanged");
+        }
+    }
+    for subtype in ["Link", "Popup"] {
+        let (mut doc, _, annot) = widget_fixture(Some(Object::name("Form")), None);
+        doc.update_dict(annot, |d| {
+            d.set(b"Subtype".to_vec(), Object::name(subtype));
+            if subtype == "Link" {
+                d.remove(b"AP");
+            }
+        })
+        .unwrap();
+        let printed = render(impose(&doc, &print_settings(Content::DocumentAndMarkups)).unwrap());
+        assert_eq!(pixel(&printed, 30, 70), [255, 255, 255, 255], "{subtype} must not acquire an appearance");
+    }
+}
+
 /// Printing keeps occupied page resource names and distinct annotation appearances intact,
 /// whether the page resources are direct or inherited from the page tree.
 #[test]
diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml
--- a/parity/acrobat-features.toml
+++ b/parity/acrobat-features.toml
@@ -819,7 +819,7 @@ status = "shipped"
 commands = []
 tools = []
 evidence = ["crates/render/src/raster/tests.rs::widget_appearance_states", "crates/render/src/raster/tests.rs::highlights_without_an_appearance_are_drawn", "crates/engine/src/tests.rs::comments_without_appearances_are_drawn_but_not_saved", "crates/engine/src/tests.rs::cli_display_adds_missing_appearances_without_changing_the_source", "crates/engine/src/tests.rs::fields_left_to_the_viewer_are_displayed_from_their_values", "crates/forms/src/tests.rs::need_appearances_draws_every_text_and_choice_field_in_the_display_copy", "crates/forms/src/tests.rs::the_field_appearance_cache_sees_every_input", "crates/engine/src/tests.rs::buttons_and_combs_left_to_the_viewer_are_displayed", "crates/forms/src/tests.rs::check_boxes_without_an_appearance_show_their_caption_symbol", "crates/forms/src/tests.rs::push_buttons_without_an_appearance_show_their_caption_and_frame", "crates/forms/src/tests.rs::need_appearances_draws_check_boxes_and_radio_buttons_from_their_captions", "crates/engine/src/tests.rs::need_appearances_buttons_are_displayed_from_their_captions", "crates/render/src/raster/tests.rs::invisible_unknown_annotations_and_popups_paint_nothing", "crates/print/src/tests.rs::invisible_annotations_of_unknown_types_do_not_print", "crates/engine/src/tests.rs::stamps_without_appearances_draw_the_stamp_they_name", "crates/automation/tests/it/automation.rs::page_render_draws_stamps_without_an_appearance"]
-notes = "comments without /AP (FreeText, Ink, notes, stamps, shapes, markup) are drawn from their dictionaries in the page view, page_render and page images, including the CLI render command, never saved; a stamp from another program draws the stamp its /Name names (the fourteen standard names of ISO 32000-2 Table 184, Draft when there is none) or, for any other name, a grey frame with both diagonals; text and choice fields are drawn from their values when the form sets /NeedAppearances (every one) or a widget has no /N (#3470), also display-only and cached per widget (a field edit on a 1000-field /NeedAppearances form takes ~13 ms against ~7 ms on a form that needs none) (a read-only document still gets the renderer's Highlight fallback only); check boxes and radio buttons with no appearance for their /AS are drawn display-only from /MK (background, border, and when on the /MK /CA ZapfDingbats symbol in the /DA colour, a check or a dot by default), and when the form sets /NeedAppearances so is every one that has an /MK, over the appearance the file carries (ISO 32000-2 Table 224; one without /MK keeps its own), and push buttons with none from /MK and /BS with their caption; Link borders without /AP and printing are not covered yet; an annotation of a non-standard type with the Invisible flag (ISO 32000-2 Table 167) is neither drawn nor printed, and a popup's own /AP is never painted on the page (its parent's text shows in the popup window, §12.5.6.14)"
+notes = "comments without /AP (FreeText, Ink, notes, stamps, shapes, markup) are drawn from their dictionaries in the page view, page_render and page images, including the CLI render command, never saved; a stamp from another program draws the stamp its /Name names (the fourteen standard names of ISO 32000-2 Table 184, Draft when there is none) or, for any other name, a grey frame with both diagonals; text and choice fields are drawn from their values when the form sets /NeedAppearances (every one) or a widget has no /N (#3470), also display-only and cached per widget (a field edit on a 1000-field /NeedAppearances form takes ~13 ms against ~7 ms on a form that needs none) (a read-only document still gets the renderer's Highlight fallback only); check boxes and radio buttons with no appearance for their /AS are drawn display-only from /MK (background, border, and when on the /MK /CA ZapfDingbats symbol in the /DA colour, a check or a dot by default), and when the form sets /NeedAppearances so is every one that has an /MK, over the appearance the file carries (ISO 32000-2 Table 224; one without /MK keeps its own), and push buttons with none from /MK and /BS with their caption; Link borders without /AP are not synthesized for display or printing; an annotation of a non-standard type with the Invisible flag (ISO 32000-2 Table 167) is neither drawn nor printed, and a popup's own /AP is never painted on the page (its parent's text shows in the popup window, §12.5.6.14)"
 
 [[feature]]
 id = "view.render-error-isolation"
@@ -8156,8 +8156,8 @@ milestone = "M10"
 status = "shipped"
 commands = ["print.dialog"]
 tools = ["doc_print"]
-evidence = ["crates/print/src/tests.rs::imposed_pdf_has_the_sheets_and_honours_comments_and_forms", "crates/print/src/tests/appearances.rs::annotation_appearances_do_not_replace_page_xobjects"]
-notes = "Print flag honoured; appearance resource names preserve existing page XObjects and distinct annotations, including inherited resources"
+evidence = ["crates/print/src/tests.rs::imposed_pdf_has_the_sheets_and_honours_comments_and_forms", "crates/print/src/tests/appearances.rs::annotation_appearances_do_not_replace_page_xobjects", "crates/print/src/tests/appearances.rs::printable_link_appearance_survives_vector_print"]
+notes = "Print and Hidden flags honoured; existing Link appearances print as document content, but missing Link appearances are not synthesized. Appearance resource names preserve existing page XObjects and distinct annotations, including inherited resources"
 
 [[feature]]
 id = "print.print-with-comment-summary"
