mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 21:10:40 +00:00
Select Text by Font: limit it to artboards or layers (#5529)
Squashed from #5578 onto main.
This commit is contained in:
@@ -10,8 +10,9 @@ use vectorcraft_color::Paint;
|
||||
use vectorcraft_doc::{AppearanceItem, Document, Node, NodeId, NodeKind, TextKind, TextObject, TextRun};
|
||||
use vectorcraft_geom::Rect;
|
||||
|
||||
use super::select::on_artboard;
|
||||
use super::typecmd::refresh_bounds;
|
||||
use super::{CommandSpec, Result, Session, always, bad, bool_or, cmd, has_doc, str_param};
|
||||
use super::{CommandSpec, Result, Session, always, bad, bool_or, cmd, has_doc, index, str_param};
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
@@ -38,7 +39,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Select Text by Font",
|
||||
[],
|
||||
None,
|
||||
"{family, style?} select the text objects using the font, and the instances of the symbols whose art uses it, that are not hidden or locked, in a hidden or locked group or layer, or on a locked artboard → {count}",
|
||||
"{family, style?, scope?: \"document\" (default) | {artboards: [index, …] (0-based)} | {layers: [id, …] (layers or sublayers)}} select the text objects using the font, and the instances of the symbols whose art uses it, that are not hidden or locked, in a hidden or locked group or layer, or on a locked artboard; with scope, only those on one of the artboards (as All on Active Artboard takes them: their bounds overlap it or their center is on it) or in one of the layers → {count}",
|
||||
has_doc,
|
||||
select_font
|
||||
),
|
||||
@@ -713,6 +714,7 @@ fn select_font(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let style = str_param(p, "style").map(str::to_string);
|
||||
let ids = scope(s, false, None)?;
|
||||
let d = &s.doc()?.doc;
|
||||
let within = FontScope::read(d, p)?;
|
||||
// Only text the Selection tool can reach: not hidden or locked, in a hidden or locked group
|
||||
// or layer, or on a locked artboard.
|
||||
let editable = d.editable();
|
||||
@@ -758,11 +760,65 @@ fn select_font(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
});
|
||||
}
|
||||
drop(editable);
|
||||
let symbols = d.symbol_boxes();
|
||||
hits.retain(|id| within.holds(d, &symbols, *id));
|
||||
drop(symbols);
|
||||
let n = hits.len();
|
||||
s.select(|_, sel| sel.set(hits))?;
|
||||
Ok(json!({ "count": n }))
|
||||
}
|
||||
|
||||
/// Where Select Text by Font looks: its `scope`.
|
||||
enum FontScope {
|
||||
Document,
|
||||
/// The artboards' rectangles.
|
||||
Artboards(Vec<Rect>),
|
||||
/// The layers and sublayers, with what they hold.
|
||||
Layers(BTreeSet<NodeId>),
|
||||
}
|
||||
|
||||
impl FontScope {
|
||||
fn read(d: &Document, p: &Value) -> Result<Self> {
|
||||
const C: &str = "select.font";
|
||||
let expected = || bad(C, "`scope` must be \"document\", {artboards: [index, …]} or {layers: [id, …]}");
|
||||
let scope = match p.get("scope") {
|
||||
None | Some(Value::Null) => return Ok(Self::Document),
|
||||
Some(Value::String(v)) if v == "document" => return Ok(Self::Document),
|
||||
Some(Value::Object(m)) if m.len() == 1 => m,
|
||||
Some(_) => return Err(expected()),
|
||||
};
|
||||
let list = |key: &str| -> Result<Option<&Vec<Value>>> {
|
||||
match scope.get(key) {
|
||||
None => Ok(None),
|
||||
Some(Value::Array(a)) if !a.is_empty() => Ok(Some(a)),
|
||||
Some(_) => Err(bad(C, format!("`scope.{key}` must be a non-empty array"))),
|
||||
}
|
||||
};
|
||||
if let Some(a) = list("artboards")? {
|
||||
let rect = |v: &Value| v.as_u64().and_then(|i| d.artboards.get(index(i))).map(|b| b.rect);
|
||||
return a.iter().map(|v| rect(v).ok_or_else(|| bad(C, format!("no such artboard {v}")))).collect::<Result<_>>().map(Self::Artboards);
|
||||
}
|
||||
if let Some(a) = list("layers")? {
|
||||
let layer = |v: &Value| v.as_u64().map(NodeId).filter(|id| d.node(*id).is_some_and(Node::is_layer));
|
||||
return a
|
||||
.iter()
|
||||
.map(|v| layer(v).ok_or_else(|| bad(C, format!("{v} names no layer or sublayer"))))
|
||||
.collect::<Result<_>>()
|
||||
.map(Self::Layers);
|
||||
}
|
||||
Err(expected())
|
||||
}
|
||||
|
||||
/// Is object `id` in the scope.
|
||||
fn holds(&self, d: &Document, symbols: &vectorcraft_doc::SymbolBoxes, id: NodeId) -> bool {
|
||||
match self {
|
||||
Self::Document => true,
|
||||
Self::Artboards(rects) => d.node(id).is_some_and(|n| rects.iter().any(|r| on_artboard(n, symbols, *r))),
|
||||
Self::Layers(layers) => d.ancestry(id).is_some_and(|a| a.iter().any(|i| layers.contains(i))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn font_list(_: &mut Session, p: &Value) -> Result<Value> {
|
||||
let db = vectorcraft_text::FontDb::global();
|
||||
match str_param(p, "family") {
|
||||
@@ -911,6 +967,95 @@ mod tests {
|
||||
assert!(s.doc().unwrap().selection.objects.is_empty());
|
||||
}
|
||||
|
||||
/// Select Text by Font with an artboard scope takes the text on the artboards, as All on
|
||||
/// Active Artboard does: overlapping one, or centered on it, a straddling one included.
|
||||
#[test]
|
||||
fn select_font_in_artboards() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
s.execute("artboard.new", &json!({"x": 300, "y": 0, "width": 200, "height": 200})).unwrap();
|
||||
s.execute("artboard.new", &json!({"x": 600, "y": 0, "width": 200, "height": 200})).unwrap();
|
||||
let text = |s: &mut Session, x: i32| {
|
||||
NodeId(s.execute("text.create", &json!({"x": x, "y": 50, "text": "Serif", "font": "Source Serif 4"})).unwrap()["id"].as_u64().unwrap())
|
||||
};
|
||||
let first = text(&mut s, 10);
|
||||
let straddling = text(&mut s, 180);
|
||||
let second = text(&mut s, 350);
|
||||
let third = text(&mut s, 650);
|
||||
let _off = text(&mut s, 1000);
|
||||
let select = |s: &mut Session, scope: Value| {
|
||||
s.execute("select.font", &json!({"family": "Source Serif 4", "scope": scope})).unwrap();
|
||||
s.doc().unwrap().selection.objects.clone()
|
||||
};
|
||||
assert_eq!(select(&mut s, json!({"artboards": [0]})), [first, straddling]);
|
||||
assert_eq!(select(&mut s, json!({"artboards": [1, 2]})), [second, third]);
|
||||
assert_eq!(select(&mut s, json!("document")).len(), 5);
|
||||
// The same objects All on Active Artboard takes.
|
||||
s.execute("select.allOnArtboard", &json!({"artboard": 0})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().selection.objects, [first, straddling]);
|
||||
// Hidden text stays out.
|
||||
s.execute("select.set", &json!({"ids": [third.0]})).unwrap();
|
||||
s.execute("object.hide", &json!({})).unwrap();
|
||||
assert_eq!(select(&mut s, json!({"artboards": [1, 2]})), [second]);
|
||||
for bad in [json!({"artboards": [3]}), json!({"artboards": []}), json!({"artboards": ["x"]}), json!({"pages": [0]}), json!("all"), json!(7)] {
|
||||
assert!(s.execute("select.font", &json!({"family": "Source Serif 4", "scope": bad})).is_err(), "{bad}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Select Text by Font with a layer scope takes the text in the layers, their sublayers
|
||||
/// included, and still leaves out hidden and locked text.
|
||||
#[test]
|
||||
fn select_font_in_layers() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
let text = |s: &mut Session, y: i32| {
|
||||
NodeId(s.execute("text.create", &json!({"x": 10, "y": y, "text": "Serif", "font": "Source Serif 4"})).unwrap()["id"].as_u64().unwrap())
|
||||
};
|
||||
let first_layer = s.doc().unwrap().doc.layers[0].id;
|
||||
let a = text(&mut s, 20);
|
||||
let second_layer = s.execute("layer.new", &json!({})).unwrap()["id"].as_u64().unwrap();
|
||||
let b = text(&mut s, 40);
|
||||
let sub = s.execute("layer.newSublayer", &json!({"parent": second_layer})).unwrap()["id"].as_u64().unwrap();
|
||||
s.execute("layer.setCurrent", &json!({"id": sub})).unwrap();
|
||||
let in_sub = text(&mut s, 60);
|
||||
let locked = text(&mut s, 80);
|
||||
s.execute("select.set", &json!({"ids": [locked.0]})).unwrap();
|
||||
s.execute("object.lock", &json!({})).unwrap();
|
||||
let select = |s: &mut Session, layers: Value| {
|
||||
s.execute("select.font", &json!({"family": "Source Serif 4", "scope": {"layers": layers}})).unwrap();
|
||||
let mut v = s.doc().unwrap().selection.objects.clone();
|
||||
v.sort();
|
||||
v
|
||||
};
|
||||
let d = &s.doc().unwrap().doc;
|
||||
assert_eq!(d.ancestry(in_sub).unwrap()[1], NodeId(sub), "the text is in the sublayer");
|
||||
assert_eq!(select(&mut s, json!([first_layer.0])), [a]);
|
||||
assert_eq!(select(&mut s, json!([second_layer])), [b, in_sub]);
|
||||
assert_eq!(select(&mut s, json!([sub])), [in_sub]);
|
||||
assert_eq!(select(&mut s, json!([first_layer.0, sub])), [a, in_sub]);
|
||||
// Not a layer: an error, the selection as it was.
|
||||
assert!(s.execute("select.font", &json!({"family": "Source Serif 4", "scope": {"layers": [a.0]}})).is_err());
|
||||
assert!(s.execute("select.font", &json!({"family": "Source Serif 4", "scope": {"layers": [u64::MAX]}})).is_err());
|
||||
assert_eq!(s.doc().unwrap().selection.objects.len(), 2);
|
||||
}
|
||||
|
||||
/// A symbol instance whose art uses the font is selected when the instance is in scope.
|
||||
#[test]
|
||||
fn select_font_scope_takes_symbol_instances_in_it() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
s.execute("artboard.new", &json!({"x": 300, "y": 0, "width": 200, "height": 200})).unwrap();
|
||||
let id = s.execute("text.create", &json!({"x": 10, "y": 50, "text": "Serif", "font": "Source Serif 4"})).unwrap()["id"].clone();
|
||||
s.execute("symbol.new", &json!({"name": "Mark", "ids": [id]})).unwrap();
|
||||
let instance = s.doc().unwrap().selection.objects[0];
|
||||
let select = |s: &mut Session, i: u64| {
|
||||
s.execute("select.font", &json!({"family": "Source Serif 4", "scope": {"artboards": [i]}})).unwrap()["count"].clone()
|
||||
};
|
||||
assert_eq!(select(&mut s, 0), 1);
|
||||
assert_eq!(s.doc().unwrap().selection.objects, [instance]);
|
||||
assert_eq!(select(&mut s, 1), 0);
|
||||
}
|
||||
|
||||
/// Replace Font leaves hidden and locked text in its font, and text inside a hidden or locked
|
||||
/// group or layer, and says so. Text on a visible, unlocked template layer is changed.
|
||||
#[test]
|
||||
|
||||
@@ -230,6 +230,10 @@ fn structured_junk() {
|
||||
("document.export", json!({"format": "bmp", "rect": {"x": 1}, "artboard": 3})),
|
||||
("document.export", json!({"format": "svg", "rect": [0, 0, 10, 10]})),
|
||||
("select.add", json!({"ids": [[1], {"a": 1}]})),
|
||||
("select.font", json!({"family": "Source Sans 3", "scope": {"artboards": [0, u64::MAX, -1, "x", null, 1.5]}})),
|
||||
("select.font", json!({"family": "Source Sans 3", "scope": {"layers": [0, u64::MAX, -1, [1], {"a": 1}]}})),
|
||||
("select.font", json!({"family": "Source Sans 3", "scope": {"artboards": [], "layers": [1]}})),
|
||||
("select.font", json!({"family": "Source Sans 3", "scope": 7})),
|
||||
("select.anchors", json!({"id": 2, "anchors": [[99, 99], [-1, 0], ["a", "b"]], "mode": "set"})),
|
||||
("select.anchorsMany", json!({"items": [{"id": 2, "anchors": [[0, 1000]]}, null, 5]})),
|
||||
("select.anchorsMany", json!({"items": [{"id": 2, "anchors": [[0, 1000], [0, 0]]}, {"id": u64::MAX}], "mode": "toggle"})),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! Type → Find Font…: the fonts the document uses (missing ones marked), Find (select the text
|
||||
//! using a font), Change / Change All to another font from the list Replace With Font From shows
|
||||
//! (the document's fonts, the recent fonts or every installed font), and Find in Folder… (the
|
||||
//! missing fonts' files, in the Missing Fonts dialog: `ui.findFontsInFolder`).
|
||||
//! missing fonts' files, in the Missing Fonts dialog: `ui.findFontsInFolder`). Find In limits Find
|
||||
//! to the active artboard or the selected layers.
|
||||
|
||||
use egui::Ui;
|
||||
use serde_json::{Value, json};
|
||||
@@ -23,9 +24,10 @@ pub fn open_on_missing(app: &mut VectorcraftApp) {
|
||||
open_at(app, first.unwrap_or(0));
|
||||
}
|
||||
|
||||
/// Open the dialog with the document's font `selected` (an index into `text.fonts`) chosen, and
|
||||
/// System's font ([`system_family`]) to change to.
|
||||
/// Open the dialog with the document's font `selected` (an index into `text.fonts`) chosen,
|
||||
/// System's font ([`system_family`]) to change to, and the last Find In choice (Document at first).
|
||||
fn open_at(app: &mut VectorcraftApp, selected: usize) {
|
||||
let find_in = FIND_IN.iter().find(|f| **f == app.ui.find_font_in).copied().unwrap_or("document");
|
||||
let used = fonts(app);
|
||||
let source = "system";
|
||||
let rows = replacement_rows(app, source, &used);
|
||||
@@ -33,7 +35,7 @@ fn open_at(app: &mut VectorcraftApp, selected: usize) {
|
||||
let style = rows.iter().find(|r| r.family.eq_ignore_ascii_case(&family)).map(|r| r.style.clone()).unwrap_or_default();
|
||||
app.ui.dialog = Some(Dialog::new(
|
||||
"findFont",
|
||||
json!({ "selected": selected, "family": family, "style": style, "selectionOnly": false, "replaceFrom": source, "rows": rows_json(&rows) }),
|
||||
json!({ "selected": selected, "family": family, "style": style, "selectionOnly": false, "replaceFrom": source, "rows": rows_json(&rows), "findIn": find_in }),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -210,6 +212,42 @@ fn set_source(app: &VectorcraftApp, d: &mut Dialog, source: &str, used: &[Value]
|
||||
d.fields.insert("style".into(), json!(pick.map(|r| r.style.clone()).unwrap_or_default()));
|
||||
}
|
||||
|
||||
/// Find In's choices (the `findIn` field): the whole document, the active artboard, or the layers
|
||||
/// highlighted in the Layers panel (else the current layer).
|
||||
const FIND_IN: [&str; 3] = ["document", "artboard", "layers"];
|
||||
const FIND_IN_LABELS: [&str; 3] = ["Document", "Artboard", "Layers"];
|
||||
|
||||
/// The `scope` Find passes `select.font` for Find In choice `find_in`. A document without
|
||||
/// artboards, or without a layer to look in, is looked through whole.
|
||||
fn find_scope(app: &VectorcraftApp, find_in: &str) -> Value {
|
||||
let Some(st) = app.session.active() else { return json!("document") };
|
||||
let n = st.doc.artboards.len();
|
||||
match find_in {
|
||||
"artboard" if n > 0 => json!({ "artboards": [crate::panels::artboards::selected(app, n)] }),
|
||||
"layers" => {
|
||||
let is_layer = |id: &vectorcraft_doc::NodeId| st.doc.node(*id).is_some_and(vectorcraft_doc::Node::is_layer);
|
||||
let mut layers: Vec<u64> = st.highlighted_rows().into_iter().filter(is_layer).map(|id| id.0).collect();
|
||||
if layers.is_empty() {
|
||||
layers.extend(st.current_layer().map(|id| id.0));
|
||||
}
|
||||
if layers.is_empty() { json!("document") } else { json!({ "layers": layers }) }
|
||||
}
|
||||
_ => json!("document"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the dialog's button `button` (find, change or changeAll) on the document font `from`.
|
||||
fn run_button(app: &mut VectorcraftApp, d: &Dialog, button: &str, from: &Value) -> Result<Value, String> {
|
||||
match button {
|
||||
"find" => {
|
||||
let scope = find_scope(app, &d.str("findIn"));
|
||||
app.run("select.font", json!({ "family": from["family"], "style": from["style"], "scope": scope }))
|
||||
}
|
||||
"change" => change(app, d, from, false),
|
||||
_ => change(app, d, from, true),
|
||||
}
|
||||
}
|
||||
|
||||
fn fonts(app: &mut VectorcraftApp) -> Vec<Value> {
|
||||
app.session.execute("text.fonts", &json!({})).ok().and_then(|v| v.as_array().cloned()).unwrap_or_default()
|
||||
}
|
||||
@@ -342,6 +380,14 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
}
|
||||
ui.add_space(14.0);
|
||||
ui.horizontal(|ui| {
|
||||
widgets::field_label(ui, tl!("Find In"));
|
||||
let at = FIND_IN.iter().position(|s| *s == d.str("findIn")).unwrap_or(0);
|
||||
if let Some(i) = widgets::dropdown_named(ui, "ff-find-in", Some(tl!("Find In")), FIND_IN_LABELS[at], &FIND_IN_LABELS, 120.0) {
|
||||
d.fields.insert("findIn".into(), json!(FIND_IN[i]));
|
||||
}
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
// An empty list (Recent before any font was applied) leaves nothing to change to.
|
||||
let replace = sel < list.len() && can_replace(&d, families.as_deref());
|
||||
ui.horizontal(|ui| {
|
||||
@@ -362,14 +408,10 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
});
|
||||
});
|
||||
});
|
||||
app.ui.find_font_in = d.str("findIn");
|
||||
let sel = d.fields.get("selected").and_then(Value::as_u64).unwrap_or(0) as usize;
|
||||
if let (Some(a), Some(from)) = (act, list.get(sel)) {
|
||||
let r = match a {
|
||||
"find" => app.run("select.font", json!({ "family": from["family"], "style": from["style"] })),
|
||||
"change" => change(app, &d, from, false),
|
||||
_ => change(app, &d, from, true),
|
||||
};
|
||||
match r {
|
||||
match run_button(app, &d, a, from) {
|
||||
// The rows are a snapshot: the document's fonts and the recent fonts have moved on.
|
||||
Ok(_) if a != "find" => {
|
||||
let used = fonts(app);
|
||||
@@ -433,6 +475,48 @@ mod tests {
|
||||
assert_eq!(fonts(&mut app)[0]["family"], "Source Sans 3");
|
||||
}
|
||||
|
||||
/// Find In passes Find the active artboard, or the selected layers (else the current
|
||||
/// layer), as `select.font`'s scope.
|
||||
#[test]
|
||||
fn find_in_limits_find_to_the_artboard_or_the_layers() {
|
||||
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
|
||||
app.session.execute("artboard.new", &json!({"x": 200, "y": 0, "width": 100, "height": 100})).unwrap();
|
||||
let text = |app: &mut VectorcraftApp, x: i32| {
|
||||
app.session.execute("text.create", &json!({"x": x, "y": 40, "text": "Hi", "font": "Inter"})).unwrap()["id"].as_u64().unwrap()
|
||||
};
|
||||
let first = text(&mut app, 10);
|
||||
let layer = app.session.execute("layer.new", &json!({})).unwrap()["id"].as_u64().unwrap();
|
||||
let second = text(&mut app, 210);
|
||||
let third = text(&mut app, 20);
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
// Choose Find In on the open dialog, draw a frame, then press Find.
|
||||
let find = |app: &mut VectorcraftApp, find_in: &str| {
|
||||
open(app);
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("findIn".into(), json!(find_in));
|
||||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| show(app, ui.ctx()));
|
||||
out.textures_delta.clear();
|
||||
let d = app.ui.dialog.clone().unwrap();
|
||||
let from = fonts(app).into_iter().find(|f| f["family"] == "Inter").unwrap();
|
||||
run_button(app, &d, "find", &from).unwrap();
|
||||
let mut ids: Vec<u64> = app.session.active().unwrap().selection.objects.iter().map(|id| id.0).collect();
|
||||
ids.sort_unstable();
|
||||
ids
|
||||
};
|
||||
open(&mut app);
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("findIn"), "document");
|
||||
assert_eq!(find(&mut app, "document"), [first, second, third]);
|
||||
// Without a view, the active artboard is the first.
|
||||
assert_eq!(find(&mut app, "artboard"), [first, third]);
|
||||
assert_eq!(find_scope(&app, "layers"), json!({ "layers": [layer] }));
|
||||
assert_eq!(find(&mut app, "layers"), [second, third]);
|
||||
// Closing and opening the dialog again keeps the last choice.
|
||||
app.ui.dialog = None;
|
||||
open(&mut app);
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("findIn"), "layers");
|
||||
}
|
||||
|
||||
/// Change All leaves locked text in its font, and the status bar says so.
|
||||
#[test]
|
||||
fn change_all_clears_the_skip_warning_after_unlocking_text() {
|
||||
|
||||
@@ -2699,6 +2699,7 @@ digits Malayalam Малаялам
|
||||
Reset Transformation Нулиране на трансформацията
|
||||
Replace With Font Замяна с шрифт
|
||||
Replace With Font From Замяна с шрифт от
|
||||
Find In Търсене в
|
||||
System Система
|
||||
Replaces the saved preset of that name. Заменя записаната предварителна настройка с това име.
|
||||
Replace… Замяна…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4000,6 +4000,7 @@ effect Regular Obično
|
||||
Find in Folder… Pronađi u folderu…
|
||||
Replace With Font Zamijeni fontom
|
||||
Replace With Font From Zamijeni fontom iz
|
||||
Find In Traži u
|
||||
System Sistem
|
||||
— substituted by {style} — zamijenjeno stilom {style}
|
||||
— {n} characters from another font — {n} znakova iz drugog fonta
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3406,6 +3406,7 @@ digits Malayalam Malaiàlam
|
||||
Reset Transformation Restableix la transformació
|
||||
Replace With Font Reemplaça per un tipus de lletra
|
||||
Replace With Font From Reemplaça per un tipus de lletra de
|
||||
Find In Cerca a
|
||||
System Sistema
|
||||
Replace “{name}”, the file this document was opened from? Voleu reemplaçar “{name}”, el fitxer amb què es va obrir aquest document?
|
||||
Replaces the saved preset of that name. Reemplaça el preajust desat amb aquest nom.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4050,6 +4050,7 @@ effect Regular Pravidelný
|
||||
Find in Folder… Najít ve složce…
|
||||
Replace With Font Nahradit písmem
|
||||
Replace With Font From Nahradit písmem z
|
||||
Find In Hledat v
|
||||
System Systém
|
||||
— substituted by {style} — nahrazeno řezem {style}
|
||||
— {n} characters from another font — znaky z jiného písma: {n}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3766,6 +3766,7 @@ effect Regular Regular
|
||||
Find in Folder… Find i mappe…
|
||||
Replace With Font Erstat med skrifttype
|
||||
Replace With Font From Erstat med skrifttype fra
|
||||
Find In Søg i
|
||||
System System
|
||||
— substituted by {style} — erstattet af {style}
|
||||
— {n} characters from another font — {n} tegn fra en anden skrifttype
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2731,6 +2731,7 @@ digits Malayalam Malayalam
|
||||
Reset Transformation Transformation zurücksetzen
|
||||
Replace With Font Durch Schrift ersetzen
|
||||
Replace With Font From Ersetzende Schrift aus
|
||||
Find In Suchen in
|
||||
System System
|
||||
Replaces the saved preset of that name. Ersetzt die gespeicherte Vorgabe dieses Namens.
|
||||
Replace… Ersetzen…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3341,6 +3341,7 @@ digits Malayalam Μαλαγιάλαμ
|
||||
Reset Transformation Επαναφορά μετασχηματισμού
|
||||
Replace With Font Αντικατάσταση με γραμματοσειρά
|
||||
Replace With Font From Αντικατάσταση με γραμματοσειρά από
|
||||
Find In Αναζήτηση σε
|
||||
System Σύστημα
|
||||
Replaces the saved preset of that name. Αντικαθιστά την αποθηκευμένη προεπιλογή με αυτό το όνομα.
|
||||
Require a password to open the document Απαίτηση κωδικού για το άνοιγμα εγγράφου
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 168 and column 5.
|
@@ -2774,6 +2774,7 @@ digits Malayalam Malayálam
|
||||
Reset Transformation Restablecer transformación
|
||||
Replace With Font Reemplazar con fuente
|
||||
Replace With Font From Reemplazar con fuente de
|
||||
Find In Buscar en
|
||||
System Sistema
|
||||
Replaces the saved preset of that name. Reemplaza el preajuste guardado con ese nombre.
|
||||
Replace… Reemplazar…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3969,6 +3969,7 @@ effect Regular Normaali
|
||||
Find in Folder… Etsi kansiosta…
|
||||
Replace With Font Korvaa fontilla
|
||||
Replace With Font From Korvaavan fontin lähde
|
||||
Find In Etsi kohteesta
|
||||
System Järjestelmä
|
||||
— substituted by {style} — korvattu tyylillä {style}
|
||||
— {n} characters from another font — {n} merkkiä toisesta fontista
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2766,6 +2766,7 @@ digits Malayalam Malayalam
|
||||
Reset Transformation Réinitialiser la transformation
|
||||
Replace With Font Remplacer par la police
|
||||
Replace With Font From Remplacer par une police de
|
||||
Find In Rechercher dans
|
||||
System Système
|
||||
Replaces the saved preset of that name. Remplace le paramètre prédéfini enregistré sous ce nom.
|
||||
Replace… Remplacer…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3306,6 +3306,7 @@ mark-style Japanese Japán
|
||||
Reset Transformation Átalakítás visszaállítása
|
||||
Replace With Font Helyettesítő betűtípus
|
||||
Replace With Font From Helyettesítő betűtípus forrása
|
||||
Find In Keresés helye
|
||||
System Rendszer
|
||||
Replace “{name}”, the file this document was opened from? Felülírod ezt a fájlt, amelyből a dokumentumot megnyitottad: „{name}”?
|
||||
Replaces the saved preset of that name. Felülírja az azonos nevű mentett előbeállítást.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3766,6 +3766,7 @@ effect Regular Reguler
|
||||
Find in Folder… Temukan di folder…
|
||||
Replace With Font Ganti dengan font
|
||||
Replace With Font From Ganti dengan font dari
|
||||
Find In Cari di
|
||||
System Sistem
|
||||
— substituted by {style} — digantikan oleh {style}
|
||||
— {n} characters from another font — {n} karakter dari font lain
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2270 and column 5.
|
@@ -2807,6 +2807,7 @@ digits Malayalam Malayalam
|
||||
Reset Transformation Reimposta trasformazione
|
||||
Replace With Font Sostituisci con font
|
||||
Replace With Font From Sostituisci con font da
|
||||
Find In Cerca in
|
||||
System Sistema
|
||||
Replaces the saved preset of that name. Sostituisce il preset salvato con quel nome.
|
||||
Replace… Sostituisci…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2793,6 +2793,7 @@ mark-style Japanese 日本式
|
||||
Reset Transformation 変形をリセット
|
||||
Replace With Font 置換するフォント
|
||||
Replace With Font From 置換するフォントの一覧
|
||||
Find In 検索範囲
|
||||
System システム
|
||||
Replace it with another file, or ignore it: its images keep showing the preview saved with the document. 別のファイルに置き換えるか、無視してください。無視した場合、画像にはドキュメントに保存されたプレビューが表示され続けます。
|
||||
Replaces the saved preset of that name. 同じ名前の保存済みプリセットを置き換えます。
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3559,6 +3559,7 @@ effect Regular ჩვეულებრივი
|
||||
Find in Folder… საქაღალდეში ძებნა…
|
||||
Replace With Font ჩანაცვლება შრიფტით
|
||||
Replace With Font From ჩანაცვლების შრიფტის წყარო
|
||||
Find In ძებნის არე
|
||||
System სისტემა
|
||||
— substituted by {style} — ჩანაცვლებულია: {style}
|
||||
— {n} characters from another font — სხვა შრიფტის სიმბოლოები: {n}
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -2782,6 +2782,7 @@ mark-style Japanese 일본식
|
||||
Reset Transformation 변형 재설정
|
||||
Replace With Font 바꿀 글꼴
|
||||
Replace With Font From 바꿀 글꼴 목록
|
||||
Find In 찾을 위치
|
||||
System 시스템
|
||||
Replace it with another file, or ignore it: its images keep showing the preview saved with the document. 다른 파일로 바꾸거나 무시하세요. 무시하면 해당 이미지는 문서에 저장된 미리 보기로 계속 표시됩니다.
|
||||
Replaces the saved preset of that name. 같은 이름으로 저장된 사전 설정을 대체합니다.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3780,6 +3780,7 @@ effect Regular Regelmatig
|
||||
Find in Folder… Zoeken in map…
|
||||
Replace With Font Vervangen door lettertype
|
||||
Replace With Font From Vervangen door lettertype uit
|
||||
Find In Zoeken in
|
||||
System Systeem
|
||||
— substituted by {style} — vervangen door {style}
|
||||
— {n} characters from another font — {n} tekens van een ander lettertype
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3889,6 +3889,7 @@ effect Regular Normalny
|
||||
Find in Folder… Znajdź w folderze…
|
||||
Replace With Font Zastąp czcionką
|
||||
Replace With Font From Zastąp czcionką z
|
||||
Find In Szukaj w
|
||||
System System
|
||||
— substituted by {style} — zastąpiona przez {style}
|
||||
— {n} characters from another font — znaki z innej czcionki: {n}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2314,6 +2314,7 @@ digits Malayalam Malaiala
|
||||
Reset Transformation Redefinir Transformação
|
||||
Replace With Font Substituir Com Fonte
|
||||
Replace With Font From Substituir Com Fonte De
|
||||
Find In Localizar em
|
||||
System Sistema
|
||||
Replace it with another file, or ignore it: its images keep showing the preview saved with the document. Substitua por outro arquivo, ou ignore-o: suas imagens continuam mostrando a pré-visualização salva com o documento.
|
||||
Replaces the saved preset of that name. Substitui o predefinido salvo com esse nome.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2802,6 +2802,7 @@ digits Malayalam Малаялам
|
||||
Reset Transformation Сбросить трансформацию
|
||||
Replace With Font Заменить шрифтом
|
||||
Replace With Font From Заменить шрифтом из
|
||||
Find In Искать в
|
||||
System Система
|
||||
Replaces the saved preset of that name. Заменяет сохранённый набор с таким именем.
|
||||
Replace… Заменить…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2710,6 +2710,7 @@ digits Malayalam Malayalam
|
||||
Reset Transformation Återställ transformering
|
||||
Replace With Font Ersätt med typsnitt
|
||||
Replace With Font From Ersätt med typsnitt från
|
||||
Find In Sök i
|
||||
System System
|
||||
Replaces the saved preset of that name. Ersätter den sparade förinställningen med samma namn.
|
||||
Replace… Ersätt…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -2579,6 +2579,7 @@ digits Malayalam Malayalam
|
||||
Reset Transformation Dönüşümü Sıfırla
|
||||
Replace With Font Yazı Tipiyle Değiştir
|
||||
Replace With Font From Değiştirilecek Yazı Tipi Kaynağı
|
||||
Find In Arama yeri
|
||||
System Sistem
|
||||
Replaces the saved preset of that name. Bu adın kayıtlı ön ayarını değiştirir.
|
||||
Replace… Değiştir…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -2799,6 +2799,7 @@ digits Malayalam Малаялам
|
||||
Reset Transformation Скинути трансформацію
|
||||
Replace With Font Замінити шрифтом
|
||||
Replace With Font From Замінити шрифтом з
|
||||
Find In Шукати в
|
||||
System Система
|
||||
Replaces the saved preset of that name. Замінює збережений набір із таким ім’ям.
|
||||
Replace… Замінити…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 182 and column 5.
|
@@ -3074,6 +3074,7 @@ mark-style Japanese Kiểu Nhật
|
||||
Reset Transformation Đặt lại biến đổi
|
||||
Replace With Font Thay thế bằng phông chữ
|
||||
Replace With Font From Thay thế bằng phông chữ từ
|
||||
Find In Tìm trong
|
||||
System Hệ thống
|
||||
Replace “{name}”, the file this document was opened from? Thay thế “{name}”, tệp mà tài liệu này được mở từ đó?
|
||||
Replaces the saved preset of that name. Thay thế thiết lập sẵn đã lưu có tên đó.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 204 and column 5.
|
@@ -2677,6 +2677,7 @@ digits Malayalam 马拉雅拉姆数字
|
||||
Reset Transformation 重置变换
|
||||
Replace With Font 替换为字体
|
||||
Replace With Font From 替换字体来源
|
||||
Find In 查找范围
|
||||
System 系统
|
||||
Replaces the saved preset of that name. 替换同名已存预设。
|
||||
Replace… 替换…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3680,6 +3680,7 @@ digits Malayalam 馬來亞拉姆數字
|
||||
Reset Transformation 重設變形
|
||||
Replace With Font 取代為字體
|
||||
Replace With Font From 取代字體來源
|
||||
Find In 尋找範圍
|
||||
System 系統
|
||||
Replaces the saved preset of that name. 取代同名的已儲存預設集。
|
||||
Replace… 取代…
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -370,6 +370,9 @@ pub struct UiState {
|
||||
/// That popup's first text field is still to take the keyboard.
|
||||
#[serde(skip)]
|
||||
pub transient_focus: bool,
|
||||
/// Type › Find Font…'s last Find In choice (`find_font::FIND_IN`), kept while the app runs.
|
||||
#[serde(skip)]
|
||||
pub find_font_in: String,
|
||||
pub control_bar: bool,
|
||||
/// The Control bar is docked to the bottom of the window instead of the top
|
||||
/// (`window.control.dock`).
|
||||
@@ -974,6 +977,7 @@ impl Default for UiState {
|
||||
open_panel: None,
|
||||
transient_panel: None,
|
||||
transient_focus: false,
|
||||
find_font_in: String::new(),
|
||||
control_bar: false,
|
||||
control_bar_bottom: false,
|
||||
toolbar: true,
|
||||
|
||||
@@ -42,6 +42,7 @@ M7 type: Touch Type moves a right-to-left character along its line and up from i
|
||||
| 2026-11-05 | minor | Selection Tools add-on: Select Linked, Embedded, Missing and Modified select the images and placed documents with that link status (Object › Plug-ins › Selection Tools and the Links panel menu; Shift adds), and Select by Link Status… lists them by name, status and format to pick from, optionally zooming to them; what is hidden, locked, on a template layer or outside the isolated group is left out and the status bar says how many (#5526, a VectorCraft addition); scores and estimates unchanged (estimated). |
|
||||
| 2026-11-05 | minor | Pencil and Paintbrush: a new stroke with Keep selected off leaves nothing selected, the earlier selection included, while Keep selected on selects only the new path (#5752); scores and estimates unchanged. |
|
||||
| 2026-11-05 | minor | Layers panel: a layer whose Layer Options › Print is off shows its name italic, as the reference application shows non-printing layers, and keeps it while the row is dragged (#5739); scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | M7: Select Text by Font takes a scope: the document, chosen artboards or chosen layers; Find Font's Find In offers Document, Artboard (the active one) and Layers (the selected ones, else the current one) (#5529, a VectorCraft addition); estimates unchanged. |
|
||||
| 2026-11-04 | minor | M7: Find Font's Replace With Font lists family and style rows with a search, and the list and the document's font list scroll on their own (#4972); scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | Selection Tools add-on: Select by Artboards selects the objects overlapping, fully inside or fully outside several chosen artboards, from a dialog that lists, searches and multi-selects the artboards and fits one in the window on double-click (#5525, #5548, #5549, a VectorCraft addition); scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | M4: raster/PSD exports default to document resolution; CLI run/convert expose --ppi, matching the engine and MCP precedence. Scores and hours unchanged. |
|
||||
|
||||
File diff suppressed because one or more lines are too long
+2
-1
@@ -34,7 +34,7 @@ against Illustrator's public documentation; **estimated** unless noted.
|
||||
| Character size and horizontal scale after object/group scaling | done | #1034: fields and setters compensate the stored affine; point size follows the transformed em-height axis, not the glyph ink bounds. Range edits, keyboard increments, leading and baseline shift use document points; rotation, reflection and shear remain editable. |
|
||||
| Preserve font sizes while scaling a group | missing | Separate enhancement; scaling still scales text with the other artwork. |
|
||||
| OpenType panel, Glyphs panel, alternates | done | Highlight Alternate Glyphs preference (#394, off by default) paints pink behind OpenType substitutions on screen when on (required shaping such as Arabic joining forms and the lam-alef ligature, including a lam-alef older fonts file under `liga`, is not highlighted; an optional feature that changes the ligature is); stylistic sets ss01–ss20, stylistic and titling alternates, proportional and lining figures, superscript and subscript are character-style features (`text.setStyle {features}`, #1607); the OpenType panel's Position menu sets superscript, subscript, numerator or denominator glyphs (`sups`, `subs`, `numr`, `dnom`). The OpenType panel's Stylistic Sets menu lists the selected font's `ss01`–`ss20` features, using a font-supplied name where available and `Set N` otherwise; several sets can be active, and selecting an active one removes it. For a mixed-font selection it uses the first selected character's font, a VectorCraft design choice ([public help](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/add-stylistic-sets-to-selected-text.html), [OpenType feature specification](https://learn.microsoft.com/en-us/typography/opentype/spec/features_pt)). The Glyphs panel highlights the character selected with the Type tool (a range's first character, else the one before the caret) and scrolls it into view as the selection moves (#4908; `panels::glyphs` tests); its Show menu lists Unicode ranges and Alternates for Current Selection, the selected character's alternate glyphs from the font's `GSUB` table, one of which a double-click draws for that character (`text.setGlyphAlternate`); the glyph-cell pop-up of alternates is not there yet |
|
||||
| Font menu: samples, live preview, filters, favourites, recent families; font matching by any name; Find Font | done | #3515: recently applied families appear first, once each; missing families are omitted and the existing Number of Recent Fonts setting applies; #339; script filter: Japanese, Latin, Thai; the font scan on Linux also reads the folders fontconfig's configuration lists, such as TeX fonts under `/usr/share/texmf/fonts` (#1548, fixes #1529; not verified on Linux) Replace With Font lists family and style rows, one click setting both, with a search over them; its list scrolls on its own, apart from the document's font list (#4972). |
|
||||
| Font menu: samples, live preview, filters, favourites, recent families; font matching by any name; Find Font | done | #3515: recently applied families appear first, once each; missing families are omitted and the existing Number of Recent Fonts setting applies; #339; script filter: Japanese, Latin, Thai; the font scan on Linux also reads the folders fontconfig's configuration lists, such as TeX fonts under `/usr/share/texmf/fonts` (#1548, fixes #1529; not verified on Linux); Select Text by Font takes a scope: the document, chosen artboards or chosen layers; Find Font's Find In offers Document, Artboard (the active one) and Layers (the selected ones, else the current one) (#5529, a VectorCraft addition); Replace With Font lists family and style rows, one click setting both, with a search over them; its list scrolls on its own, apart from the document's font list (#4972). |
|
||||
| Missing glyphs set in an installed font made for their script (Thai, Lao, Khmer, Myanmar first; then any font that has them) | done | Thai: Leelawadee UI, Tahoma, Thonburi, Sukhumvit Set, Noto Sans Thai, TLWG |
|
||||
| Full font-name matching (OpenType name ID 4) | done | #1258: loaded/cataloged faces and Missing Fonts searches; localized names and variable default faces; Windows font-service QA pending |
|
||||
| Variable fonts | done | named instances as styles; Character panel › Variable Font sets each axis (`fontAxes`); the font menu's Font Technology › Variable lists only variable font families, with the script, kind and ★ filters ([Find, apply, and adjust variable fonts](https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/find-apply-and-adjust-variable-fonts.html)); a family with a variable file beside static ones counts as variable. Evidence: `font_menu::tests::the_variable_filter_lists_only_variable_families`, `tests_axes::a_family_with_variation_axes_is_variable`, `tests_sysfonts::a_family_with_a_variable_file_beside_static_ones_is_variable` |
|
||||
@@ -87,6 +87,7 @@ Character Styles menu New/Redefine and footer New capture the first selected cha
|
||||
| 2026-11-06 | minor | Snap to Glyph › Glyph Bounds: each glyph's box gives four corners a drawn point lands on, also on a curved side (`B`), and its box and edges turn with rotated type (#5112); scores and estimates unchanged |
|
||||
| 2026-11-05 | minor | Character panel › Show Snap to Glyph Options scrolls the panel down to the Snap to Glyph section, which comes after every character attribute and could sit out of sight, and the section starts with a Snap to Glyph checkbox (View › Snap to Glyph) so its dimmed options can be turned on there (#5273); scores and estimates unchanged |
|
||||
| 2026-11-05 | minor | Release Type on a Path joins the Type Tools add-on: type on a path splits into point type and its path (#5532, beyond Illustrator). Scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | Select Text by Font takes a scope: the document, chosen artboards or chosen layers; Find Font's Find In offers Document, Artboard (the active one) and Layers (the selected ones, else the current one) (#5529, a VectorCraft addition); scores and estimates unchanged. |
|
||||
| 2026-11-04 | minor | Typographer's Quotes: a lone typed `”` (U+201D) is set like a straight `"` where a quote opens (at the start, after a space), taking the document's opening quote, and stays `”` where one closes: the macOS Serbian Cyrillic layout has no straight quote and types `”` on Shift+2, which now gives „…” (#5247). `“ „` and the single quotes `‘ ’ ‚` stay as typed (that layout's apostrophe key types `’`, and `’90s` keeps its apostrophe); pasted text keeps its quotes; with the option off `”` stays as typed. Evidence: `docsetup::a_typed_closing_double_quote_opens_where_a_quote_opens`; scores unchanged |
|
||||
| 2026-11-04 | minor | A Type-range Copy or Cut publishes styled art alongside plain text, so a later canvas Paste keeps its character formatting after editing ends (#5636). Scores and estimates unchanged. |
|
||||
| 2026-11-03 | minor | Glyphs panel adds Alternates for Current Selection. A chosen alternate changes one character's drawn glyph while keeping its Unicode text; character-style capture does not copy that per-character choice. Scores and estimates unchanged. |
|
||||
|
||||
Reference in New Issue
Block a user