mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Preallocate Page Refs
This commit is contained in:
parent
55dce19f49
commit
9dca4c2f78
@ -53,6 +53,8 @@ struct PdfExporter<'a> {
|
|||||||
glyph_sets: HashMap<FaceId, HashSet<u16>>,
|
glyph_sets: HashMap<FaceId, HashSet<u16>>,
|
||||||
image_map: Remapper<ImageId>,
|
image_map: Remapper<ImageId>,
|
||||||
image_refs: Vec<Ref>,
|
image_refs: Vec<Ref>,
|
||||||
|
page_refs: Vec<Ref>,
|
||||||
|
heading_tree: Vec<HeadingNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PdfExporter<'a> {
|
impl<'a> PdfExporter<'a> {
|
||||||
@ -68,6 +70,8 @@ impl<'a> PdfExporter<'a> {
|
|||||||
glyph_sets: HashMap::new(),
|
glyph_sets: HashMap::new(),
|
||||||
image_map: Remapper::new(),
|
image_map: Remapper::new(),
|
||||||
image_refs: vec![],
|
image_refs: vec![],
|
||||||
|
page_refs: vec![],
|
||||||
|
heading_tree: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +85,9 @@ impl<'a> PdfExporter<'a> {
|
|||||||
|
|
||||||
fn build_pages(&mut self, frames: &[Arc<Frame>]) {
|
fn build_pages(&mut self, frames: &[Arc<Frame>]) {
|
||||||
for frame in frames {
|
for frame in frames {
|
||||||
let page = PageExporter::new(self).export(frame);
|
let page_id = self.alloc.bump();
|
||||||
|
self.page_refs.push(page_id);
|
||||||
|
let page = PageExporter::new(self, page_id).export(frame);
|
||||||
self.pages.push(page);
|
self.pages.push(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,43 +312,34 @@ impl<'a> PdfExporter<'a> {
|
|||||||
let page_tree_ref = self.alloc.bump();
|
let page_tree_ref = self.alloc.bump();
|
||||||
|
|
||||||
// The page objects (non-root nodes in the page tree).
|
// The page objects (non-root nodes in the page tree).
|
||||||
let mut page_refs = vec![];
|
let mut page_heights =
|
||||||
let mut page_heights = vec![];
|
self.pages.iter().map(|page| page.size.y.to_f32()).collect();
|
||||||
for page in &self.pages {
|
|
||||||
let page_id = self.alloc.bump();
|
|
||||||
page_refs.push(page_id);
|
|
||||||
page_heights.push(page.size.y.to_f32());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut languages = HashMap::new();
|
let mut languages = HashMap::new();
|
||||||
let mut heading_tree: Vec<HeadingNode> = vec![];
|
|
||||||
|
|
||||||
for (page, page_id) in
|
for (page, page_id) in std::mem::take(&mut self.pages)
|
||||||
std::mem::take(&mut self.pages).into_iter().zip(page_refs.iter())
|
.into_iter()
|
||||||
|
.zip(self.page_refs.clone().iter())
|
||||||
{
|
{
|
||||||
self.write_page(
|
self.write_page(
|
||||||
page,
|
page,
|
||||||
*page_id,
|
*page_id,
|
||||||
&page_refs,
|
|
||||||
page_tree_ref,
|
page_tree_ref,
|
||||||
&mut languages,
|
&mut languages,
|
||||||
&mut heading_tree,
|
|
||||||
&mut page_heights,
|
&mut page_heights,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_page_tree(&page_refs, page_tree_ref);
|
self.write_page_tree(page_tree_ref);
|
||||||
self.write_catalog(page_tree_ref, &languages, &heading_tree);
|
self.write_catalog(page_tree_ref, &languages);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_page(
|
fn write_page(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: Page,
|
page: Page,
|
||||||
page_id: Ref,
|
page_id: Ref,
|
||||||
page_refs: &[Ref],
|
|
||||||
page_tree_ref: Ref,
|
page_tree_ref: Ref,
|
||||||
languages: &mut HashMap<Lang, usize>,
|
languages: &mut HashMap<Lang, usize>,
|
||||||
heading_tree: &mut Vec<HeadingNode>,
|
|
||||||
page_heights: &mut Vec<f32>,
|
page_heights: &mut Vec<f32>,
|
||||||
) {
|
) {
|
||||||
let content_id = self.alloc.bump();
|
let content_id = self.alloc.bump();
|
||||||
@ -371,7 +368,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
link.action()
|
link.action()
|
||||||
.action_type(ActionType::GoTo)
|
.action_type(ActionType::GoTo)
|
||||||
.destination_direct()
|
.destination_direct()
|
||||||
.page(page_refs[index])
|
.page(self.page_refs[index])
|
||||||
.xyz(loc.pos.x.to_f32(), height - loc.pos.y.to_f32(), None);
|
.xyz(loc.pos.x.to_f32(), height - loc.pos.y.to_f32(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -387,24 +384,16 @@ impl<'a> PdfExporter<'a> {
|
|||||||
.or_insert_with(|| count);
|
.or_insert_with(|| count);
|
||||||
}
|
}
|
||||||
|
|
||||||
for heading in page.headings.into_iter() {
|
|
||||||
if let Some(last) = heading_tree.last_mut() {
|
|
||||||
if !last.insert(heading.clone(), page_id, 1) {
|
|
||||||
heading_tree.push(HeadingNode::leaf(heading, page_id))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
heading_tree.push(HeadingNode::leaf(heading, page_id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.writer
|
self.writer
|
||||||
.stream(content_id, &deflate(&page.content.finish()))
|
.stream(content_id, &deflate(&page.content.finish()))
|
||||||
.filter(Filter::FlateDecode);
|
.filter(Filter::FlateDecode);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_page_tree(&mut self, page_refs: &[Ref], page_tree_ref: Ref) {
|
fn write_page_tree(&mut self, page_tree_ref: Ref) {
|
||||||
let mut pages = self.writer.pages(page_tree_ref);
|
let mut pages = self.writer.pages(page_tree_ref);
|
||||||
pages.count(page_refs.len() as i32).kids(page_refs.iter().copied());
|
pages
|
||||||
|
.count(self.page_refs.len() as i32)
|
||||||
|
.kids(self.page_refs.iter().copied());
|
||||||
|
|
||||||
let mut resources = pages.resources();
|
let mut resources = pages.resources();
|
||||||
let mut spaces = resources.color_spaces();
|
let mut spaces = resources.color_spaces();
|
||||||
@ -431,31 +420,26 @@ impl<'a> PdfExporter<'a> {
|
|||||||
pages.finish();
|
pages.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_catalog(
|
fn write_catalog(&mut self, page_tree_ref: Ref, languages: &HashMap<Lang, usize>) {
|
||||||
&mut self,
|
|
||||||
page_tree_ref: Ref,
|
|
||||||
languages: &HashMap<Lang, usize>,
|
|
||||||
heading_tree: &Vec<HeadingNode>,
|
|
||||||
) {
|
|
||||||
// Build the outline tree.
|
// Build the outline tree.
|
||||||
let outline_root_id = self.alloc.bump();
|
let outline_root_id = self.alloc.bump();
|
||||||
|
|
||||||
let outline_start_ref = self.alloc;
|
let outline_start_ref = self.alloc;
|
||||||
|
|
||||||
for (i, node) in heading_tree.iter().enumerate() {
|
for (i, node) in std::mem::take(&mut self.heading_tree).iter().enumerate() {
|
||||||
self.write_outline_item(
|
self.write_outline_item(
|
||||||
node,
|
node,
|
||||||
i == 0,
|
i == 0,
|
||||||
i + 1 == heading_tree.len(),
|
i + 1 == self.heading_tree.len(),
|
||||||
outline_root_id,
|
outline_root_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !heading_tree.is_empty() {
|
if !self.heading_tree.is_empty() {
|
||||||
let mut outline_root = self.writer.outline(outline_root_id);
|
let mut outline_root = self.writer.outline(outline_root_id);
|
||||||
outline_root.first(outline_start_ref);
|
outline_root.first(outline_start_ref);
|
||||||
outline_root.last(Ref::new(self.alloc.get() - 1));
|
outline_root.last(Ref::new(self.alloc.get() - 1));
|
||||||
outline_root.count(heading_tree.len() as i32);
|
outline_root.count(self.heading_tree.len() as i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lang = languages
|
let lang = languages
|
||||||
@ -475,7 +459,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
catalog.pages(page_tree_ref);
|
catalog.pages(page_tree_ref);
|
||||||
catalog.viewer_preferences().direction(dir);
|
catalog.viewer_preferences().direction(dir);
|
||||||
|
|
||||||
if !heading_tree.is_empty() {
|
if !self.heading_tree.is_empty() {
|
||||||
catalog.outlines(outline_root_id);
|
catalog.outlines(outline_root_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +500,7 @@ impl<'a> PdfExporter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
outline.title(TextStr(&node.heading.content));
|
outline.title(TextStr(&node.heading.content));
|
||||||
outline.dest_direct().page(node.page).xyz(
|
outline.dest_direct().page(node.heading.page).xyz(
|
||||||
node.heading.position.x.to_f32(),
|
node.heading.position.x.to_f32(),
|
||||||
(node.heading.position.y + Length::pt(3.0)).to_f32(),
|
(node.heading.position.y + Length::pt(3.0)).to_f32(),
|
||||||
None,
|
None,
|
||||||
@ -538,13 +522,14 @@ struct PageExporter<'a> {
|
|||||||
font_map: &'a mut Remapper<FaceId>,
|
font_map: &'a mut Remapper<FaceId>,
|
||||||
image_map: &'a mut Remapper<ImageId>,
|
image_map: &'a mut Remapper<ImageId>,
|
||||||
glyphs: &'a mut HashMap<FaceId, HashSet<u16>>,
|
glyphs: &'a mut HashMap<FaceId, HashSet<u16>>,
|
||||||
|
heading_tree: &'a mut Vec<HeadingNode>,
|
||||||
|
page_ref: Ref,
|
||||||
languages: HashMap<Lang, usize>,
|
languages: HashMap<Lang, usize>,
|
||||||
bottom: f32,
|
bottom: f32,
|
||||||
content: Content,
|
content: Content,
|
||||||
links: Vec<(Destination, Rect)>,
|
links: Vec<(Destination, Rect)>,
|
||||||
state: State,
|
state: State,
|
||||||
saves: Vec<State>,
|
saves: Vec<State>,
|
||||||
headings: Vec<Heading>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data for an exported page.
|
/// Data for an exported page.
|
||||||
@ -553,7 +538,6 @@ struct Page {
|
|||||||
content: Content,
|
content: Content,
|
||||||
links: Vec<(Destination, Rect)>,
|
links: Vec<(Destination, Rect)>,
|
||||||
languages: HashMap<Lang, usize>,
|
languages: HashMap<Lang, usize>,
|
||||||
headings: Vec<Heading>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simulated graphics state used to deduplicate graphics state changes and
|
/// A simulated graphics state used to deduplicate graphics state changes and
|
||||||
@ -574,57 +558,55 @@ struct Heading {
|
|||||||
content: EcoString,
|
content: EcoString,
|
||||||
level: usize,
|
level: usize,
|
||||||
position: Point,
|
position: Point,
|
||||||
|
page: Ref,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct HeadingNode {
|
struct HeadingNode {
|
||||||
heading: Heading,
|
heading: Heading,
|
||||||
page: Ref,
|
|
||||||
children: Vec<HeadingNode>,
|
children: Vec<HeadingNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeadingNode {
|
impl HeadingNode {
|
||||||
fn leaf(heading: Heading, page: Ref) -> Self {
|
fn leaf(heading: Heading) -> Self {
|
||||||
HeadingNode { heading, page, children: Vec::new() }
|
HeadingNode { heading, children: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
1 + self.children.iter().map(|c| c.len()).sum::<usize>()
|
1 + self.children.iter().map(|c| c.len()).sum::<usize>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, other: Heading, page: Ref, level: usize) -> bool {
|
fn insert(&mut self, other: Heading, level: usize) -> bool {
|
||||||
if level >= other.level {
|
if level >= other.level {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.children.is_empty() && level + 1 > other.level {
|
if !self.children.is_empty() && level + 1 > other.level {
|
||||||
return self.children.last_mut().unwrap().insert(other, page, level + 1);
|
return self.children.last_mut().unwrap().insert(other, level + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.children.push(HeadingNode {
|
self.children
|
||||||
heading: other,
|
.push(HeadingNode { heading: other, children: Vec::new() });
|
||||||
page,
|
|
||||||
children: Vec::new(),
|
|
||||||
});
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PageExporter<'a> {
|
impl<'a> PageExporter<'a> {
|
||||||
fn new(exporter: &'a mut PdfExporter) -> Self {
|
fn new(exporter: &'a mut PdfExporter, page_ref: Ref) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fonts: exporter.fonts,
|
fonts: exporter.fonts,
|
||||||
font_map: &mut exporter.face_map,
|
font_map: &mut exporter.face_map,
|
||||||
image_map: &mut exporter.image_map,
|
image_map: &mut exporter.image_map,
|
||||||
glyphs: &mut exporter.glyph_sets,
|
glyphs: &mut exporter.glyph_sets,
|
||||||
|
heading_tree: &mut exporter.heading_tree,
|
||||||
|
page_ref,
|
||||||
languages: HashMap::new(),
|
languages: HashMap::new(),
|
||||||
bottom: 0.0,
|
bottom: 0.0,
|
||||||
content: Content::new(),
|
content: Content::new(),
|
||||||
links: vec![],
|
links: vec![],
|
||||||
state: State::default(),
|
state: State::default(),
|
||||||
saves: vec![],
|
saves: vec![],
|
||||||
headings: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,17 +627,25 @@ impl<'a> PageExporter<'a> {
|
|||||||
content: self.content,
|
content: self.content,
|
||||||
links: self.links,
|
links: self.links,
|
||||||
languages: self.languages,
|
languages: self.languages,
|
||||||
headings: self.headings,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_frame(&mut self, frame: &Frame) {
|
fn write_frame(&mut self, frame: &Frame) {
|
||||||
if let Some(Role::Heading(level)) = frame.role() {
|
if let Some(Role::Heading(level)) = frame.role() {
|
||||||
self.headings.push(Heading {
|
let heading = Heading {
|
||||||
position: Point::new(self.state.transform.tx, self.state.transform.ty),
|
position: Point::new(self.state.transform.tx, self.state.transform.ty),
|
||||||
content: frame.inner_text(),
|
content: frame.inner_text(),
|
||||||
|
page: self.page_ref,
|
||||||
level,
|
level,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
if let Some(last) = self.heading_tree.last_mut() {
|
||||||
|
if !last.insert(heading.clone(), 1) {
|
||||||
|
self.heading_tree.push(HeadingNode::leaf(heading))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.heading_tree.push(HeadingNode::leaf(heading))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for &(pos, ref element) in &frame.elements {
|
for &(pos, ref element) in &frame.elements {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user