mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 20:08:42 +00:00
Merge pull request #6842 from dotCooCoo/fix/legacy-ai-vertical-roman-tcy
Legacy .ai: type keeps Standard Vertical Roman Alignment and Tate-chu-yoko
This commit is contained in:
@@ -33,7 +33,10 @@
|
||||
//! - Type (§15): point type, area type and type on a path as text objects, `type To`, `a b c d tx
|
||||
//! ty startPt Tp` (the text matrix; the frame or path in art space between it and `TP`), each
|
||||
//! run's paint style, `Tr` (fill, stroke, both or neither), `/_font size ascent descent Tf`,
|
||||
//! `Ts Tz Tt TA Tl Ta Ti` and its characters `(…) Tx` (`\r` ends a paragraph), `TO`. Only the
|
||||
//! `Ts Tz Tt TA Tl Ta Ti` and its characters `(…) Tx` (`\r` ends a paragraph), `TO`. Standard
|
||||
//! Vertical Roman Alignment is written as `1 Tv` and `0 Tv`, and Tate-chu-yoko in horizontal
|
||||
//! type as a Kumi block recorded for later use, `1 TY` … `0 TY` (§15.3.4); neither operator is
|
||||
//! written for type that has neither mark. Only the
|
||||
//! revisable form is written: readers lay the type out again (§15.1). Fonts are named by their
|
||||
//! PostScript names in `%%DocumentFonts` and `%%IncludeFont` (§3.1) and re-encoded to
|
||||
//! Windows-1252 (`TE`, then `%AI3_BeginEncoding: _name name`, `TZ`, §3.3), the 8-bit encoding
|
||||
|
||||
@@ -106,7 +106,7 @@ VCLegacy begin
|
||||
/TE {cleartomark} bd /TZ {cleartomark} bd
|
||||
/To {pop} bd /Tp {7 {pop} repeat} bd /TP {} bd /TO {} bd
|
||||
/Tr {pop} bd /Tf {pop pop pop pop} bd /Ts {pop} bd /Tz {pop pop} bd /Tt {pop} bd /TA {pop} bd
|
||||
/Tl {pop pop} bd /Ta {pop} bd /Ti {pop pop pop} bd /Tx {pop} bd
|
||||
/Tl {pop pop} bd /Ta {pop} bd /Ti {pop pop pop} bd /Tx {pop} bd /Tv {pop} bd /TY {pop} bd
|
||||
% The page trailer (3): annotatepage marks nothing.
|
||||
/annotatepage {} bd
|
||||
end
|
||||
|
||||
@@ -1193,6 +1193,59 @@ fn variable_font_settings_are_warned_as_left_out() {
|
||||
assert!(String::from_utf8(out.bytes).unwrap().contains("(H\\351llo \\2005\\r) Tx\n"), "still written as text");
|
||||
}
|
||||
|
||||
/// Standard Vertical Roman Alignment is written as `Tv` and Tate-chu-yoko in horizontal type as a
|
||||
/// Kumi block recorded for later use, `TY` (§15.3.4), each where it changes: the marks read back
|
||||
/// on the same characters, a block is closed before its text object ends, and type with neither
|
||||
/// mark has neither operator.
|
||||
#[test]
|
||||
fn vertical_roman_and_tate_chu_yoko_are_written_and_read_back() {
|
||||
use vectorcraft_doc::{TextObject, TextRun};
|
||||
let plain = style(12.0, Paint::solid(Color::BLACK));
|
||||
let roman = vectorcraft_doc::CharStyle { standard_vertical_roman: true, ..plain.clone() };
|
||||
let tcy = vectorcraft_doc::CharStyle { tate_chu_yoko: true, ..plain.clone() };
|
||||
let both = vectorcraft_doc::CharStyle { tate_chu_yoko: true, standard_vertical_roman: true, ..plain.clone() };
|
||||
let mut doc = Document::new(300.0, 200.0);
|
||||
let layer = doc.layers[0].id;
|
||||
let mut marked = TextObject::point(vectorcraft_geom::Point::new(20.0, 40.0), "A", plain.clone());
|
||||
for (text, st) in [("12", &tcy), ("b", &roman), ("c", &both), ("34", &tcy)] {
|
||||
marked.runs.push(TextRun::new(text, st.clone()));
|
||||
}
|
||||
let next = TextObject::point(vectorcraft_geom::Point::new(20.0, 80.0), "D", plain.clone());
|
||||
for t in [marked, next] {
|
||||
let n = Node::new(doc.alloc_id(), NodeKind::Text(Box::new(t)));
|
||||
doc.insert(Some(layer), usize::MAX, n).unwrap();
|
||||
}
|
||||
let out = write(&doc);
|
||||
assert!(!out.warnings.iter().any(|w| w == TYPE || w == TYPE_ATTRIBUTES), "{:?}", out.warnings);
|
||||
let text = String::from_utf8(out.bytes.clone()).unwrap();
|
||||
assert_eq!((text.matches("1 TY\n").count(), text.matches("0 TY\n").count()), (2, 2), "{text}");
|
||||
assert_eq!((text.matches("1 Tv\n").count(), text.matches("0 Tv\n").count()), (1, 1), "{text}");
|
||||
// The last block ends before its text object does.
|
||||
assert!(text.contains("(34) Tx\n0 TY\nTO\n"), "{text}");
|
||||
let read = texts(&crate::import(&out.bytes).unwrap().document);
|
||||
let flags = |t: &TextObject| -> Vec<(String, bool, bool)> {
|
||||
let mut out: Vec<(String, bool, bool)> = vec![];
|
||||
for r in &t.runs {
|
||||
let key = (r.style.tate_chu_yoko, r.style.standard_vertical_roman);
|
||||
match out.last_mut() {
|
||||
Some(last) if (last.1, last.2) == key => last.0.push_str(&r.text),
|
||||
_ => out.push((r.text.clone(), key.0, key.1)),
|
||||
}
|
||||
}
|
||||
out
|
||||
};
|
||||
let s = |x: &str| x.to_string();
|
||||
assert_eq!(
|
||||
flags(&read[0].1),
|
||||
[(s("A"), false, false), (s("12"), true, false), (s("b"), false, true), (s("c"), true, true), (s("34"), true, false)]
|
||||
);
|
||||
assert_eq!(flags(&read[1].1), [(s("D"), false, false)]);
|
||||
|
||||
// Type with neither mark has neither operator.
|
||||
let text = String::from_utf8(write(&typed()).bytes).unwrap();
|
||||
assert!(!text.contains(" Tv\n") && !text.contains(" TY\n"), "{text}");
|
||||
}
|
||||
|
||||
/// A legacy `.ai` text path holds the path and where the type starts on it, and no Align to Path
|
||||
/// or end bracket: type on a path with either is written as text, with the warning that they are
|
||||
/// left out. An end bracket at the end of an open path, or once around a closed one, is kept. A
|
||||
|
||||
@@ -39,6 +39,10 @@ pub(super) struct Writer<'a> {
|
||||
in_hidden: bool,
|
||||
/// The fonts type is set in: PostScript name, and whether it's TrueType.
|
||||
pub(super) fonts: Vec<(String, bool)>,
|
||||
/// The Standard Vertical Roman Alignment the last `Tv` set (it lasts into later text objects).
|
||||
roman: bool,
|
||||
/// Inside a Kumi block `1 TY` began (Tate-chu-yoko in horizontal type).
|
||||
kumi: bool,
|
||||
}
|
||||
|
||||
impl<'a> Writer<'a> {
|
||||
@@ -59,6 +63,8 @@ impl<'a> Writer<'a> {
|
||||
hidden_art: vec![],
|
||||
in_hidden: false,
|
||||
fonts: vec![],
|
||||
roman: false,
|
||||
kumi: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,6 +825,10 @@ impl<'a> Writer<'a> {
|
||||
self.line(" Tx");
|
||||
}
|
||||
}
|
||||
if self.kumi {
|
||||
self.line("0 TY");
|
||||
self.kumi = false;
|
||||
}
|
||||
self.line("TO");
|
||||
}
|
||||
|
||||
@@ -838,7 +848,8 @@ impl<'a> Writer<'a> {
|
||||
}
|
||||
|
||||
/// A run's character attributes and paint style: `Tr`, its fill and stroke, `Tf`, `Ts`, `Tz`,
|
||||
/// `Tt`, `TA`, and the leading with the space before its paragraph, `Tl`.
|
||||
/// `Tt`, `TA`, and the leading with the space before its paragraph, `Tl`; then `Tv` and `TY`
|
||||
/// where Standard Vertical Roman Alignment or Tate-chu-yoko changes.
|
||||
fn char_style(&mut self, st: &CharStyle, p: &ParaStyle) {
|
||||
let fill = !st.fill.is_none();
|
||||
let stroke = st.has_stroke();
|
||||
@@ -876,6 +887,14 @@ impl<'a> Writer<'a> {
|
||||
let leading = st.leading.unwrap_or_else(|| p.justification.auto_leading_for(st.size));
|
||||
ps::push_nums(&mut self.body, &[finite(leading, size * 1.2), finite(p.space_before, 0.0)]);
|
||||
self.line("Tl");
|
||||
if st.standard_vertical_roman != self.roman {
|
||||
self.roman = st.standard_vertical_roman;
|
||||
self.line(if self.roman { "1 Tv" } else { "0 Tv" });
|
||||
}
|
||||
if st.tate_chu_yoko != self.kumi {
|
||||
self.kumi = st.tate_chu_yoko;
|
||||
self.line(if self.kumi { "1 TY" } else { "0 TY" });
|
||||
}
|
||||
}
|
||||
|
||||
/// Type as its glyphs' outlines: a group of a compound path for each run, with the run's fill
|
||||
|
||||
@@ -155,7 +155,8 @@ private editing data. VectorCraft:
|
||||
under them, clipped to where they show, along a smooth outline traced at the image's own resolution; that art stays) or omitted (Transparent Images,
|
||||
`transparentImages: "flatten"|"omit"`). Type is written as editable text objects (point type, area type and type
|
||||
on a path, with their fonts' PostScript names, sizes, colours, alignment, indents, leading, tracking and
|
||||
scaling), which the reading app sets in those fonts; characters outside Windows-1252, vertical type, ruby, inline
|
||||
scaling, and the Standard Vertical Roman Alignment and Tate-chu-yoko marks as `Tv` and `TY`), which the reading
|
||||
app sets in those fonts; characters outside Windows-1252, vertical type, ruby, inline
|
||||
graphics and gradient or pattern fills make that type outlines, and so do rotated characters (Character Rotation,
|
||||
which a version 7 text object can't hold: the outlines keep the rotation, and the type is no longer editable text
|
||||
in the file), and Text: Outlines (`outlineText: true`) writes
|
||||
|
||||
Reference in New Issue
Block a user