mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 02:07:09 -06:00
File > Document Setup is a tabbed dialog over one engine command, document.setup (no params: a query; any keys: one undo step; document.setUnits stays as the units alias). - General: units, bleed (four sides with a link toggle), Edit Artboards, Show Images in Outline Mode (greyscale pixels under the frame), Highlight Substituted Fonts/Glyphs (pink boxes on screen only). - Transparency: grid size and colour presets or custom colours, Simulate Colored Paper, the flattener preset (built-in or saved) and Discard White Overprint (Overprint Preview keeps white overprints visible). - Type: language with its quotes, Use Typographer's Quotes for the Type tool and Smart Punctuation, superscript/subscript size and position and small caps size (Character panel position/smallCaps; changing the setup restyles the text in the same undo step), text export (SVG text as text or outlines by default). - The canvas draws a red bleed outline and the grid in the document's colours and cell size as one textured quad, also in rotated views; a white Background Contents exports raster formats opaque. Model: Document.setup (DocSetup, defaulted, skipped when default) and CharStyle position/small_caps; old files load unchanged. tests_transparencygrid's helper now finds the grid by its texture (the grid is one textured mesh instead of per-cell rectangles). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
2.4 KiB
Rust
51 lines
2.4 KiB
Rust
//! Superscript, subscript and synthesized small caps (Character panel, Document Setup → Type).
|
|
|
|
use kurbo::Shape;
|
|
use vectorcraft_doc::{CharPosition, CharStyle, ScriptMetrics, TextObject};
|
|
|
|
use super::*;
|
|
|
|
fn layout_of(text: &str, st: CharStyle) -> TextLayout {
|
|
layout(FontDb::global(), &TextObject::point(kurbo::Point::ZERO, text, st))
|
|
}
|
|
|
|
fn glyph_box(l: &TextLayout, i: usize) -> kurbo::Rect {
|
|
l.glyphs[i].outline.bounding_box()
|
|
}
|
|
|
|
#[test]
|
|
fn superscript_and_subscript_scale_and_shift_glyphs() {
|
|
let base = CharStyle { size: 20.0, ..CharStyle::default() };
|
|
let normal = layout_of("H", base.clone());
|
|
let m = ScriptMetrics { size: 50.0, position: 40.0 };
|
|
let sup = layout_of("H", CharStyle { position: CharPosition::Superscript(m), ..base.clone() });
|
|
let sub = layout_of("H", CharStyle { position: CharPosition::Subscript(m), ..base.clone() });
|
|
let (n, p, b) = (glyph_box(&normal, 0), glyph_box(&sup, 0), glyph_box(&sub, 0));
|
|
// Half the height and advance.
|
|
assert!((p.height() - n.height() * 0.5).abs() < 0.05, "{p:?} vs {n:?}");
|
|
assert!((sup.glyphs[0].advance - normal.glyphs[0].advance * 0.5).abs() < 1e-6);
|
|
// Raised / lowered by 40% of 20 pt (y points down).
|
|
assert!((p.y1 - (n.y1 - 8.0)).abs() < 0.05, "{p:?}");
|
|
assert!((b.y1 - (n.y1 + 8.0)).abs() < 0.05, "{b:?}");
|
|
// Line metrics keep the full size.
|
|
assert_eq!(sup.lines[0].ascent, normal.lines[0].ascent);
|
|
}
|
|
|
|
#[test]
|
|
fn small_caps_draw_lowercase_as_smaller_capitals() {
|
|
let base = CharStyle { size: 20.0, ..CharStyle::default() };
|
|
let cap = layout_of("A", base.clone());
|
|
let cap14 = layout_of("A", CharStyle { size: 14.0, ..base.clone() });
|
|
let small = layout_of("Aa", CharStyle { small_caps: Some(70.0), ..base.clone() });
|
|
// The capital is unchanged; the lowercase letter is a capital A at 70% (14 pt).
|
|
assert_eq!(small.glyphs[0].advance, cap.glyphs[0].advance);
|
|
assert!((small.glyphs[1].advance - cap14.glyphs[0].advance).abs() < 1e-6);
|
|
let (a, s) = (glyph_box(&cap14, 0), glyph_box(&small, 1));
|
|
assert!((s.height() - a.height()).abs() < 0.05, "{s:?} vs {a:?}");
|
|
// Byte offsets still map back to the source characters.
|
|
assert_eq!((small.glyphs[1].byte, small.glyphs[1].len), (1, 1));
|
|
// All Caps wins over Small Caps.
|
|
let all = layout_of("a", CharStyle { all_caps: true, small_caps: Some(70.0), ..base });
|
|
assert_eq!(all.glyphs[0].advance, cap.glyphs[0].advance);
|
|
}
|