mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 02:37:01 -06:00
Type: Highlight Substituted Fonts moves to Preferences › Type and applies to every document
Highlight Substituted Fonts is now a preference in Preferences › Type, next to Highlight Alternate Glyphs, on by default and saved with the other preferences (`prefs.set highlightSubstitutedFonts`). The canvas reads it through `RenderOptions::highlight_substituted_fonts` for every open document; a document's own setup no longer decides it. File › Document Setup no longer shows the checkbox and keeps Highlight Substituted Glyphs. A file saved with Document Setup's Highlight Substituted Fonts off now shows the pink highlight when it opens, while the preference is on. The file still opens with the stored value, and saving it again keeps that value. `document.setup` no longer reports `highlightSubstitutedFonts`. It ignores the key and adds a `warnings` entry that names `prefs.set` to its reply, so an older reply passed back still applies its other keys. The raster cache of type under a raster effect now also redraws when Highlight Alternate Glyphs changes.
This commit is contained in:
@@ -212,7 +212,8 @@ pub struct DocSetup {
|
||||
pub bleed: [f64; 4],
|
||||
/// Show Images in Outline Mode.
|
||||
pub outline_images: bool,
|
||||
/// Highlight text whose font is missing (drawn in a substitute font).
|
||||
/// The Highlight Substituted Fonts value older builds kept in Document Setup. Files keep it as
|
||||
/// they were saved; the screen follows Preferences › Type › Highlight Substituted Fonts.
|
||||
pub highlight_substituted_fonts: bool,
|
||||
/// Highlight glyphs the font lacks (drawn from a fallback font).
|
||||
pub highlight_substituted_glyphs: bool,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Document Setup",
|
||||
[],
|
||||
None,
|
||||
"{units?: \"Points\"|\"Inches\"|\"Millimeters\"|…, bleed?: pt (all sides)|[top, bottom, left, right]|{top?, bottom?, left?, right?} (0–72 pt), outlineImages?: bool (show images in Outline mode), highlightSubstitutedFonts?: bool, highlightSubstitutedGlyphs?: bool, gridSize?: \"small\"|\"medium\"|\"large\", gridColors?: \"Light\"|\"Medium\"|\"Dark\"|\"Red\"|\"Orange\"|\"Green\"|\"Blue\"|\"Purple\"|[colour, colour] (transparency grid; the first is also the paper colour), simulatePaper?: bool, flattenerPreset?: \"High Resolution\"|\"Medium Resolution\"|\"Low Resolution\"|a saved preset (flattener.presets.list), discardWhiteOverprint?: bool, language?: \"English: USA\"|\"German\"|\"French\"|… (also picks its quotes), quotes?: {double?: \"“”\", single?: \"‘’\"} (beside a language: only if they differ from the current ones), typographersQuotes?: bool (typed straight quotes become the document's quotes), superscript?: {size?: %, position?: %}, subscript?: {size?: %, position?: %}, smallCapsSize?: %, exportText?: \"editable\"|\"appearance\" (SVG text as text or as outlines), backgroundContents?: \"transparent\"|\"white\" (white: raster exports are white behind the art), …the keys of its own reply (it can be passed back; the values it only reports are ignored)} change the document setup in one undo step; no params → the current setup",
|
||||
"{units?: \"Points\"|\"Inches\"|\"Millimeters\"|…, bleed?: pt (all sides)|[top, bottom, left, right]|{top?, bottom?, left?, right?} (0–72 pt), outlineImages?: bool (show images in Outline mode), highlightSubstitutedGlyphs?: bool (Highlight Substituted Fonts is a preference: prefs.set highlightSubstitutedFonts; a highlightSubstitutedFonts key is ignored and the reply carries a warning),gridSize?: \"small\"|\"medium\"|\"large\", gridColors?: \"Light\"|\"Medium\"|\"Dark\"|\"Red\"|\"Orange\"|\"Green\"|\"Blue\"|\"Purple\"|[colour, colour] (transparency grid; the first is also the paper colour), simulatePaper?: bool, flattenerPreset?: \"High Resolution\"|\"Medium Resolution\"|\"Low Resolution\"|a saved preset (flattener.presets.list), discardWhiteOverprint?: bool, language?: \"English: USA\"|\"German\"|\"French\"|… (also picks its quotes), quotes?: {double?: \"“”\", single?: \"‘’\"} (beside a language: only if they differ from the current ones), typographersQuotes?: bool (typed straight quotes become the document's quotes), superscript?: {size?: %, position?: %}, subscript?: {size?: %, position?: %}, smallCapsSize?: %, exportText?: \"editable\"|\"appearance\" (SVG text as text or as outlines), backgroundContents?: \"transparent\"|\"white\" (white: raster exports are white behind the art), …the keys of its own reply (it can be passed back; the values it only reports are ignored)} change the document setup in one undo step; no params → the current setup",
|
||||
has_doc,
|
||||
setup
|
||||
)]
|
||||
@@ -31,7 +31,6 @@ pub(crate) fn setup_json(d: &Document) -> Value {
|
||||
"units": d.units.label(),
|
||||
"bleed": s.bleed,
|
||||
"outlineImages": s.outline_images,
|
||||
"highlightSubstitutedFonts": s.highlight_substituted_fonts,
|
||||
"highlightSubstitutedGlyphs": s.highlight_substituted_glyphs,
|
||||
"gridSize": s.grid_size.id(),
|
||||
"gridColors": s.grid_colors.map(|c| c.to_hex()),
|
||||
@@ -72,9 +71,19 @@ fn setup(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
apply(&mut new, &mut units, k, v, &s.prefs.flattener_presets)?;
|
||||
}
|
||||
// An older build's reply passed back still applies; the moved key only gets a warning.
|
||||
let reply = |d: &Document| {
|
||||
let mut r = setup_json(d);
|
||||
if o.contains_key("highlightSubstitutedFonts")
|
||||
&& let Some(m) = r.as_object_mut()
|
||||
{
|
||||
m.insert("warnings".into(), json!([MOVED_FONTS_HIGHLIGHT]));
|
||||
}
|
||||
r
|
||||
};
|
||||
let st = s.doc()?;
|
||||
if new == st.doc.setup && units == st.doc.units {
|
||||
return Ok(setup_json(&st.doc));
|
||||
return Ok(reply(&st.doc));
|
||||
}
|
||||
s.edit("Document Setup", |d, _| {
|
||||
let scripts_changed =
|
||||
@@ -86,9 +95,13 @@ fn setup(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(setup_json(&s.doc()?.doc))
|
||||
Ok(reply(&s.doc()?.doc))
|
||||
}
|
||||
|
||||
/// The warning for `highlightSubstitutedFonts`, which `document.setup` ignores.
|
||||
const MOVED_FONTS_HIGHLIGHT: &str =
|
||||
"highlightSubstitutedFonts is ignored: it is a preference (Preferences › Type), set with prefs.set {key: \"highlightSubstitutedFonts\", value}";
|
||||
|
||||
fn flag(k: &str, v: &Value) -> Result<bool> {
|
||||
v.as_bool().ok_or_else(|| bad(C, format!("{k} must be true or false")))
|
||||
}
|
||||
@@ -102,7 +115,10 @@ fn apply(s: &mut DocSetup, units: &mut vectorcraft_doc::Unit, k: &str, v: &Value
|
||||
"units" => *units = v.as_str().and_then(vectorcraft_doc::Unit::named).ok_or_else(|| bad(C, "unknown units"))?,
|
||||
"bleed" => s.bleed = bleed_param(v, s.bleed, C)?,
|
||||
"outlineImages" => s.outline_images = flag(k, v)?,
|
||||
"highlightSubstitutedFonts" => s.highlight_substituted_fonts = flag(k, v)?,
|
||||
// Preferences › Type holds it for every document: ignored, with a warning in the reply.
|
||||
"highlightSubstitutedFonts" => {
|
||||
flag(k, v)?;
|
||||
}
|
||||
"highlightSubstitutedGlyphs" => s.highlight_substituted_glyphs = flag(k, v)?,
|
||||
"gridSize" => s.grid_size = v.as_str().and_then(GridSize::parse).ok_or_else(|| bad(C, "gridSize must be small, medium or large"))?,
|
||||
"gridColors" => s.grid_colors = grid_colors(v)?,
|
||||
@@ -125,7 +141,7 @@ fn apply(s: &mut DocSetup, units: &mut vectorcraft_doc::Unit, k: &str, v: &Value
|
||||
"exportText" => s.export_text = v.as_str().and_then(ExportText::parse).ok_or_else(|| bad(C, "exportText must be editable or appearance"))?,
|
||||
"backgroundContents" => s.background = background_param(v, C)?,
|
||||
// Read-only values `document.setup` reports.
|
||||
"gridColorsName" => {}
|
||||
"gridColorsName" | "warnings" => {}
|
||||
_ => return Err(bad(C, format!("unknown setting `{k}`"))),
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -193,6 +193,7 @@ pub const PREF_SPECS: &[PrefSpec] = &[
|
||||
p!("recentFontsCount", "Type", "Options", "Number of Recent Fonts", int(1, 15)),
|
||||
p!("missingGlyphProtection", "Type", "Options", "Enable Missing Glyph Protection", bool),
|
||||
p!("highlightAlternateGlyphs", "Type", "Options", "Highlight Alternate Glyphs", bool),
|
||||
p!("highlightSubstitutedFonts", "Type", "Options", "Highlight Substituted Fonts", bool),
|
||||
p!("placeholderText", "Type", "Options", "Fill New Type Objects With Placeholder Text", bool),
|
||||
p!("fontsFolder", "Type", "Options", "Additional Fonts Folder", text),
|
||||
// Units
|
||||
|
||||
@@ -986,6 +986,9 @@ pub struct Prefs {
|
||||
pub recent_fonts_count: u32,
|
||||
pub missing_glyph_protection: bool,
|
||||
pub highlight_alternate_glyphs: bool,
|
||||
/// Type › Highlight Substituted Fonts: on screen, text drawn in a substitute for a missing
|
||||
/// font is highlighted, in every document (on by default).
|
||||
pub highlight_substituted_fonts: bool,
|
||||
pub placeholder_text: bool,
|
||||
// Units
|
||||
pub units_general: String,
|
||||
@@ -1243,6 +1246,7 @@ impl Default for Prefs {
|
||||
recent_fonts_count: 10,
|
||||
missing_glyph_protection: true,
|
||||
highlight_alternate_glyphs: false,
|
||||
highlight_substituted_fonts: true,
|
||||
placeholder_text: true,
|
||||
units_general: s("points"),
|
||||
units_stroke: s("points"),
|
||||
|
||||
@@ -288,6 +288,48 @@ fn setup_survives_save_and_load() {
|
||||
assert_eq!(back.setup.grid_colors_name(), "Custom");
|
||||
}
|
||||
|
||||
/// Highlight Substituted Fonts is a preference (Preferences › Type): `document.setup` does not
|
||||
/// report it, and ignores it with a warning that names `prefs.set`, so an older build's reply
|
||||
/// passed back still applies its other keys. The document's stored value and the preference stay.
|
||||
#[test]
|
||||
fn highlight_substituted_fonts_is_ignored_with_a_pointer_to_the_preference() {
|
||||
let mut s = session();
|
||||
let q = s.execute("document.setup", &json!({})).unwrap();
|
||||
assert!(q.get("highlightSubstitutedFonts").is_none() && q.get("warnings").is_none(), "{q}");
|
||||
let r = s.execute("document.setup", &json!({"highlightSubstitutedFonts": false, "bleed": 9})).unwrap();
|
||||
let w = r["warnings"][0].as_str().unwrap_or_default();
|
||||
assert!(w.contains("prefs.set") && w.contains("highlightSubstitutedFonts"), "{r}");
|
||||
assert_eq!(setup(&s).bleed, [9.0; 4]);
|
||||
assert!(setup(&s).highlight_substituted_fonts, "the stored value is not changed");
|
||||
assert!(s.prefs.highlight_substituted_fonts, "nor is the preference");
|
||||
assert_eq!(s.doc().unwrap().history.undo.len(), 1);
|
||||
// The reply, warning included, can be passed back; alone, the key changes nothing.
|
||||
let mut plain = r.clone();
|
||||
plain.as_object_mut().unwrap().remove("warnings");
|
||||
assert_eq!(s.execute("document.setup", &r).unwrap(), plain);
|
||||
assert_eq!(s.execute("document.setup", &json!({"highlightSubstitutedFonts": true})).unwrap()["warnings"], r["warnings"]);
|
||||
assert_eq!(s.doc().unwrap().history.undo.len(), 1);
|
||||
let e = s.execute("document.setup", &json!({"highlightSubstitutedFonts": 1})).unwrap_err().to_string();
|
||||
assert!(e.contains("true or false"), "{e}");
|
||||
}
|
||||
|
||||
/// A file saved with Document Setup's old Highlight Substituted Fonts value opens, keeps that
|
||||
/// value when saved again, and leaves the preference as it was.
|
||||
#[test]
|
||||
fn a_file_with_the_old_highlight_substituted_fonts_value_opens_and_keeps_it() {
|
||||
let mut s = session();
|
||||
let mut d = (*s.doc().unwrap().doc).clone();
|
||||
d.setup.highlight_substituted_fonts = false;
|
||||
let data = vectorcraft_format::base64_encode(&vectorcraft_format::save_file(&d));
|
||||
s.execute("document.open", &json!({"name": "old.vectorcraft", "dataBase64": data})).unwrap();
|
||||
assert!(!setup(&s).highlight_substituted_fonts);
|
||||
assert!(s.prefs.highlight_substituted_fonts, "the preference decides the screen");
|
||||
let again = vectorcraft_format::load(&vectorcraft_format::save_file(&s.doc().unwrap().doc)).unwrap();
|
||||
assert!(!again.setup.highlight_substituted_fonts);
|
||||
let q = s.execute("document.setup", &json!({})).unwrap();
|
||||
assert_eq!(s.execute("document.setup", &q).unwrap(), q);
|
||||
}
|
||||
|
||||
/// A file whose bleed, superscript, subscript or small caps size lies outside the range
|
||||
/// `document.setup` takes opens with it at the nearer end of that range, and the setup it reports
|
||||
/// can be fed back.
|
||||
|
||||
@@ -1454,6 +1454,25 @@ fn highlight_alternate_glyphs_preference_is_off_by_default() {
|
||||
assert!(!s.prefs.highlight_alternate_glyphs);
|
||||
}
|
||||
|
||||
/// Preferences › Type › Highlight Substituted Fonts: on by default, listed in the Type category,
|
||||
/// and saved with the other preferences.
|
||||
/// <https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/edit-text-without-replacing-missing-fonts.html>
|
||||
#[test]
|
||||
fn highlight_substituted_fonts_preference_is_on_by_default_and_saved() {
|
||||
let mut s = Session::new();
|
||||
assert!(s.prefs.highlight_substituted_fonts, "on by default");
|
||||
let spec = crate::cmd::prefscmds::PREF_SPECS.iter().find(|p| p.key == "highlightSubstitutedFonts").expect("listed");
|
||||
assert_eq!((spec.category, spec.label), ("Type", "Highlight Substituted Fonts"));
|
||||
assert_eq!(s.execute("prefs.get", &json!({"key": "highlightSubstitutedFonts"})).unwrap(), json!(true));
|
||||
set_pref(&mut s, "highlightSubstitutedFonts", json!(false));
|
||||
assert!(!s.prefs.highlight_substituted_fonts);
|
||||
let saved: Prefs = serde_json::from_value(serde_json::to_value(&s.prefs).unwrap()).unwrap();
|
||||
assert!(!saved.highlight_substituted_fonts);
|
||||
// Preferences saved before the key existed read it as on.
|
||||
let old: Prefs = serde_json::from_value(json!({"highlightAlternateGlyphs": true})).unwrap();
|
||||
assert!(old.highlight_substituted_fonts);
|
||||
}
|
||||
|
||||
/// Preferences › Performance › Real-time Drawing and Editing (#394): off, the document isn't
|
||||
/// previewed while dragging but the pending shape still shows as a canvas overlay until commit.
|
||||
#[test]
|
||||
|
||||
+10
-6
@@ -35,8 +35,8 @@
|
||||
//!
|
||||
//! A cached raster is made again when something its content reads from outside the object
|
||||
//! changes ([`Inputs`]): the pattern definitions, symbols, images and brushes it uses, the
|
||||
//! document's raster resolution, color mode and substitution highlights, the knockout its object
|
||||
//! inherits, the color settings and fonts, the plug-ins its plug-in effects run, and the options
|
||||
//! document's raster resolution and color mode, the substitution highlights, the knockout its
|
||||
//! object inherits, the color settings and fonts, the plug-ins its plug-in effects run, and the options
|
||||
//! and layer settings it is drawn with.
|
||||
//!
|
||||
//! The same applies to one fill or stroke's own raster effects (around that item alone) and to
|
||||
@@ -941,8 +941,8 @@ pub(crate) struct Reads {
|
||||
images: bool,
|
||||
/// Brushed strokes: the brush library.
|
||||
brushes: bool,
|
||||
/// Type: the fonts, whether overprinting shows ([`crate::proof::overprints`]) and Document
|
||||
/// Setup's substitution highlights.
|
||||
/// Type: the fonts, whether overprinting shows ([`crate::proof::overprints`]) and the
|
||||
/// substitution highlights.
|
||||
text: bool,
|
||||
/// Plug-in effects on its objects: the plug-ins installed. Those in the symbol and pattern art
|
||||
/// it shows are found with the document's inputs ([`DocInputs`]).
|
||||
@@ -2136,8 +2136,12 @@ impl Renderer {
|
||||
self.brushes.library_key(f.doc, self.stamp).hash(&mut h);
|
||||
}
|
||||
if reads.text {
|
||||
let setup = &f.doc.setup;
|
||||
let highlights = f.opts.highlight_substitutions.then_some((setup.highlight_substituted_fonts, setup.highlight_substituted_glyphs));
|
||||
let o = f.opts;
|
||||
let highlights = o.highlight_substitutions.then_some((
|
||||
o.highlight_substituted_fonts,
|
||||
f.doc.setup.highlight_substituted_glyphs,
|
||||
o.highlight_alternate_glyphs,
|
||||
));
|
||||
(vectorcraft_text::FontDb::global().generation(), crate::proof::overprints(f.opts), highlights).hash(&mut h);
|
||||
}
|
||||
if reads.plugins || (doc.plugins && (reads.symbols || reads.patterns)) {
|
||||
|
||||
@@ -145,8 +145,12 @@ pub struct RenderOptions {
|
||||
/// View Opacity Mask (Alt-click the mask thumbnail): instead of the artwork, show this object's
|
||||
/// opacity mask alone as its coverage in greyscale (white = opaque, black = transparent).
|
||||
pub mask_view: Option<NodeId>,
|
||||
/// Screen view: highlight substituted fonts and glyphs as Document Setup asks.
|
||||
/// Screen view: highlight substituted fonts and glyphs, as Preferences › Type and Document
|
||||
/// Setup ask.
|
||||
pub highlight_substitutions: bool,
|
||||
/// Screen view: Preferences › Type › Highlight Substituted Fonts. Pink behind text drawn in a
|
||||
/// substitute for a missing font, in every document (on by default).
|
||||
pub highlight_substituted_fonts: bool,
|
||||
/// Screen view: Preferences › Type › Highlight Alternate Glyphs. Pink behind glyphs OpenType
|
||||
/// features substituted (ligatures, contextual forms), not Document Setup's missing-glyph
|
||||
/// highlights.
|
||||
@@ -266,6 +270,7 @@ impl Default for RenderOptions {
|
||||
guide: ([0x4a, 0xd8, 0xff], false),
|
||||
mask_view: None,
|
||||
highlight_substitutions: false,
|
||||
highlight_substituted_fonts: true,
|
||||
highlight_alternate_glyphs: false,
|
||||
anti_alias: AntiAlias::Art,
|
||||
progressive_placed: false,
|
||||
@@ -1976,10 +1981,9 @@ impl Renderer {
|
||||
self.draw_text_items(ctx, f, n, below, all, tb);
|
||||
}
|
||||
if f.opts.highlight_substitutions {
|
||||
let setup = &f.doc.setup;
|
||||
for (on, path) in [
|
||||
(setup.highlight_substituted_fonts, Some(&g.substituted_fonts)),
|
||||
(setup.highlight_substituted_glyphs, Some(&g.substituted_glyphs)),
|
||||
(f.opts.highlight_substituted_fonts, Some(&g.substituted_fonts)),
|
||||
(f.doc.setup.highlight_substituted_glyphs, Some(&g.substituted_glyphs)),
|
||||
// Measured only when shown: it lays the text out again.
|
||||
(f.opts.highlight_alternate_glyphs, f.opts.highlight_alternate_glyphs.then(|| g.alternate_glyphs(t))),
|
||||
] {
|
||||
@@ -2535,7 +2539,8 @@ struct TextGeom {
|
||||
spans: Vec<(usize, usize, usize)>,
|
||||
bounds: Rect,
|
||||
/// The boxes of characters whose font is missing (drawn in a substitute), and of glyphs the
|
||||
/// font lacks (drawn from a fallback font): Document Setup's substitution highlights.
|
||||
/// font lacks (drawn from a fallback font): Highlight Substituted Fonts and Highlight
|
||||
/// Substituted Glyphs.
|
||||
substituted_fonts: BezPath,
|
||||
substituted_glyphs: BezPath,
|
||||
/// Glyphs OpenType may have substituted (a ligature, or not the face's cmap glyph for their
|
||||
@@ -2733,7 +2738,8 @@ fn text_geom_snapped(t: &TextObject, snap: Option<Affine>) -> TextGeom {
|
||||
}
|
||||
}
|
||||
|
||||
/// Document Setup's and Preferences › Highlight Alternate Glyphs pink (screen only).
|
||||
/// The substitution highlights' pink: Preferences › Highlight Substituted Fonts and Highlight
|
||||
/// Alternate Glyphs, and Document Setup's Highlight Substituted Glyphs (screen only).
|
||||
const SUBSTITUTED: peniko::Color = peniko::Color::from_rgba8(255, 120, 190, 110);
|
||||
/// The pixel size of `region` rendered at `scale` pixels per point (at least 1×1).
|
||||
pub fn region_pixels(region: Rect, scale: f64) -> (u32, u32) {
|
||||
|
||||
@@ -1,8 +1,37 @@
|
||||
//! Preferences › Type › Highlight Alternate Glyphs (#394): OpenType substitutions get a pink
|
||||
//! cell behind them on screen when the preference is on.
|
||||
//! cell behind them on screen when the preference is on. Preferences › Type › Highlight
|
||||
//! Substituted Fonts does the same for text drawn in a substitute for a missing font.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Highlight Substituted Fonts follows the preference passed in [`RenderOptions`] for every
|
||||
/// document, whatever a document's own setup stored: on, type in a missing font has pink behind
|
||||
/// it; off, it does not.
|
||||
#[test]
|
||||
fn highlight_substituted_fonts_follows_the_preference_in_every_document() {
|
||||
use vectorcraft_doc::text::{CharStyle, TextObject};
|
||||
use vectorcraft_doc::{Node, NodeKind};
|
||||
use vectorcraft_geom::Point;
|
||||
|
||||
let on = RenderOptions { background: Some([255, 255, 255, 255]), highlight_substitutions: true, ..Default::default() };
|
||||
assert!(on.highlight_substituted_fonts, "on by default");
|
||||
let off = RenderOptions { highlight_substituted_fonts: false, ..on.clone() };
|
||||
let pinkish = |px: &[u8]| px.chunks(4).filter(|p| p[0] > 200 && p[1] < 180 && p[2] > 150 && p[2] < 230).count();
|
||||
// A document saved with the old Document Setup value on, and one with it off.
|
||||
for stored in [true, false] {
|
||||
let mut d = Document::new(200.0, 100.0);
|
||||
d.setup.highlight_substituted_fonts = stored;
|
||||
let style = CharStyle { size: 30.0, font_family: "No Such Family".into(), ..Default::default() };
|
||||
let n = Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(Point::new(10.0, 60.0), "Hamburg", style))));
|
||||
let l = d.layers[0].id;
|
||||
d.insert(Some(l), 0, n).unwrap();
|
||||
let with = Renderer::new().render(&d, 200, 100, Affine::IDENTITY, &on);
|
||||
let without = Renderer::new().render(&d, 200, 100, Affine::IDENTITY, &off);
|
||||
assert!(pinkish(&with.pixels) > 50, "stored {stored}: the preference on highlights the text");
|
||||
assert_eq!(pinkish(&without.pixels), 0, "stored {stored}: the preference off leaves no highlight");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_opentype_alternate_marks_ligatures_and_forms() {
|
||||
assert!(is_opentype_alternate(100, 100, 2), "ligature cluster");
|
||||
|
||||
@@ -1794,8 +1794,8 @@ fn missing_font_type(d: &mut Document, fx: vectorcraft_doc::Effect) -> Node {
|
||||
|
||||
/// Raster effects are made again when a document or layer setting they read changes: the raster
|
||||
/// resolution under De-Interlace, Page Knockout Group around a blurred group of translucent
|
||||
/// objects, Document Setup's highlight of substituted fonts under a blur, and the layer's Dim
|
||||
/// Images under an image's blur.
|
||||
/// objects, the Highlight Substituted Fonts and Highlight Alternate Glyphs preferences under a
|
||||
/// blur, and the layer's Dim Images under an image's blur.
|
||||
#[test]
|
||||
fn rasters_draw_document_and_layer_setting_edits() {
|
||||
let _fonts = fonts_steady();
|
||||
@@ -1823,14 +1823,29 @@ fn rasters_draw_document_and_layer_setting_edits() {
|
||||
edit_draws_as_new(with_art(d, vec![group]), |d| d.page_knockout = true);
|
||||
// Blurred type in a missing family, with the substitution highlights on screen.
|
||||
let mut d = Document::new(200.0, 100.0);
|
||||
d.setup.highlight_substituted_fonts = false;
|
||||
let blurred = missing_font_type(&mut d, effect("blur.gaussian", serde_json::json!({"radius": 1.0})));
|
||||
let mut d = with_art(d, vec![blurred]);
|
||||
let highlights = RenderOptions { background: Some([255, 255, 255, 255]), highlight_substitutions: true, ..Default::default() };
|
||||
let unhighlighted = RenderOptions { highlight_substituted_fonts: false, ..highlights.clone() };
|
||||
// The first lookup of the missing family catalogs the installed fonts, which changes the font
|
||||
// generation: a frame drawn first does that.
|
||||
Renderer::new().render(&d, 200, 100, Affine::IDENTITY, &highlights);
|
||||
draws_as_new(&mut d, &highlights, &highlights, |d| d.setup.highlight_substituted_fonts = true);
|
||||
// Preferences › Type › Highlight Substituted Fonts turned on between two frames.
|
||||
draws_as_new(&mut d, &unhighlighted, &highlights, |_| {});
|
||||
// Blurred type with a ligature, with Highlight Alternate Glyphs turned on between two frames.
|
||||
let mut d = Document::new(200.0, 100.0);
|
||||
let mut ligature = missing_font_type(&mut d, effect("blur.gaussian", serde_json::json!({"radius": 1.0})));
|
||||
if let NodeKind::Text(t) = &mut ligature.kind {
|
||||
**t = vectorcraft_doc::text::TextObject::point(
|
||||
vectorcraft_geom::Point::new(10.0, 60.0),
|
||||
"fi",
|
||||
vectorcraft_doc::text::CharStyle { size: 48.0, font_family: "Source Serif 4".into(), ..Default::default() },
|
||||
);
|
||||
}
|
||||
let mut d = with_art(d, vec![ligature]);
|
||||
let alternates = RenderOptions { highlight_alternate_glyphs: true, ..highlights.clone() };
|
||||
Renderer::new().render(&d, 200, 100, Affine::IDENTITY, &highlights);
|
||||
draws_as_new(&mut d, &highlights, &alternates, |_| {});
|
||||
// The layer of a blurred image dims its images to 50%.
|
||||
edit_draws_as_new(blurred_image_doc("dimmed", 106), |d| {
|
||||
if let NodeKind::Layer { dim_images, .. } = &mut Arc::make_mut(&mut d.layers[0]).kind {
|
||||
|
||||
@@ -421,6 +421,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
rot: v.rotation,
|
||||
anti_alias: app.session.prefs.anti_aliased_artwork,
|
||||
highlight_alternate_glyphs: app.session.prefs.highlight_alternate_glyphs,
|
||||
highlight_substituted_fonts: app.session.prefs.highlight_substituted_fonts,
|
||||
placed: vectorcraft_render::placed_document::generation(),
|
||||
pixel,
|
||||
smooth_images,
|
||||
@@ -494,6 +495,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
highlight_substitutions: true,
|
||||
// Type › Highlight Alternate Glyphs (#394): pink behind OpenType substitutions.
|
||||
highlight_alternate_glyphs: app.session.prefs.highlight_alternate_glyphs,
|
||||
// Type › Highlight Substituted Fonts: pink behind text in a substitute font.
|
||||
highlight_substituted_fonts: app.session.prefs.highlight_substituted_fonts,
|
||||
// General › Anti-aliased Artwork: off, edges are hard on screen (raster effects and
|
||||
// pattern tiles stay smooth), as in Illustrator.
|
||||
anti_alias: if app.session.prefs.anti_aliased_artwork { vectorcraft_render::AntiAlias::Art } else { vectorcraft_render::AntiAlias::None },
|
||||
@@ -5614,6 +5617,37 @@ mod tests {
|
||||
assert!(partial(&alphas(&mut app).expect("re-rendered")) > 50, "smooth again");
|
||||
}
|
||||
|
||||
/// Type › Highlight Substituted Fonts: on (the default), type in a missing font has pink
|
||||
/// behind it on the canvas; off, it does not, and turning it either way re-renders.
|
||||
#[test]
|
||||
fn highlight_substituted_fonts_preference_reaches_the_canvas() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
app.canvas.worker_started = true; // render on this thread
|
||||
app.session.execute("text.create", &json!({"x": 20, "y": 120, "text": "Hamburg", "size": 60, "font": "Missing Family"})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
// How many pink pixels the art's raster uploaded by one frame has, None when it was not
|
||||
// re-rendered.
|
||||
let pink = |app: &mut VectorcraftApp| -> Option<usize> {
|
||||
let (_, mut delta) = frame_output(app, &ctx);
|
||||
let art = app.canvas.texture.as_ref().unwrap().id();
|
||||
let n = delta.set.iter().find(|e| e.0.eq(&art)).and_then(|e| e.1.last()).map(|d| {
|
||||
let egui::ImageData::Color(img) = &d.image;
|
||||
img.pixels.iter().filter(|c| c.a() != 0 && c.r() > c.g().saturating_add(30) && c.b() > c.g()).count()
|
||||
});
|
||||
delta.clear();
|
||||
n
|
||||
};
|
||||
let on = pink(&mut app).expect("the first frame renders the art");
|
||||
assert!(on > 100, "on by default: the text has pink behind it ({on} pixels)");
|
||||
assert!(app.canvas.shown.as_ref().is_some_and(|(_, o)| o.highlight_substituted_fonts));
|
||||
app.session.execute("prefs.set", &json!({"key": "highlightSubstitutedFonts", "value": false})).unwrap();
|
||||
assert_eq!(pink(&mut app), Some(0), "the preference off re-renders without the highlight");
|
||||
assert!(app.canvas.key.as_ref().is_some_and(|k| !k.highlight_substituted_fonts));
|
||||
app.session.execute("prefs.set", &json!({"key": "highlightSubstitutedFonts", "value": true})).unwrap();
|
||||
assert_eq!(pink(&mut app), Some(on), "on again, as before");
|
||||
}
|
||||
|
||||
/// A drag of an object with raster effects (#1257): the drag's frames render while
|
||||
/// interacting and draw the object's drop shadow and filtered content from the rasters of the
|
||||
/// frame before; the frame after the drag renders without `interacting`, on the render worker,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! File → Document Setup: General (units, bleed, Edit Artboards, outline images, substitution
|
||||
//! highlights), Transparency (grid size and colours, simulated paper, flattener preset, white
|
||||
//! File → Document Setup: General (units, bleed, Edit Artboards, outline images, the substituted
|
||||
//! glyphs highlight), Transparency (grid size and colours, simulated paper, flattener preset, white
|
||||
//! overprint) and Type (language, quotes, superscript/subscript/small caps, text export) tabs.
|
||||
//!
|
||||
//! Fields: what `document.setup` reports (and takes back), plus `tab` and `bleedLinked`. OK runs
|
||||
@@ -98,7 +98,6 @@ fn general(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool
|
||||
});
|
||||
ui.add_space(12.0);
|
||||
check(ui, d, "outlineImages", tl!("Show Images in Outline Mode"));
|
||||
check(ui, d, "highlightSubstitutedFonts", tl!("Highlight Substituted Fonts"));
|
||||
check(ui, d, "highlightSubstitutedGlyphs", tl!("Highlight Substituted Glyphs"));
|
||||
close
|
||||
}
|
||||
@@ -325,4 +324,23 @@ mod tests {
|
||||
assert!(crate::dialogs::confirm(&mut app).is_err());
|
||||
assert!(app.ui.dialog.is_some());
|
||||
}
|
||||
|
||||
/// Highlight Substituted Fonts is in Preferences › Type, so the General tab shows only the
|
||||
/// substituted glyphs highlight, and OK still applies.
|
||||
#[test]
|
||||
fn general_tab_has_no_substituted_fonts_checkbox() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 200, "height": 200})).unwrap();
|
||||
app.run("file.documentSetup", json!({})).unwrap();
|
||||
assert!(app.ui.dialog.as_ref().unwrap().fields.get("highlightSubstitutedFonts").is_none());
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
crate::theme::apply(&ctx, Default::default());
|
||||
dialog_frame(&mut app, &ctx, vec![]);
|
||||
let texts: Vec<String> = dialog_frame(&mut app, &ctx, vec![]).into_iter().map(|(t, _)| t).collect();
|
||||
assert!(texts.iter().any(|t| t == "Highlight Substituted Glyphs"), "{texts:?}");
|
||||
assert!(!texts.iter().any(|t| t == "Highlight Substituted Fonts"), "{texts:?}");
|
||||
crate::dialogs::confirm(&mut app).unwrap();
|
||||
assert!(app.ui.dialog.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,6 +432,8 @@ pub struct CacheKey {
|
||||
pub anti_alias: bool,
|
||||
/// Type › Highlight Alternate Glyphs (#394).
|
||||
pub highlight_alternate_glyphs: bool,
|
||||
/// Type › Highlight Substituted Fonts.
|
||||
pub highlight_substituted_fonts: bool,
|
||||
/// [`vectorcraft_render::placed_document::generation`]: placed documents' bitmaps made since.
|
||||
pub placed: u64,
|
||||
/// View › Pixel Preview: the document pixels rendered (x0, y0, x1, y1), one per point, shown
|
||||
|
||||
@@ -466,7 +466,7 @@ string is points). The `preferences` dialog's `unitsGeneral` starts as the activ
|
||||
|
||||
Document Setup: File → Document Setup… (`file.documentSetup`, Cmd+Alt+P) opens the `documentSetup` dialog. Its fields
|
||||
are what `document.setup` reports (`units`, `bleed` [top, bottom, left, right] in pt, `outlineImages`,
|
||||
`highlightSubstitutedFonts`, `highlightSubstitutedGlyphs`, `gridSize`, `gridColors`, `simulatePaper`, `flattenerPreset`,
|
||||
`highlightSubstitutedGlyphs`, `gridSize`, `gridColors`, `simulatePaper`, `flattenerPreset`,
|
||||
`discardWhiteOverprint`, `language`, `quotes`, `typographersQuotes`, `superscript`, `subscript`, `smallCapsSize`,
|
||||
`exportText`, `backgroundContents`) plus `tab` (`General`, `Transparency` or `Type`) and `bleedLinked`.
|
||||
`ui.dialog.confirm` runs `document.setup` with them as one undo step; a bad value answers with an error and the dialog
|
||||
|
||||
+10
-4
@@ -2784,8 +2784,9 @@ off, an empty window, and the Home button or `app.home` still shows the screen),
|
||||
default: on, a click away from a panel popped out of the icon column puts it away), `displayPrintSize` (off by
|
||||
default: View › Actual Size / `view.actualSize` is one document point per screen point; on, one document inch fills
|
||||
96 screen points, an inch at the system's reference density whatever the display scaling). `highlightAlternateGlyphs` (off by default: on, the canvas paints a pink cell behind glyphs OpenType features
|
||||
substituted — ligatures, contextual forms — on screen; Document Setup's Highlight Substituted Fonts / Glyphs stay
|
||||
separate). `blackOnScreen`
|
||||
substituted — ligatures, contextual forms — on screen; Document Setup's Highlight Substituted Glyphs stays
|
||||
separate). `highlightSubstitutedFonts` (on by default: the canvas paints pink behind text drawn in a substitute for a
|
||||
missing font, in every document). `blackOnScreen`
|
||||
(Appearance of Black › On Screen, `accurate` by default) sets how the canvas shows pure CMYK black, black ink alone
|
||||
at 100%: `accurate` shows it through the CMYK profile, a dark grey, and `rich` shows it as RGB black. Rich blacks,
|
||||
black tints and images show the same either way, and exports are unchanged. `blackOutput` (Appearance of Black ›
|
||||
@@ -2901,8 +2902,13 @@ Pen's second anchor at (150, 431), in line with the square's centre.
|
||||
matched without regard to case: a word with no hyphens is never hyphenated (`typography`), and a word with hyphens
|
||||
breaks only where they are (`hap-pen`). `prefs.set` pushes the list into the hyphenator and reflows open documents.
|
||||
- `highlightAlternateGlyphs` (off by default): Preferences › Type › Highlight Alternate Glyphs. On, the canvas paints
|
||||
pink behind OpenType substitutions (ligatures, contextual forms). Document Setup's Highlight Substituted Fonts /
|
||||
Glyphs stay separate. Agents set it with `prefs.set`; the desktop canvas reads it on the next paint.
|
||||
pink behind OpenType substitutions (ligatures, contextual forms). Document Setup's Highlight Substituted Glyphs
|
||||
stays separate. Agents set it with `prefs.set`; the desktop canvas reads it on the next paint.
|
||||
- `highlightSubstitutedFonts` (on by default): Preferences › Type › Highlight Substituted Fonts. On, the canvas paints
|
||||
pink behind text drawn in a substitute for a missing font, in every open document. Off, no document shows that
|
||||
highlight. `document.setup` ignores this key and adds a `warnings` entry that names `prefs.set` to its reply. A file
|
||||
saved with the key in its Document Setup still opens and keeps the value it was saved with, and the preference
|
||||
decides whether that file shows the highlight.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"prefs.set","params":{"values":{"typeSizeIncrement":4,"trackingIncrement":50}}}}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user