mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 21:44:57 +00:00
Merge pull request #7587 from dotCooCoo/fix/open-dimension-labels-own-fonts
Dimensions: an opened file redraws its labels in its own composite fonts
This commit is contained in:
@@ -478,6 +478,75 @@ fn an_opened_file_measures_its_type_in_its_own_composite_fonts() {
|
|||||||
assert_eq!(actual.cached_bounds, Some(expected));
|
assert_eq!(actual.cached_bounds, Some(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The label of dimension `id` in `doc`.
|
||||||
|
fn label(doc: &vectorcraft_doc::Document, id: u64) -> vectorcraft_doc::TextObject {
|
||||||
|
let node = doc.node(NodeId(id)).unwrap();
|
||||||
|
node.children().unwrap().iter().find_map(|n| if let NodeKind::Text(t) = &n.kind { Some((**t).clone()) } else { None }).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The center of label `t`'s measured box on the page.
|
||||||
|
fn label_center(t: &vectorcraft_doc::TextObject) -> vectorcraft_geom::Point {
|
||||||
|
t.xf * t.cached_bounds.unwrap().center()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether every character of label `t` is filled in CMYK.
|
||||||
|
fn label_in_cmyk(t: &vectorcraft_doc::TextObject) -> bool {
|
||||||
|
!t.runs.is_empty() && t.runs.iter().all(|r| matches!(r.style.fill.color(), Some(vectorcraft_color::Color::Cmyk { .. })))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The native file of a CMYK document whose composite font `name` sets Roman at full size, with a
|
||||||
|
/// dimension whose label is set in it and whose label's characters are saved in RGB, so opening
|
||||||
|
/// the file draws the dimension again → (the file, the dimension's id, the center of its label as
|
||||||
|
/// drawn in the document's own composite font).
|
||||||
|
fn dimension_file(name: &str) -> (Vec<u8>, u64, vectorcraft_geom::Point) {
|
||||||
|
let mut s = Session::new();
|
||||||
|
s.execute("file.new", &json!({"width": 400, "height": 400, "colorMode": "cmyk"})).unwrap();
|
||||||
|
let mut full = font(name);
|
||||||
|
full["parts"]["roman"]["size"] = json!(100);
|
||||||
|
s.execute("type.compositeFonts.set", &json!({"fonts": [full]})).unwrap();
|
||||||
|
let line = s.execute("shape.line", &json!({"x1": 100, "y1": 100, "x2": 172, "y2": 100})).unwrap()["id"].as_u64().unwrap();
|
||||||
|
let id = s
|
||||||
|
.execute(
|
||||||
|
"dimension.create",
|
||||||
|
&json!({"source": {"kind": "segment", "edge": {"node": line, "subpath": 0, "segment": 0}},
|
||||||
|
"options": {"units": "in", "precision": 2, "fontFamily": name, "fontSize": 20}}),
|
||||||
|
)
|
||||||
|
.unwrap()["id"]
|
||||||
|
.as_u64()
|
||||||
|
.unwrap();
|
||||||
|
let mut doc = (*s.doc().unwrap().doc).clone();
|
||||||
|
let drawn = label(&doc, id);
|
||||||
|
assert!(label_in_cmyk(&drawn));
|
||||||
|
let center = label_center(&drawn);
|
||||||
|
let mut label_id = None;
|
||||||
|
doc.node(NodeId(id)).unwrap().walk(&mut |n| {
|
||||||
|
if matches!(n.kind, NodeKind::Text(_)) {
|
||||||
|
label_id = Some(n.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(NodeKind::Text(t)) = doc.node_mut(label_id.unwrap()).map(|n| &mut n.kind) {
|
||||||
|
for r in &mut t.runs {
|
||||||
|
r.style.fill = vectorcraft_color::Paint::solid(vectorcraft_color::Color::rgb8(0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(vectorcraft_format::save_file(&doc), id, center)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A file whose dimensions are drawn again when it opens (their art is not in the document's color
|
||||||
|
/// mode) draws their labels in its own composite fonts, not in those of the front document, which
|
||||||
|
/// defines the label's font name with Roman at half size: each label stays centered on its anchor.
|
||||||
|
#[test]
|
||||||
|
fn an_opened_file_draws_its_dimension_labels_in_its_own_composite_fonts() {
|
||||||
|
let (bytes, id, center) = dimension_file("CF Dimension");
|
||||||
|
let mut s = session();
|
||||||
|
s.execute("type.compositeFonts.set", &json!({"fonts": [font("CF Dimension")]})).unwrap();
|
||||||
|
let file = crate::cmd::fileio::read_for_open("dimension.vectorcraft", &bytes, None, &json!({}), false, false).unwrap();
|
||||||
|
crate::cmd::fileio::open_read(&mut s, file).unwrap();
|
||||||
|
let opened = label(&s.doc().unwrap().doc, id);
|
||||||
|
assert!(label_in_cmyk(&opened));
|
||||||
|
assert!((label_center(&opened) - center).hypot() < 1e-6, "{:?} vs {center:?}", label_center(&opened));
|
||||||
|
}
|
||||||
|
|
||||||
/// Threaded type read from a file flows through its frames in the file's own composite fonts, when
|
/// Threaded type read from a file flows through its frames in the file's own composite fonts, when
|
||||||
/// no open document defines them.
|
/// no open document defines them.
|
||||||
#[test]
|
#[test]
|
||||||
@@ -536,6 +605,40 @@ fn a_document_replaced_in_the_back_is_measured_when_the_front_one_closes() {
|
|||||||
assert_eq!(actual.cached_bounds, Some(expected));
|
assert_eq!(actual.cached_bounds, Some(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A document replaced in front (File › Revert) whose dimensions are drawn again draws their labels
|
||||||
|
/// in its new composite fonts, not in those it had before.
|
||||||
|
#[test]
|
||||||
|
fn a_document_replaced_in_front_draws_its_dimension_labels_in_its_own_composite_fonts() {
|
||||||
|
let (bytes, id, center) = dimension_file("CF Reverted");
|
||||||
|
let mut s = session();
|
||||||
|
s.execute("type.compositeFonts.set", &json!({"fonts": [font("CF Reverted")]})).unwrap();
|
||||||
|
assert!(s.replace_document(0, vectorcraft_format::load(&bytes).unwrap()));
|
||||||
|
let replaced = label(&s.doc().unwrap().doc, id);
|
||||||
|
assert!(label_in_cmyk(&replaced));
|
||||||
|
assert!((label_center(&replaced) - center).hypot() < 1e-6, "{:?} vs {center:?}", label_center(&replaced));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A document replaced in the back draws its dimension labels in its own composite fonts, while the
|
||||||
|
/// front document keeps its definition of the same name registered; the labels stay where they are
|
||||||
|
/// when the document comes to the front.
|
||||||
|
#[test]
|
||||||
|
fn a_document_replaced_in_the_back_draws_its_dimension_labels_in_its_own_composite_fonts() {
|
||||||
|
let (bytes, id, center) = dimension_file("CF Back Dimension");
|
||||||
|
let mut s = session();
|
||||||
|
s.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
|
||||||
|
s.execute("type.compositeFonts.set", &json!({"fonts": [font("CF Back Dimension")]})).unwrap();
|
||||||
|
assert!(s.replace_document(0, vectorcraft_format::load(&bytes).unwrap()));
|
||||||
|
assert_eq!(s.active_index(), Some(1));
|
||||||
|
let front = vectorcraft_text::FontDb::global().composite_font("CF Back Dimension").unwrap();
|
||||||
|
assert_eq!(front.parts.get(vectorcraft_doc::CharClass::Roman).size, 50.0);
|
||||||
|
let replaced = label(&s.documents()[0].doc, id);
|
||||||
|
assert!(label_in_cmyk(&replaced));
|
||||||
|
assert!((label_center(&replaced) - center).hypot() < 1e-6, "{:?} vs {center:?}", label_center(&replaced));
|
||||||
|
assert!(s.set_active(0));
|
||||||
|
let shown = label(&s.doc().unwrap().doc, id);
|
||||||
|
assert!((label_center(&shown) - center).hypot() < 1e-6, "{:?} vs {center:?}", label_center(&shown));
|
||||||
|
}
|
||||||
|
|
||||||
/// The type of document `index` with id `id`, wherever its tab is.
|
/// The type of document `index` with id `id`, wherever its tab is.
|
||||||
fn text_in(s: &Session, index: usize, id: u64) -> vectorcraft_doc::TextObject {
|
fn text_in(s: &Session, index: usize, id: u64) -> vectorcraft_doc::TextObject {
|
||||||
match &s.documents()[index].doc.node(NodeId(id)).unwrap().kind {
|
match &s.documents()[index].doc.node(NodeId(id)).unwrap().kind {
|
||||||
|
|||||||
@@ -495,7 +495,8 @@ pub(crate) fn refresh(before: &Document, doc: &mut Document) -> Result<bool> {
|
|||||||
|
|
||||||
/// Redraw the dimensions of an opened document whose art isn't in its colour mode: files saved
|
/// Redraw the dimensions of an opened document whose art isn't in its colour mode: files saved
|
||||||
/// before dimensions followed the colour mode have RGB dimensions in CMYK documents. A dimension
|
/// before dimensions followed the colour mode have RGB dimensions in CMYK documents. A dimension
|
||||||
/// that can't be redrawn keeps the art it has.
|
/// that can't be redrawn keeps the art it has. The labels are measured in the document's own
|
||||||
|
/// composite fonts, also when another document's definitions of the same names are registered.
|
||||||
pub(crate) fn conform(doc: &mut Document) {
|
pub(crate) fn conform(doc: &mut Document) {
|
||||||
if !doc.dimensions {
|
if !doc.dimensions {
|
||||||
return;
|
return;
|
||||||
@@ -521,6 +522,10 @@ pub(crate) fn conform(doc: &mut Document) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if stale.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _lent = vectorcraft_text::FontDb::global().lend_composite_fonts(&doc.composite_fonts);
|
||||||
for (id, d) in stale {
|
for (id, d) in stale {
|
||||||
// An error leaves this dimension as it was; the others are still redrawn.
|
// An error leaves this dimension as it was; the others are still redrawn.
|
||||||
let _ = replace(doc, id, d);
|
let _ = replace(doc, id, d);
|
||||||
|
|||||||
@@ -724,6 +724,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
|||||||
- **Reordering document tabs (G7, #5663):** a document tab dragged along the tab strip moves its document among the others ([public source](https://helpx.adobe.com/illustrator/using/customizing-workspace.html)); a line marks the gap it goes in. The current document stays current, each document keeps its own zoom, centre and artboard, the views Window › New Window opened move with their document, and Window › Next/Previous Document follow the new order. `document.move` (`{from?, to}`) does the same from MCP, the control channel and recordings. `crates/ui-egui/src/tests/tab_reorder.rs`, `crates/engine/src/tests/document_move.rs`.
|
- **Reordering document tabs (G7, #5663):** a document tab dragged along the tab strip moves its document among the others ([public source](https://helpx.adobe.com/illustrator/using/customizing-workspace.html)); a line marks the gap it goes in. The current document stays current, each document keeps its own zoom, centre and artboard, the views Window › New Window opened move with their document, and Window › Next/Previous Document follow the new order. `document.move` (`{from?, to}`) does the same from MCP, the control channel and recordings. `crates/ui-egui/src/tests/tab_reorder.rs`, `crates/engine/src/tests/document_move.rs`.
|
||||||
- **Closing a background document tab:** closing a tab other than the current one (`file.close {index}`) keeps the current document current, and its tool keeps its state; closing the current tab still makes its neighbor current. `crates/engine/src/tests/document_close.rs`.
|
- **Closing a background document tab:** closing a tab other than the current one (`file.close {index}`) keeps the current document current, and its tool keeps its state; closing the current tab still makes its neighbor current. `crates/engine/src/tests/document_close.rs`.
|
||||||
- **Replacing a background document's content:** `Session::replace_document` on a tab other than the current one (the replacement File › Revert makes for the current tab) measures the new content's type in that document's own composite fonts at once, so that type has its measured bounds while the document is in the back. When that document becomes the current one unchanged, its type in those fonts is not measured again. `crates/engine/src/cmd/compositefonts/tests.rs`.
|
- **Replacing a background document's content:** `Session::replace_document` on a tab other than the current one (the replacement File › Revert makes for the current tab) measures the new content's type in that document's own composite fonts at once, so that type has its measured bounds while the document is in the back. When that document becomes the current one unchanged, its type in those fonts is not measured again. `crates/engine/src/cmd/compositefonts/tests.rs`.
|
||||||
|
- **Dimension labels in an opened document's composite fonts:** when a document opens or is replaced (File › Revert) with dimensions that are drawn again because their art is not in the document's color mode, each label is measured and centered on its anchor in the document's own composite fonts, also when another open document defines the label's font name differently. `crates/engine/src/cmd/compositefonts/tests.rs` (`an_opened_file_draws_its_dimension_labels_in_its_own_composite_fonts`, `a_document_replaced_in_front_draws_its_dimension_labels_in_its_own_composite_fonts`, `a_document_replaced_in_the_back_draws_its_dimension_labels_in_its_own_composite_fonts`).
|
||||||
- **Export and Import Preferences (#6435):** an optional VectorCraft feature. The Preferences dialog's Export Preferences… saves the applied preferences to a JSON file (`prefs.export`; the web build downloads it), and Import Preferences… applies such a file (`prefs.import`). The file leaves out the folder preferences. The import checks every value first and changes nothing on an error; it keeps the open document's units and records no undo step. The file format is VectorCraft's own. Tests: `crates/engine/src/tests/prefs_file.rs`, `crates/ui-egui/src/prefs_dialog/tests.rs`.
|
- **Export and Import Preferences (#6435):** an optional VectorCraft feature. The Preferences dialog's Export Preferences… saves the applied preferences to a JSON file (`prefs.export`; the web build downloads it), and Import Preferences… applies such a file (`prefs.import`). The file leaves out the folder preferences. The import checks every value first and changes nothing on an error; it keeps the open document's units and records no undo step. The file format is VectorCraft's own. Tests: `crates/engine/src/tests/prefs_file.rs`, `crates/ui-egui/src/prefs_dialog/tests.rs`.
|
||||||
- **Preferences › User Interface › Open Documents As Tabs (G7, #394):** checked by default, documents open under tabs in one document window ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/workspace-overview.html)). With the box cleared, each document opened or created afterwards opens in a floating window of its own, at the next cascade place, as Float in Window floats it; documents already open stay where they are, and checking the box again leaves floating documents in their windows. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
|
- **Preferences › User Interface › Open Documents As Tabs (G7, #394):** checked by default, documents open under tabs in one document window ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/workspace-overview.html)). With the box cleared, each document opened or created afterwards opens in a floating window of its own, at the next cascade place, as Float in Window floats it; documents already open stay where they are, and checking the box again leaves floating documents in their windows. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
|
||||||
- **Preferences search:** a Search Preferences field above the category list has the keyboard focus when the dialog opens, and Cmd/Ctrl+F gives it back; typing lists the settings it finds, and choosing one shows its category, as the [help page](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/set-app-preferences.html) describes. VectorCraft's own rules: the field matches a setting's label, section or category, ignoring case, in the interface language or in English; the list replaces the categories while the field has text and shows every match, or "No settings found"; the chosen setting's row is highlighted and scrolled into view; Escape in the field with text clears it, and Escape in the empty field closes the dialog. The dialog's `__search` field (`ui.dialog.set`) holds the text. Tests: `crates/ui-egui/src/prefs_dialog/tests.rs`.
|
- **Preferences search:** a Search Preferences field above the category list has the keyboard focus when the dialog opens, and Cmd/Ctrl+F gives it back; typing lists the settings it finds, and choosing one shows its category, as the [help page](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/set-app-preferences.html) describes. VectorCraft's own rules: the field matches a setting's label, section or category, ignoring case, in the interface language or in English; the list replaces the categories while the field has text and shows every match, or "No settings found"; the chosen setting's row is highlighted and scrolled into view; Escape in the field with text clears it, and Escape in the empty field closes the dialog. The dialog's `__search` field (`ui.dialog.set`) holds the text. Tests: `crates/ui-egui/src/prefs_dialog/tests.rs`.
|
||||||
|
|||||||
Reference in New Issue
Block a user