mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 14:14:11 +00:00
.ai import: legacy binding punctuation opens as Burasagari's Force setting
The legacy reader skipped `bindrepeat Tg` (section 15.3.4 of the version 7 file format specification). It now reads the binding punctuation bit: 1 and 3 set the paragraph's Burasagari to Force, and 0 and 2 set it to none, which is VectorCraft's own mapping. The value is modal, lasts into the next text object, and an operand other than 0 to 3 leaves it as it was. The repeat character, `TG`, `Xu` and `Tu` are still skipped. docs/mcp.md and the `ParaStyle::burasagari` doc comment name the legacy `.ai` case as the exception to imported text keeping `none`.
This commit is contained in:
@@ -228,7 +228,8 @@ pub struct ParaStyle {
|
||||
pub leading_model: LeadingModel,
|
||||
/// Hanging punctuation (Paragraph panel menu › Burasagari): a comma or full stop ending a line
|
||||
/// may stand outside the frame. New type takes [`Burasagari::Standard`]; documents from before
|
||||
/// it and imported text keep [`Burasagari::None`].
|
||||
/// it and imported text keep [`Burasagari::None`], except legacy `.ai` type with binding
|
||||
/// punctuation on, which opens with [`Burasagari::Forced`].
|
||||
pub burasagari: Burasagari,
|
||||
/// Kinsoku (Paragraph panel › Kinsoku Set): which Japanese characters may not start or end a
|
||||
/// line.
|
||||
|
||||
@@ -4562,6 +4562,8 @@ fn arb_ai_token() -> impl Strategy<Value = String> {
|
||||
"1e308 Ty",
|
||||
"1 TY",
|
||||
"(x) TY",
|
||||
"3 Tg",
|
||||
"1e308 Tg",
|
||||
"1 1 1e308 -1e308 4e9 Th",
|
||||
"1 1 (2) 3 4 Th",
|
||||
"36 2 Xb",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
//! type runs along, `TP`), the modal character and paragraph attributes
|
||||
//! (`Tr Tf Ts Tz Tt TA Tl Ta Ti`, and §15.4.7 and §22's letter and word spacing `TC TW`, hanging
|
||||
//! punctuation `Tq`, hyphenation `Th` and tab stops `Xb Xe XB`, and §15.3.4's writing direction `TV`,
|
||||
//! Roman character rotation `Tv` and Kumi blocks `Ty TY`), the paint style, and the characters (`Tx`, `Tj`,
|
||||
//! overflow `TX`; `\r` ends a paragraph). Type set in rendering modes 4 to 7 is a mask (§5.7): the
|
||||
//! Roman character rotation `Tv`, Kumi blocks `Ty TY` and binding punctuation `Tg`), the paint
|
||||
//! style, and the characters (`Tx`, `Tj`, overflow `TX`; `\r` ends a paragraph). Type set in rendering modes 4 to 7 is a mask (§5.7): the
|
||||
//! text object clips the objects of its clipping group. Final-form operators (the
|
||||
//! computed positions, §15.1) are skipped: the type is laid out again. Strings are decoded by the
|
||||
//! encoding pairs of the current font's `TZ`, then by the file's `TE` encoding unless that `TZ`'s
|
||||
@@ -17,7 +17,7 @@ use std::sync::Arc;
|
||||
use craft_pdf::interpret::hayro_cmap::{BfString, CMap, CMapName, load_embedded};
|
||||
use kurbo::{Affine, BezPath, ParamCurve, ParamCurveArclen};
|
||||
use vectorcraft_color::Paint;
|
||||
use vectorcraft_doc::{CharStyle, Justify, NodeKind, ParaStyle, TabAlign, TabStop, TextKind, TextObject, TextRun};
|
||||
use vectorcraft_doc::{Burasagari, CharStyle, Justify, NodeKind, ParaStyle, TabAlign, TabStop, TextKind, TextObject, TextRun};
|
||||
use vectorcraft_geom::AnchorKind;
|
||||
|
||||
use super::super::obj::V;
|
||||
@@ -870,6 +870,19 @@ impl Reader<'_> {
|
||||
st.para.roman_hanging = on;
|
||||
}
|
||||
}
|
||||
// `bindrepeat Tg` (§15.3.4): 1 and 3 turn binding punctuation on, and 0 and 2 turn it
|
||||
// off. VectorCraft's own mapping opens it on as Burasagari's Force setting and off as no
|
||||
// Burasagari. The repeat character (2 and 3) is not kept.
|
||||
"Tg" => {
|
||||
let burasagari = match n0 {
|
||||
Some(1.0 | 3.0) => Some(Burasagari::Forced),
|
||||
Some(0.0 | 2.0) => Some(Burasagari::None),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(b) = burasagari {
|
||||
st.para.burasagari = b;
|
||||
}
|
||||
}
|
||||
// `hyphenate limitHyphLines minLeadHyphen minTailHyphen maxHyphLines Th`, kept to the
|
||||
// ranges `text.setFormat` takes.
|
||||
"Th" => {
|
||||
|
||||
@@ -360,13 +360,13 @@ impl<'a> Reader<'a> {
|
||||
"TO" => self.end_text(hidden)?,
|
||||
// `(text) justified TS`, special characters (§22.6): the reader adds its characters like `Tx`.
|
||||
"Tx" | "Tj" | "TX" | "TS" => self.text_body(w, &vals),
|
||||
"Tr" | "Tf" | "Ts" | "Tz" | "Tt" | "TA" | "Tl" | "Ta" | "Ti" | "TC" | "TW" | "Tq" | "Th" | "TV" | "Tv" | "Ty" | "TY" | "Xb" | "Xe"
|
||||
| "XB" => {
|
||||
"Tr" | "Tf" | "Ts" | "Tz" | "Tt" | "TA" | "Tl" | "Ta" | "Ti" | "TC" | "TW" | "Tq" | "Th" | "TV" | "Tv" | "Ty" | "TY" | "Tg" | "Xb"
|
||||
| "Xe" | "XB" => {
|
||||
self.text_attr(w, &vals);
|
||||
}
|
||||
// Attributes the reader doesn't keep (kerning pairs, line breaking, binding punctuation
|
||||
// and Japanese layout rules) and final-form positions (§15.3).
|
||||
"Tk" | "TK" | "Tc" | "Tw" | "Tm" | "Td" | "T*" | "TR" | "T+" | "T-" | "TG" | "Tg" | "Tu" | "Xu" => {}
|
||||
// Attributes the reader doesn't keep (kerning pairs, line breaking and Japanese layout
|
||||
// rules) and final-form positions (§15.3).
|
||||
"Tk" | "TK" | "Tc" | "Tw" | "Tm" | "Td" | "T*" | "TR" | "T+" | "T-" | "TG" | "Tu" | "Xu" => {}
|
||||
// `TM` (pop text matrix) and `Te` (end render), listed in §22.6, keep nothing either; the
|
||||
// reader takes `Te` with any number of operands.
|
||||
"TM" | "Te" => {}
|
||||
|
||||
@@ -3599,6 +3599,32 @@ fn legacy_type_keeps_its_spacing_hanging_punctuation_and_hyphenation() {
|
||||
assert_eq!(texts[1].para_at(0), two);
|
||||
}
|
||||
|
||||
/// `Tg` (§15.3.4) turns binding punctuation on (1 and 3) or off (0 and 2); VectorCraft opens it on
|
||||
/// as Burasagari's Force setting. Each paragraph of point type and area type keeps the value in
|
||||
/// force at its first character, and it lasts into the next text object.
|
||||
#[test]
|
||||
fn legacy_type_keeps_its_binding_punctuation() {
|
||||
use vectorcraft_doc::Burasagari;
|
||||
let burasagari = |attrs: &str| legacy_texts(&point_type(attrs, "Hi"))[0].para_at(0).burasagari;
|
||||
assert_eq!(burasagari(""), Burasagari::None);
|
||||
for on in ["1", "3"] {
|
||||
assert_eq!(burasagari(&format!("0 Tg\n{on} Tg")), Burasagari::Forced, "{on} Tg");
|
||||
}
|
||||
for off in ["0", "2"] {
|
||||
assert_eq!(burasagari(&format!("1 Tg\n{off} Tg")), Burasagari::None, "{off} Tg");
|
||||
}
|
||||
let two_paragraphs = point_type("1 Tg", "One\\r").replace("TO\n", "0 Tg\n(Two) Tx\nTO\n");
|
||||
let texts = legacy_texts(&format!("{two_paragraphs}{}{}", point_type("3 Tg", "Three"), point_type("", "Four")));
|
||||
let each: Vec<Burasagari> = vec![texts[0].para_at(0).burasagari, texts[0].para_at(1).burasagari, texts[1].para_at(0).burasagari];
|
||||
assert_eq!(each, [Burasagari::Forced, Burasagari::None, Burasagari::Forced]);
|
||||
assert_eq!(texts[2].para_at(0).burasagari, Burasagari::Forced, "the next text object keeps it");
|
||||
let frame = "10 10 m\n110 10 L\n110 110 L\n10 110 L\nN";
|
||||
let area = format!("1 To\n1 0 0 1 10 110 0 Tp\n{frame}\nTP\n0 Tr\n0 0 0 1 k\n/_Helvetica 12 Tf\n1 Tg\n(Area) Tx\nTO\n");
|
||||
let texts = legacy_texts(&area);
|
||||
assert!(matches!(texts[0].kind, vectorcraft_doc::TextKind::Area { .. }));
|
||||
assert_eq!(texts[0].para_at(0).burasagari, Burasagari::Forced, "area type");
|
||||
}
|
||||
|
||||
/// `TV` (§15.3.4) sets the writing direction of point type, area type, type on a path and linked
|
||||
/// areas, and lasts into the next text object until it changes.
|
||||
#[test]
|
||||
@@ -3739,6 +3765,10 @@ fn legacy_type_attributes_with_bad_operands_stay_as_they_were() {
|
||||
assert_eq!(p.justify, vectorcraft_doc::Justify::Right);
|
||||
assert_eq!((p.first_line_indent, p.left_indent, p.right_indent), (5.0, 6.0, 7.0));
|
||||
assert_eq!(texts[0].runs[0].style.kerning, Some(0.0), "kerning stays off");
|
||||
let texts = legacy_texts(&point_type("1 Tg\n4 Tg\n-1 Tg\n0.5 Tg\n/x Tg\n(0) Tg\nTg", "Hi"));
|
||||
assert_eq!(texts[0].para_at(0).burasagari, vectorcraft_doc::Burasagari::Forced, "binding punctuation stays on");
|
||||
let texts = legacy_texts(&point_type("0 Tg\n4 Tg\n-1 Tg\n2.5 Tg", "Hi"));
|
||||
assert_eq!(texts[0].para_at(0).burasagari, vectorcraft_doc::Burasagari::None, "binding punctuation stays off");
|
||||
let texts = legacy_texts(&point_type("1 TV\n1 Tv\n1 Ty\n2 TV\n/x TV\nTV\n-1 Tv\n(x) Tv\n0.5 Ty\n2 TY", "Hi"));
|
||||
let st = &texts[0].runs[0].style;
|
||||
assert!(texts[0].vertical && st.standard_vertical_roman && st.tate_chu_yoko);
|
||||
|
||||
@@ -187,7 +187,9 @@ private editing data. VectorCraft:
|
||||
block in vertical type opens unmarked, and two blocks with nothing between them open as one block. Its upright
|
||||
Roman characters (`Tv`) open with Standard Vertical Roman Alignment. VectorCraft's own layout rule also sets a
|
||||
run of two or three half-width digits across the column in vertical type, inside or outside a block and with
|
||||
either `Tv` value.
|
||||
either `Tv` value. Its binding punctuation (`Tg`) opens as Burasagari's Force setting when on and as no
|
||||
Burasagari when off, which is VectorCraft's own mapping; the repeat character, line breaking (`TG`) and
|
||||
Japanese layout rules (`Xu`, `Tu`) are not read.
|
||||
Legacy `.ai` type set in rendering modes 4 to 7 (`Tr`, text used as a mask) is a clipping path of its
|
||||
clipping group, as a `W` path is, and the mode lasts into later text objects. Type in linked areas, in
|
||||
wraparound text or in a compound path doesn't clip, with a warning. A `Tr` operand that is not a whole number
|
||||
|
||||
+2
-2
@@ -5529,8 +5529,8 @@ Force) of the selected type (or `ids`): an East Asian comma or full stop ending
|
||||
、。) hangs outside the frame, Regular only when it doesn't fit, Force always (the rest of the line then fills the
|
||||
measure). Closing brackets and Latin punctuation don't hang; point type, right-to-left and hyphenated lines are left as
|
||||
they are. The hanging mark is as wide as its punctuation spacing makes it (`mojikumi`). New type (the Type tools,
|
||||
`text.create`) takes `standard`; documents from before it and imported text keep `none`, and `none` isn't saved. One
|
||||
undo step.
|
||||
`text.create`) takes `standard`; documents from before it and imported text keep `none`, except legacy `.ai` type
|
||||
with binding punctuation (`Tg`) on, which opens as `forced`. `none` isn't saved. One undo step.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"text.setFormat","params":{"burasagari":"forced"}}}
|
||||
|
||||
Reference in New Issue
Block a user