mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 03:37:00 -06:00
Place: Word's low, medium and high kashida justification sets Insert Kashidas
A Word paragraph justified with w:jc lowKashida, mediumKashida or highKashida placed as plain justified type, and its kashida length was dropped. ECMA-376 Part 1, 17.18.44 (ST_Jc) gives these values a slightly longer, a medium and the widest kashida length. Such a paragraph now places justified with Insert Kashidas set to Short, Medium or Long. A paragraph justified with both, and every other alignment, still places with Insert Kashidas set to None. VectorCraft writes no Word or RTF files, so this change does not affect export. Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
@@ -19,7 +19,9 @@
|
||||
//! the color.
|
||||
//! - Paragraph formatting: alignment (`w:jc`), start and end indents (`w:start` or `w:left`,
|
||||
//! `w:end` or `w:right`: the right and left indents of a right-to-left paragraph), first-line and
|
||||
//! hanging indents, space before and after, and direction (`w:bidi`).
|
||||
//! hanging indents, space before and after, and direction (`w:bidi`). A paragraph justified with
|
||||
//! a low, medium or high kashida length (`lowKashida`, `mediumKashida`, `highKashida`) is
|
||||
//! justified with Short, Medium or Long kashidas; `both` is justified without kashidas.
|
||||
//! - Styles (§17.7.2): the document defaults, then the paragraph style (else the default paragraph
|
||||
//! style) and the styles it is based on, then the run's character style (else the default
|
||||
//! character style) and the styles it is based on, then direct formatting. Bold, italic, caps,
|
||||
@@ -37,6 +39,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use roxmltree::Node;
|
||||
use vectorcraft_doc::text::Kashida;
|
||||
use vectorcraft_doc::{CharPosition, CharStyle, Justify, ParaDirection, ParaStyle, ScriptMetrics, TextRun};
|
||||
|
||||
use super::text::{self, Story};
|
||||
@@ -361,8 +364,9 @@ enum Align {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
/// Justified, the last line at the start (the right of a right-to-left paragraph).
|
||||
Both,
|
||||
/// Justified, the last line at the start (the right of a right-to-left paragraph), with the
|
||||
/// kashida length of `lowKashida`, `mediumKashida` and `highKashida` (§17.18.44).
|
||||
Both(Kashida),
|
||||
/// Justified, the last line too.
|
||||
Distribute,
|
||||
Start,
|
||||
@@ -860,8 +864,8 @@ impl Reader {
|
||||
Align::Left => Justify::Left,
|
||||
Align::Center => Justify::Center,
|
||||
Align::Right => Justify::Right,
|
||||
Align::Both if rtl => Justify::JustifyRight,
|
||||
Align::Both => Justify::JustifyLeft,
|
||||
Align::Both(_) if rtl => Justify::JustifyRight,
|
||||
Align::Both(_) => Justify::JustifyLeft,
|
||||
Align::Distribute => Justify::JustifyAll,
|
||||
Align::Start => Justify::Auto,
|
||||
Align::End if rtl => Justify::Left,
|
||||
@@ -873,6 +877,7 @@ impl Reader {
|
||||
space_before: points(p.before),
|
||||
space_after: points(p.after),
|
||||
direction: p.rtl.map(|rtl| if rtl { ParaDirection::RightToLeft } else { ParaDirection::LeftToRight }),
|
||||
kashida: if let Align::Both(k) = p.align { k } else { Kashida::None },
|
||||
..ParaStyle::default()
|
||||
}
|
||||
})
|
||||
@@ -898,7 +903,10 @@ fn para_props(ppr: Node) -> ParaProps {
|
||||
Some("left") => Some(Align::Left),
|
||||
Some("center") => Some(Align::Center),
|
||||
Some("right") => Some(Align::Right),
|
||||
Some("both" | "lowKashida" | "mediumKashida" | "highKashida") => Some(Align::Both),
|
||||
Some("both") => Some(Align::Both(Kashida::None)),
|
||||
Some("lowKashida") => Some(Align::Both(Kashida::Short)),
|
||||
Some("mediumKashida") => Some(Align::Both(Kashida::Medium)),
|
||||
Some("highKashida") => Some(Align::Both(Kashida::Long)),
|
||||
Some("distribute" | "thaiDistribute") => Some(Align::Distribute),
|
||||
Some("start") => Some(Align::Start),
|
||||
Some("end") => Some(Align::End),
|
||||
|
||||
@@ -366,6 +366,47 @@ fn docx_tables_breaks_and_fields_place_as_paragraphs_and_tabs() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn docx_kashida_justification_places_with_insert_kashidas() {
|
||||
use vectorcraft_doc::Justify;
|
||||
use vectorcraft_doc::text::Kashida;
|
||||
// The document holds right-to-left paragraphs justified with each kashida length and with `both`, a paragraph style
|
||||
// that sets `mediumKashida`, that style under direct formatting, and a left-to-right paragraph.
|
||||
let styles = r#"<w:style w:type="paragraph" w:styleId="Kashidas"><w:pPr><w:bidi/><w:jc w:val="mediumKashida"/></w:pPr></w:style>"#;
|
||||
let para = |ppr: &str| format!(r#"<w:p><w:pPr>{ppr}</w:pPr><w:r><w:t>كتاب جميل</w:t></w:r></w:p>"#);
|
||||
let body: String = [
|
||||
r#"<w:bidi/><w:jc w:val="lowKashida"/>"#,
|
||||
r#"<w:bidi/><w:jc w:val="mediumKashida"/>"#,
|
||||
r#"<w:bidi/><w:jc w:val="highKashida"/>"#,
|
||||
r#"<w:bidi/><w:jc w:val="both"/>"#,
|
||||
r#"<w:pStyle w:val="Kashidas"/>"#,
|
||||
r#"<w:pStyle w:val="Kashidas"/><w:jc w:val="both"/>"#,
|
||||
r#"<w:jc w:val="highKashida"/>"#,
|
||||
r#"<w:pStyle w:val="Kashidas"/><w:jc w:val="center"/>"#,
|
||||
]
|
||||
.map(para)
|
||||
.concat();
|
||||
let mut s = session();
|
||||
let r = s.execute("file.place", &json!({"name": "kashida.docx", "dataBase64": vectorcraft_format::base64_encode(&docx(&body, styles))})).unwrap();
|
||||
let n = s.doc().unwrap().doc.node(NodeId(r["ids"][0].as_u64().unwrap())).unwrap().clone();
|
||||
let NodeKind::Text(t) = &n.kind else { panic!("not type") };
|
||||
let paras: Vec<_> = t.paragraph_styles().iter().map(|p| (p.justify, p.kashida)).collect();
|
||||
assert_eq!(
|
||||
paras,
|
||||
[
|
||||
(Justify::JustifyRight, Kashida::Short),
|
||||
(Justify::JustifyRight, Kashida::Medium),
|
||||
(Justify::JustifyRight, Kashida::Long),
|
||||
(Justify::JustifyRight, Kashida::None),
|
||||
(Justify::JustifyRight, Kashida::Medium),
|
||||
(Justify::JustifyRight, Kashida::None),
|
||||
(Justify::JustifyLeft, Kashida::Long),
|
||||
(Justify::Center, Kashida::None),
|
||||
],
|
||||
"low, medium and high kashida lengths are Short, Medium and Long; other alignments have none"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_docx_fails_cleanly() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user