Write language and direction for PDFs

This commit is contained in:
Martin Haug 2022-05-11 15:19:03 +02:00
parent a247653cd0
commit 84bd3454df
4 changed files with 52 additions and 6 deletions

View File

@ -7,8 +7,8 @@ use std::sync::Arc;
use image::{DynamicImage, GenericImageView, ImageFormat, ImageResult, Rgba};
use pdf_writer::types::{
ActionType, AnnotationType, CidFontType, ColorSpaceOperand, FontFlags, SystemInfo,
UnicodeCmap,
ActionType, AnnotationType, CidFontType, ColorSpaceOperand, Direction, FontFlags,
SystemInfo, UnicodeCmap,
};
use pdf_writer::writers::ColorSpace;
use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref, Str, TextStr};
@ -18,10 +18,11 @@ use super::subset::subset;
use crate::font::{find_name, FaceId, FontStore};
use crate::frame::{Element, Frame, Group, Text};
use crate::geom::{
self, Color, Em, Geometry, Length, Numeric, Paint, Point, Shape, Size, Stroke,
self, Color, Dir, Em, Geometry, Length, Numeric, Paint, Point, Shape, Size, Stroke,
Transform,
};
use crate::image::{Image, ImageId, ImageStore, RasterImage};
use crate::library::text::Lang;
use crate::Context;
/// Export a collection of frames into a PDF file.
@ -303,6 +304,7 @@ impl<'a> PdfExporter<'a> {
// The page objects (non-root nodes in the page tree).
let mut page_refs = vec![];
let mut languages = HashMap::new();
for page in self.pages {
let page_id = self.alloc.bump();
let content_id = self.alloc.bump();
@ -330,6 +332,13 @@ impl<'a> PdfExporter<'a> {
annotations.finish();
page_writer.finish();
for (lang, count) in page.languages {
languages
.entry(lang)
.and_modify(|x| *x += count)
.or_insert_with(|| count);
}
self.writer
.stream(content_id, &deflate(&page.content.finish()))
.filter(Filter::FlateDecode);
@ -359,9 +368,28 @@ impl<'a> PdfExporter<'a> {
resources.finish();
pages.finish();
let lang = languages
.into_iter()
.max_by(|(_, v1), (_, v2)| v1.cmp(v2))
.map(|(k, _)| k);
let dir = if lang.map(Lang::dir) == Some(Dir::RTL) {
Direction::R2L
} else {
Direction::L2R
};
// Write the document information, catalog and wrap it up!
self.writer.document_info(self.alloc.bump()).creator(TextStr("Typst"));
self.writer.catalog(self.alloc.bump()).pages(page_tree_ref);
let mut catalog = self.writer.catalog(self.alloc.bump());
catalog.pages(page_tree_ref);
catalog.viewer_preferences().direction(dir);
if let Some(lang) = lang {
catalog.lang(TextStr(lang.as_str()));
}
catalog.finish();
self.writer.finish()
}
}
@ -372,6 +400,7 @@ struct PageExporter<'a> {
font_map: &'a mut Remapper<FaceId>,
image_map: &'a mut Remapper<ImageId>,
glyphs: &'a mut HashMap<FaceId, HashSet<u16>>,
languages: HashMap<Lang, usize>,
bottom: f32,
content: Content,
links: Vec<(String, Rect)>,
@ -384,6 +413,7 @@ struct Page {
size: Size,
content: Content,
links: Vec<(String, Rect)>,
languages: HashMap<Lang, usize>,
}
/// A simulated graphics state used to deduplicate graphics state changes and
@ -403,6 +433,7 @@ impl<'a> PageExporter<'a> {
font_map: &mut exporter.face_map,
image_map: &mut exporter.image_map,
glyphs: &mut exporter.glyph_sets,
languages: HashMap::new(),
bottom: 0.0,
content: Content::new(),
links: vec![],
@ -422,6 +453,7 @@ impl<'a> PageExporter<'a> {
size: frame.size,
content: self.content,
links: self.links,
languages: self.languages,
}
}
@ -508,6 +540,11 @@ impl<'a> PageExporter<'a> {
items.show(Str(&encoded));
}
self.languages
.entry(text.lang)
.and_modify(|x| *x += text.glyphs.len())
.or_insert_with(|| text.glyphs.len());
items.finish();
positioned.finish();
self.content.end_text();

View File

@ -8,6 +8,7 @@ use crate::geom::{
Align, Em, Length, Numeric, Paint, Point, Shape, Size, Spec, Transform,
};
use crate::image::ImageId;
use crate::library::text::Lang;
use crate::util::{EcoString, MaybeShared};
/// A finished layout with elements at fixed positions.
@ -269,6 +270,8 @@ pub struct Text {
pub size: Length,
/// Glyph color.
pub fill: Paint,
/// The natural language of the text.
pub lang: Lang,
/// The glyphs.
pub glyphs: Vec<Glyph>,
}

View File

@ -28,7 +28,7 @@ impl Lang {
}
/// The default direction for the language.
pub fn dir(&self) -> Dir {
pub fn dir(self) -> Dir {
match self.as_str() {
"ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur"
| "yi" => Dir::RTL,

View File

@ -104,7 +104,13 @@ impl<'a> ShapedText<'a> {
})
.collect();
let text = Text { face_id, size: self.size, fill, glyphs };
let text = Text {
face_id,
size: self.size,
lang: self.styles.get(TextNode::LANG),
fill,
glyphs,
};
let text_layer = frame.layer();
let width = text.width();