mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 23:01:02 +00:00
Merge pull request #6243 from z3r0pix3l/fix/6072-pdf-arabic-ligatures
PDF import: Arabic ligatures keep their letters in order (#6072)
This commit is contained in:
@@ -865,6 +865,9 @@ impl<'p> Builder<'p> {
|
||||
let vx = self.fonts.get(&key).and_then(|f| f.vertical.as_ref()).map_or(500.0, |v| v.centre(glyph));
|
||||
m * kurbo::Point::new(vx, 880.0)
|
||||
});
|
||||
// A horizontal line is gathered in visual order and put back in logical order as a whole:
|
||||
// a ligature's characters (lam-alef, lam-lam-heh) go in visual order too (#6072).
|
||||
let text = if top.is_none() { visual_glyph_text(text) } else { text };
|
||||
let place = |line: &mut TextLine| match top {
|
||||
Some(top) => line.push_upright(&look, at, opacity, Upright { top }, &text),
|
||||
None => line.push(&look, at, opacity, advance, &text),
|
||||
@@ -894,3 +897,16 @@ impl<'p> Builder<'p> {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The characters of one glyph in the order they are drawn: a PDF names a ligature's characters
|
||||
/// in logical order (lam then alef for لا), while a horizontal line of type is gathered glyph by
|
||||
/// glyph in visual order and only then put back in logical order. Right-to-left characters are
|
||||
/// turned round here so that putting the line back restores them.
|
||||
fn visual_glyph_text(text: String) -> String {
|
||||
if text.chars().nth(1).is_none() {
|
||||
return text;
|
||||
}
|
||||
let Some((order, _)) = vectorcraft_text::logical_order(&text) else { return text };
|
||||
let chars: Vec<char> = text.chars().collect();
|
||||
order.iter().filter_map(|&i| chars.get(i)).collect()
|
||||
}
|
||||
|
||||
@@ -477,3 +477,43 @@ fn hebrew_comes_back_in_logical_order() {
|
||||
assert_eq!(shown(&t[0]), shown(&original), "{text}");
|
||||
}
|
||||
}
|
||||
|
||||
/// The text objects a PDF of `text` set in `face` imports as.
|
||||
fn imported_texts(face: &vectorcraft_text::FontFace, text: &str) -> Vec<String> {
|
||||
let mut pdf = krilla::Document::new();
|
||||
let mut page = pdf.start_page_with(PageSettings::from_wh(300.0, 200.0).unwrap());
|
||||
let mut s = page.surface();
|
||||
let font = Font::new(face.file_data().to_vec().into(), face.face_index()).unwrap();
|
||||
s.draw_text(KPoint::from_xy(20.0, 50.0), font, 24.0, text, false, TextDirection::Auto);
|
||||
s.finish();
|
||||
page.finish();
|
||||
let r = import_with_report(&pdf.finish().unwrap(), &ImportOptions::default()).unwrap();
|
||||
texts(&r.document).iter().map(|t| t.plain_text()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arabic_ligatures_keep_their_letters_in_order() {
|
||||
// A lam-alef ligature is one glyph whose text is lam then alef: it must not be turned round
|
||||
// with the line it sits in (#6072).
|
||||
let db = vectorcraft_text::FontDb::global();
|
||||
let preferred = ["DejaVu Sans", "Arial", "Times New Roman", "Noto Sans Arabic", "Noto Naskh Arabic"].map(String::from);
|
||||
// Whether `face` draws lam-alef as one glyph, the ligature the fix is about.
|
||||
let ligates = |face: &vectorcraft_text::FontFace| {
|
||||
let style = vectorcraft_doc::CharStyle { font_family: face.family.clone(), font_style: face.style.clone(), ..Default::default() };
|
||||
let t = TextObject::point(kurbo::Point::ORIGIN, "لا", style);
|
||||
vectorcraft_text::layout(db, &t).glyphs.len() == 1
|
||||
};
|
||||
// An installed Arabic font with a lam-alef ligature whose glyphs come back as live type.
|
||||
let Some(face) = preferred
|
||||
.iter()
|
||||
.chain(&db.families())
|
||||
.filter_map(|f| db.face(f, "Regular"))
|
||||
.filter(|f| f.covers('\u{0644}') && f.covers('\u{0627}') && ligates(f))
|
||||
.find(|f| !imported_texts(f, "بب").is_empty())
|
||||
else {
|
||||
return; // No such font on this host.
|
||||
};
|
||||
for text in ["لا", "بلال", "السلام عليكم", "لا إله إلا الله"] {
|
||||
assert_eq!(imported_texts(&face, text), [text], "in {}", face.family);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user