mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
M6: leave the combo box drop-down arrow to #3567
The on-screen drop-down button for combo boxes drawn display-only, the narrowed text area beside it, and the Print as Image copy without it (with_printed_field_appearances, printed_display_bytes) are removed. Open PR #3567 draws the arrow for every combo box in the page view. Check boxes, radio buttons, push buttons and comb lines are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0175FWj4xFhMhSH3UpMU8DSC
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5233e12281
commit
dd1a894b2d
@@ -934,16 +934,6 @@ fn display_bytes(editor: Option<&mut Editor>, bytes: &Arc<Vec<u8>>) -> Arc<Vec<u
|
||||
}
|
||||
}
|
||||
|
||||
/// The display copy as it prints ([`print_raster`]): [`display_bytes`] without what is only for the
|
||||
/// screen, a drawn combo box's drop-down button. `None` when it would be the display copy itself
|
||||
/// (no field is drawn for display, or the document is not open for editing) or cannot be written.
|
||||
pub(crate) fn printed_display_bytes(doc: &Document) -> Option<Arc<Vec<u8>>> {
|
||||
let e = doc.editor.as_ref()?;
|
||||
let comments = pdfcraft_annot::with_missing_appearances(&e.cos);
|
||||
let fields = pdfcraft_forms::with_printed_field_appearances(comments.as_ref().unwrap_or(&e.cos))?;
|
||||
write_incremental(&fields, &SaveOptions::default()).ok().map(Arc::new)
|
||||
}
|
||||
|
||||
/// `comments` (the display copy with comment appearances, if it needed any; else the document
|
||||
/// itself) with its form fields drawn from their values where the file leaves that to the viewer.
|
||||
fn with_field_appearances(comments: Option<pdfcraft_cos::Document>, doc: &pdfcraft_cos::Document) -> Option<pdfcraft_cos::Document> {
|
||||
|
||||
@@ -23,9 +23,7 @@ pub fn rasterize(doc: &Document, pages: &[usize], raster: &Raster, content: Cont
|
||||
// The copy below holds only what prints, so the viewer's own comment hiding plays no part.
|
||||
config.hide_comments = false;
|
||||
let printed = guard(|| -> Result<Vec<u8>, String> {
|
||||
// The display copy, less what it shows only on screen.
|
||||
let shown = crate::printed_display_bytes(doc).unwrap_or_else(|| src.bytes.clone());
|
||||
let mut cos = pdfcraft_cos::Document::open_with_password(shown, config.password.as_deref()).map_err(|e| e.to_string())?;
|
||||
let mut cos = pdfcraft_cos::Document::open_with_password(src.bytes.clone(), config.password.as_deref()).map_err(|e| e.to_string())?;
|
||||
pdfcraft_print::keep_printed(&mut cos, content);
|
||||
write_incremental(&cos, &SaveOptions::default()).map_err(|e| e.to_string())
|
||||
})??;
|
||||
|
||||
@@ -7440,22 +7440,20 @@ fn a_page_truncated_for_forms_and_pictures_is_listed_once() {
|
||||
}
|
||||
|
||||
/// ISO 32000-2 Table 192, 12.7.5.2.3 and Table 228 (Comb): a check box, a push button, a comb
|
||||
/// field and a combo box the file leaves to the viewer. The page as displayed shows the check
|
||||
/// box's caption symbol in its text colour, the button's grey face, the comb's cells in the border
|
||||
/// colour and the combo box's drop-down button; printing leaves that button out, and saving
|
||||
/// writes the file as it was.
|
||||
/// field the file leaves to the viewer. The page as displayed and printed as an image shows the
|
||||
/// check box's caption symbol in its text colour, the button's grey face and the comb's cells in
|
||||
/// the border colour, and saving writes the file as it was.
|
||||
#[test]
|
||||
fn buttons_and_combs_left_to_the_viewer_are_displayed() {
|
||||
let pdf = b"%PDF-1.7\n\
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R /AcroForm 4 0 R >> endobj\n\
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj\n\
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 400 200] /Annots [6 0 R 7 0 R 8 0 R 9 0 R] >> endobj\n\
|
||||
4 0 obj << /Fields [6 0 R 7 0 R 8 0 R 9 0 R] /DA (/Helv 0 Tf 0 g) /DR << /Font << /Helv 5 0 R >> >> >> endobj\n\
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 400 200] /Annots [6 0 R 7 0 R 8 0 R] >> endobj\n\
|
||||
4 0 obj << /Fields [6 0 R 7 0 R 8 0 R] /DA (/Helv 0 Tf 0 g) /DR << /Font << /Helv 5 0 R >> >> >> endobj\n\
|
||||
5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj\n\
|
||||
6 0 obj << /Type /Annot /Subtype /Widget /FT /Btn /T (k) /V /Yes /AS /Yes /DA (/ZaDb 0 Tf 1 0 0 rg) /MK << /CA (8) /BC [0 0 0] >> /Rect [20 150 60 190] /F 4 /P 3 0 R >> endobj\n\
|
||||
7 0 obj << /Type /Annot /Subtype /Widget /FT /Btn /Ff 65536 /T (o) /DA (/Helv 12 Tf 0 g) /MK << /CA (Press) /BG [0.8 0.8 0.8] /BC [0 0 0] >> /BS << /W 2 /S /B >> /Rect [100 150 220 190] /F 4 /P 3 0 R >> endobj\n\
|
||||
8 0 obj << /Type /Annot /Subtype /Widget /FT /Tx /Ff 16777216 /MaxLen 4 /T (d) /V (AB) /DA (/Helv 12 Tf 0 g) /MK << /BC [0 0 1] >> /Rect [20 80 220 110] /F 4 /P 3 0 R >> endobj\n\
|
||||
9 0 obj << /Type /Annot /Subtype /Widget /FT /Ch /Ff 131072 /T (m) /Opt [(Apple) (Banana)] /V (Apple) /DA (/Helv 12 Tf 0 g) /MK << /BC [0 0 0] >> /Rect [20 20 220 50] /F 4 /P 3 0 R >> endobj\n\
|
||||
trailer << /Root 1 0 R >>\n%%EOF"
|
||||
.to_vec();
|
||||
let render = |bytes: Arc<Vec<u8>>| {
|
||||
@@ -7484,15 +7482,12 @@ fn buttons_and_combs_left_to_the_viewer_are_displayed() {
|
||||
assert!(count(&shown, (x - 1, 84, x + 1, 106), &blue) >= 20, "a cell line at {x}");
|
||||
}
|
||||
assert_eq!(count(&shown, (95, 84, 97, 106), &blue), 0, "no line inside a cell");
|
||||
// The combo box's drop-down button: a 28 pt square inside the right edge.
|
||||
let button = (193, 22, 218, 48);
|
||||
assert!(count(&shown, button, &grey) > 300, "the drop-down button on screen");
|
||||
// Print as Image: the page as a picture, at 72 dpi so it is the size of the page here.
|
||||
let raster = pdfcraft_print::Raster { dpi: 72.0, grayscale: false };
|
||||
let pictures = crate::print_raster::rasterize(s.get(id).unwrap(), &[0], &raster, pdfcraft_print::Content::Document).unwrap();
|
||||
let printed = render(Arc::new(pictures));
|
||||
assert_eq!(count(&printed, button, &grey), 0, "not on paper");
|
||||
assert!(count(&printed, (24, 154, 56, 186), &red) > 60, "the check box prints");
|
||||
assert!(count(&printed, (104, 154, 216, 186), &grey) > 1000, "and the push button");
|
||||
// The file is saved as it was: no widget has an appearance.
|
||||
let saved = pdfcraft_cos::Document::open(s.save_bytes(id).unwrap()).unwrap();
|
||||
for f in pdfcraft_forms::fields(&saved) {
|
||||
|
||||
@@ -58,9 +58,7 @@ let cells: Vec<table::Cell> = grid.cells_for(names)?; // "Table1_1", "Table
|
||||
drawing while the objects it was drawn from are the same (`FieldAppearanceCache`), so typing in
|
||||
one field redraws that field, not the form. Check boxes and radio buttons with no appearance for
|
||||
their state are drawn from `/MK` (border, background, and when on the `/MK /CA` ZapfDingbats
|
||||
symbol in the `/DA` colour), and push buttons with none from `/MK`, `/BS` and their caption. On
|
||||
screen a drawn combo box also shows a drop-down button; `with_printed_field_appearances` leaves it
|
||||
out for printing.
|
||||
symbol in the `/DA` colour), and push buttons with none from `/MK`, `/BS` and their caption.
|
||||
- Check boxes and radio buttons switch `/V` and each widget's `/AS`. Widgets with no appearances
|
||||
get PdfCraft's own (a drawn check mark or dot; no symbol font needed).
|
||||
- Imported `/MaxLen 0` is treated as unlimited (also clearing an inherited limit), while the
|
||||
|
||||
@@ -390,25 +390,6 @@ fn comb_dividers(doc: &Document, wd: &Dict, w: f64, h: f64, cells: usize) -> Str
|
||||
c
|
||||
}
|
||||
|
||||
/// A combo box's drop-down button for the screen: a `size`-wide light grey face at the right
|
||||
/// edge inside the `bw` border of a `w` × `h` widget, parted from the text by a line in the
|
||||
/// border's colour, with a downward triangle.
|
||||
fn drop_down_button(doc: &Document, wd: &Dict, w: f64, h: f64, bw: f64, size: f64) -> String {
|
||||
let mk = wd.get(b"MK").map(|m| doc.resolve(m)).and_then(|m| m.as_dict().cloned());
|
||||
let (x0, y0, x1, y1) = (w - bw - size, bw, w - bw, h - bw);
|
||||
let mut c = format!("0.85 g\n{} {} {} {} re f\n", n(x0), n(y0), n(size), n(y1 - y0));
|
||||
let lw = line_width(doc, wd);
|
||||
if let Some(bc) = color_array(doc, mk.as_ref(), b"BC")
|
||||
&& lw > 0.0
|
||||
&& lw.is_finite()
|
||||
{
|
||||
c.push_str(&format!("{}\n{} w\n{} {} m {} {} l S\n", stroke_op(&bc), n(lw), n(x0), n(y0), n(x0), n(y1)));
|
||||
}
|
||||
let (cx, cy, d) = ((x0 + x1) / 2.0, (y0 + y1) / 2.0, size * 0.22);
|
||||
c.push_str(&format!("0 g\n{} {} m {} {} l {} {} l h f\n", n(cx - d), n(cy + d / 2.0), n(cx + d), n(cy + d / 2.0), n(cx), n(cy - d / 2.0)));
|
||||
c
|
||||
}
|
||||
|
||||
/// Where a list box draws its options: one row per option from the top, starting with option
|
||||
/// `top` (`/TI`, §12.7.4.4). The appearance PdfCraft writes and the viewer's in-place list share
|
||||
/// it, so a click picks the option drawn under the pointer (#1524).
|
||||
@@ -499,19 +480,6 @@ pub fn field_appearance_as(doc: &Document, f: &Field, w: &Widget, values: &[Stri
|
||||
/// hold it: the form's font is a simple (WinAnsi) font, Helvetica stands in for one PdfCraft can't
|
||||
/// write with (an embedded subset, `Identity-H`), or a UCS-2 CMap meets a character beyond U+FFFF.
|
||||
pub fn field_appearance_drawn(doc: &Document, f: &Field, w: &Widget, values: &[String], format: bool) -> (Stream, Option<char>) {
|
||||
field_look(doc, f, w, values, format, false)
|
||||
}
|
||||
|
||||
/// [`field_appearance_drawn`] as the page view shows a field the file leaves to the viewer: a
|
||||
/// combo box also gets its drop-down button at the right edge, which the text keeps clear of.
|
||||
/// The button is something to click on screen, not part of the field's printed look, so it is
|
||||
/// drawn only here and never into an appearance PdfCraft saves or prints.
|
||||
pub(crate) fn field_appearance_on_screen(doc: &Document, f: &Field, w: &Widget, values: &[String], format: bool) -> (Stream, Option<char>) {
|
||||
field_look(doc, f, w, values, format, true)
|
||||
}
|
||||
|
||||
/// The appearance of a text or choice field, with a combo box's drop-down button when `screen`.
|
||||
fn field_look(doc: &Document, f: &Field, w: &Widget, values: &[String], format: bool, screen: bool) -> (Stream, Option<char>) {
|
||||
// The Format event: what is shown, not what is stored.
|
||||
let formatted: Vec<String>;
|
||||
let values = if format
|
||||
@@ -552,15 +520,7 @@ fn field_look(doc: &Document, f: &Field, w: &Widget, values: &[String], format:
|
||||
let width_of = |text: &str, size: f64| enc.width(text, size);
|
||||
let (mut c, bw) = frame(doc, &wd, width, height);
|
||||
let pad = 2.0 + bw;
|
||||
// A combo box's drop-down button on screen: a square as tall as the room inside the border,
|
||||
// never wider than that room.
|
||||
let button = if screen && f.kind == FieldKind::Combo { (height - 2.0 * bw).min(width - 2.0 * bw).max(0.0) } else { 0.0 };
|
||||
if button > 0.0 {
|
||||
c.push_str(&drop_down_button(doc, &wd, width, height, bw, button));
|
||||
}
|
||||
// The text and its clip keep to the room left of the button.
|
||||
let text_w = width - button;
|
||||
let inner_w = (text_w - 2.0 * pad).max(1.0);
|
||||
let inner_w = (width - 2.0 * pad).max(1.0);
|
||||
let q = wd.get(b"Q").and_then(|o| doc.resolve(o).as_int()).unwrap_or(f.quadding);
|
||||
let mut body: Vec<u8> = Vec::new();
|
||||
// The first character drawn that the font can't hold (drawn as `?`), for the caller to report.
|
||||
@@ -579,7 +539,7 @@ fn field_look(doc: &Document, f: &Field, w: &Widget, values: &[String], format:
|
||||
let tw = width_of(text, size);
|
||||
match q {
|
||||
1 => pad + (inner_w - tw) / 2.0,
|
||||
2 => text_w - pad - tw,
|
||||
2 => width - pad - tw,
|
||||
_ => pad,
|
||||
}
|
||||
};
|
||||
@@ -649,7 +609,7 @@ fn field_look(doc: &Document, f: &Field, w: &Widget, values: &[String], format:
|
||||
"/Tx BMC\nq\n{c}{} {} {} {} re W n\nBT\n/{} {} Tf\n{}\n",
|
||||
n(bw),
|
||||
n(bw),
|
||||
n(text_w - 2.0 * bw),
|
||||
n(width - 2.0 * bw),
|
||||
n(height - 2.0 * bw),
|
||||
font_name,
|
||||
n(size),
|
||||
|
||||
+2
-16
@@ -1304,9 +1304,8 @@ pub fn need_appearances(doc: &Document) -> bool {
|
||||
/// writer left the appearances to the viewer), and otherwise each widget without a normal
|
||||
/// appearance (`/AP /N`), as typing the value would draw it (#3470). Check boxes and radio buttons
|
||||
/// with no normal appearance for their state (`/AS`) are drawn from their `/MK` background, border
|
||||
/// and caption, and push buttons with none from their `/MK` and `/BS` (Table 192); the page view
|
||||
/// also shows a drawn combo box's drop-down button. `doc` is not changed: saving writes the file
|
||||
/// as it was.
|
||||
/// and caption, and push buttons with none from their `/MK` and `/BS` (Table 192). `doc` is not
|
||||
/// changed: saving writes the file as it was.
|
||||
pub fn with_field_appearances(doc: &Document) -> Option<Document> {
|
||||
with_field_appearances_cached(doc, &mut FieldAppearanceCache::default())
|
||||
}
|
||||
@@ -1314,18 +1313,6 @@ pub fn with_field_appearances(doc: &Document) -> Option<Document> {
|
||||
/// [`with_field_appearances`], reusing the appearances `cache` drew for widgets whose inputs are
|
||||
/// the same objects as then (see [`FieldAppearanceCache`]).
|
||||
pub fn with_field_appearances_cached(doc: &Document, cache: &mut FieldAppearanceCache) -> Option<Document> {
|
||||
fields_shown(doc, cache, true)
|
||||
}
|
||||
|
||||
/// [`with_field_appearances`] for printing: the same, without the drop-down buttons of combo
|
||||
/// boxes, which are for clicking on screen and not part of a field's printed look.
|
||||
pub fn with_printed_field_appearances(doc: &Document) -> Option<Document> {
|
||||
fields_shown(doc, &mut FieldAppearanceCache::default(), false)
|
||||
}
|
||||
|
||||
/// [`with_field_appearances_cached`]; `screen` adds the on-screen parts (a combo box's button).
|
||||
/// One cache serves one of the two: the engine keeps one for the screen and prints uncached.
|
||||
fn fields_shown(doc: &Document, cache: &mut FieldAppearanceCache, screen: bool) -> Option<Document> {
|
||||
let all = need_appearances(doc);
|
||||
let mut copy: Option<Document> = None;
|
||||
for f in fields(doc) {
|
||||
@@ -1365,7 +1352,6 @@ fn fields_shown(doc: &Document, cache: &mut FieldAppearanceCache, screen: bool)
|
||||
let stream = match f.kind {
|
||||
FieldKind::CheckBox | FieldKind::Radio => appearance::button_state_shown(doc, &f, w, state.as_deref() != Some("Off")),
|
||||
FieldKind::PushButton => appearance::push_button_shown(doc, &f, w),
|
||||
_ if screen => appearance::field_appearance_on_screen(doc, &f, w, &f.value, true).0,
|
||||
_ => appearance::field_appearance_drawn(doc, &f, w, &f.value, true).0,
|
||||
};
|
||||
cache.insert(w.obj, inputs, &stream);
|
||||
|
||||
@@ -3098,8 +3098,8 @@ fn a_skipped_field_leaves_its_widget_to_the_next() {
|
||||
assert_eq!(widgets, [("b", 1)]);
|
||||
}
|
||||
|
||||
/// Check boxes, radio buttons and push buttons whose file gives them no appearance, a comb field
|
||||
/// and a combo box, for the display copy.
|
||||
/// Check boxes, radio buttons and push buttons whose file gives them no appearance, a comb field,
|
||||
/// a combo box and a list box, for the display copy.
|
||||
fn left_buttons() -> Document {
|
||||
let widget = |n: &str, rest: &str| format!("<< /Type /Annot /Subtype /Widget /T ({n}) /F 4 /P 3 0 R {rest} >>");
|
||||
document(&[
|
||||
@@ -3226,30 +3226,3 @@ fn comb_fields_show_their_cells() {
|
||||
let plain = saved_look(&doc, "d");
|
||||
assert!(!plain.contains(" m ") && plain.contains("(A) Tj"), "{plain}");
|
||||
}
|
||||
|
||||
/// A combo box drawn for display shows its drop-down button at the right edge and keeps its text
|
||||
/// clear of it; a list box, a text field, the combo box's saved look and its printed one have none.
|
||||
#[test]
|
||||
fn combo_boxes_shown_on_screen_have_a_drop_down_button() {
|
||||
let doc = left_buttons();
|
||||
let screen = with_field_appearances(&doc).expect("fields to draw");
|
||||
let printed = with_printed_field_appearances(&doc).expect("fields to draw");
|
||||
let m = ap(&screen, &field(&fields(&screen), "m").widgets[0]);
|
||||
// A 22 pt square inside the 1 pt border, a line in the border colour, a triangle.
|
||||
assert!(m.contains("0.85 g\n97 1 22 22 re f\n0 0 0 RG\n1 w\n97 1 m 97 23 l S\n0 g\n"), "{m}");
|
||||
// The text's clip stops at the button.
|
||||
assert!(m.contains("1 1 96 22 re W n"), "{m}");
|
||||
let printed_m = ap(&printed, &field(&fields(&printed), "m").widgets[0]);
|
||||
assert!(!printed_m.contains("0.85 g") && printed_m.contains("1 1 118 22 re W n"), "{printed_m}");
|
||||
let saved = saved_look(&doc, "m");
|
||||
assert!(!saved.contains("0.85 g"), "{saved}");
|
||||
// Right-aligned text moves left by the button's width.
|
||||
let x = |s: &str| -> f64 {
|
||||
s.split(" Tm ").next().and_then(|s| s.rsplit("1 0 0 1 ").next()).and_then(|s| s.split(' ').next()).and_then(|s| s.parse().ok()).unwrap()
|
||||
};
|
||||
assert!((x(&saved) - x(&m) - 22.0).abs() < 0.01, "{saved} / {m}");
|
||||
for other in ["n", "d"] {
|
||||
let s = ap(&screen, &field(&fields(&screen), other).widgets[0]);
|
||||
assert!(!s.contains("0.85 g"), "{other}: {s}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,8 +806,8 @@ milestone = "M2"
|
||||
status = "shipped"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = ["crates/render/src/raster.rs::widget_appearance_states", "crates/render/src/raster.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::combo_boxes_shown_on_screen_have_a_drop_down_button"]
|
||||
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; 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), push buttons with none from /MK and /BS with their caption, and a drawn combo box shows a drop-down button on screen only (not in Print as Image); Link borders without /AP and printing are not covered yet"
|
||||
evidence = ["crates/render/src/raster.rs::widget_appearance_states", "crates/render/src/raster.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"]
|
||||
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; 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 push buttons with none from /MK and /BS with their caption; Link borders without /AP and printing are not covered yet"
|
||||
|
||||
[[feature]]
|
||||
id = "view.render-error-isolation"
|
||||
|
||||
Reference in New Issue
Block a user