mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 10:47:11 -06:00
Merge pull request #4099 from dotCooCoo/feat/highlight-substituted-fonts-preference
Type: Highlight Substituted Fonts moves to Preferences › Type and applies to every document
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