Files
vectorcraft/crates/text/src/tests_scripts.rs
T
bflatasticandClaude Opus 5.5 d527349f5a M4.20: Document Setup with bleed, transparency grid, paper and type options
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>
2026-10-05 08:28:16 +07:00

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);
}