Reformat + add some link handling agian

This commit is contained in:
Laurenz Stampfl 2024-12-15 19:48:42 +01:00
parent 1d135ddd90
commit 761c8cf15e
5 changed files with 87 additions and 72 deletions

View File

@ -62,7 +62,14 @@ impl RasterImage {
// Extract pixel density. // Extract pixel density.
let dpi = determine_dpi(&data, exif.as_ref()); let dpi = determine_dpi(&data, exif.as_ref());
Ok(Self(Arc::new(Repr { data, format, is_rotated, dynamic: Arc::new(dynamic), icc, dpi }))) Ok(Self(Arc::new(Repr {
data,
format,
is_rotated,
dynamic: Arc::new(dynamic),
icc,
dpi,
})))
} }
/// The raw image data. /// The raw image data.

View File

@ -1,13 +1,9 @@
use std::collections::HashMap;
use std::io::Cursor;
use ecow::eco_format; use ecow::eco_format;
use image::{DynamicImage, GenericImageView, Rgba}; use image::{GenericImageView, Rgba};
use pdf_writer::{Chunk, Filter, Finish, Ref}; use pdf_writer::{Chunk, Filter, Finish, Ref};
use std::collections::HashMap;
use typst_library::diag::{At, SourceResult, StrResult}; use typst_library::diag::{At, SourceResult, StrResult};
use typst_library::visualize::{ use typst_library::visualize::{ColorSpace, Image, ImageKind, RasterImage, SvgImage};
ColorSpace, Image, ImageKind, RasterFormat, RasterImage, SvgImage,
};
use typst_utils::Deferred; use typst_utils::Deferred;
use crate::{color_old, deflate, PdfChunk, WithGlobalRefs}; use crate::{color_old, deflate, PdfChunk, WithGlobalRefs};

View File

@ -1,3 +1,4 @@
use crate::content_old::Builder;
use crate::primitive::{PointExt, SizeExt, TransformExt}; use crate::primitive::{PointExt, SizeExt, TransformExt};
use crate::{paint, AbsExt}; use crate::{paint, AbsExt};
use bytemuck::TransparentWrapper; use bytemuck::TransparentWrapper;
@ -16,7 +17,9 @@ use std::hash::{Hash, Hasher};
use std::ops::Range; use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
use svg2pdf::usvg::Rect; use svg2pdf::usvg::Rect;
use typst_library::layout::{Abs, Frame, FrameItem, GroupItem, PagedDocument, Point, Ratio, Size, Transform}; use typst_library::layout::{
Abs, Frame, FrameItem, GroupItem, PagedDocument, Point, Size, Transform,
};
use typst_library::model::Destination; use typst_library::model::Destination;
use typst_library::text::{Font, Glyph, TextItem}; use typst_library::text::{Font, Glyph, TextItem};
use typst_library::visualize::{ use typst_library::visualize::{
@ -37,7 +40,11 @@ struct State {
impl State { impl State {
/// Creates a new, clean state for a given `size`. /// Creates a new, clean state for a given `size`.
fn new(size: Size, transform_chain: Transform, container_transform_chain: Transform) -> Self { fn new(
size: Size,
transform_chain: Transform,
container_transform_chain: Transform,
) -> Self {
Self { Self {
transform_chain, transform_chain,
transform: Transform::identity(), transform: Transform::identity(),
@ -73,15 +80,15 @@ impl State {
pub(crate) struct FrameContext { pub(crate) struct FrameContext {
states: Vec<State>, states: Vec<State>,
annotations: Vec<krilla::annotation::Annotation>,
} }
impl FrameContext { impl FrameContext {
pub fn new(size: Size) -> Self { pub fn new(size: Size) -> Self {
Self { states: vec![State::new(size, Transform::identity(), Transform::identity())] } Self {
states: vec![State::new(size, Transform::identity(), Transform::identity())],
annotations: vec![],
} }
pub fn derive_new(&self, size: Size) -> Self {
Self { states: vec![State::new(size, self.state().transform_chain, self.state().container_transform_chain)] }
} }
pub fn push(&mut self) { pub fn push(&mut self) {
@ -148,12 +155,11 @@ impl krilla::font::Glyph for PdfGlyph {
pub struct GlobalContext { pub struct GlobalContext {
fonts: HashMap<Font, krilla::font::Font>, fonts: HashMap<Font, krilla::font::Font>,
annotations: Vec<krilla::annotation::Annotation>,
} }
impl GlobalContext { impl GlobalContext {
pub fn new() -> Self { pub fn new() -> Self {
Self { fonts: Default::default(), annotations: vec![] } Self { fonts: Default::default() }
} }
} }
@ -193,8 +199,7 @@ pub fn pdf(typst_document: &PagedDocument) -> Vec<u8> {
); );
surface.finish(); surface.finish();
let annotations = std::mem::take(&mut context.annotations); for annotation in fc.annotations {
for annotation in annotations {
page.add_annotation(annotation); page.add_annotation(annotation);
} }
} }
@ -237,7 +242,7 @@ pub fn process_frame(
FrameItem::Image(image, size, span) => { FrameItem::Image(image, size, span) => {
handle_image(fc, image, *size, surface) handle_image(fc, image, *size, surface)
} }
FrameItem::Link(d, s) => {} FrameItem::Link(d, s) => write_link(fc, d, *s),
FrameItem::Tag(_) => {} FrameItem::Tag(_) => {}
} }
@ -247,6 +252,53 @@ pub fn process_frame(
fc.pop(); fc.pop();
} }
/// Save a link for later writing in the annotations dictionary.
fn write_link(fc: &mut FrameContext, dest: &Destination, size: Size) {
let mut min_x = Abs::inf();
let mut min_y = Abs::inf();
let mut max_x = -Abs::inf();
let mut max_y = -Abs::inf();
let pos = Point::zero();
// Compute the bounding box of the transformed link.
for point in [
pos,
pos + Point::with_x(size.x),
pos + Point::with_y(size.y),
pos + size.to_point(),
] {
let t = point.transform(fc.state().transform);
min_x.set_min(t.x);
min_y.set_min(t.y);
max_x.set_max(t.x);
max_y.set_max(t.y);
}
let x1 = min_x.to_f32();
let x2 = max_x.to_f32();
let y1 = min_y.to_f32();
let y2 = max_y.to_f32();
let rect = Rect::from_ltrb(x1, y1, x2, y2).unwrap();
let target = match dest {
Destination::Url(u) => {
Target::Action(Action::Link(LinkAction::new(u.to_string())))
}
Destination::Position(p) => {
// TODO: Ignore non-exported destinations
Target::Destination(krilla::destination::Destination::Xyz(
XyzDestination::new(p.page.get() - 1, p.point.as_krilla()),
))
}
// TODO: Implement
Destination::Location(_) => return,
};
fc.annotations.push(LinkAnnotation::new(rect, target).into());
}
pub fn handle_group( pub fn handle_group(
fc: &mut FrameContext, fc: &mut FrameContext,
group: &GroupItem, group: &GroupItem,
@ -390,8 +442,10 @@ pub fn handle_shape(
// Typst supports them, so we apply a transform if needed // Typst supports them, so we apply a transform if needed
// Because this operation is expensive according to tiny-skia's // Because this operation is expensive according to tiny-skia's
// docs, we prefer to not apply it if not needed // docs, we prefer to not apply it if not needed
let transform = krilla::geom::Transform::from_scale(w.signum(), h.signum()); let transform =
Rect::from_xywh(0.0, 0.0, w.abs(), h.abs()).and_then(|rect| rect.transform(transform)) krilla::geom::Transform::from_scale(w.signum(), h.signum());
Rect::from_xywh(0.0, 0.0, w.abs(), h.abs())
.and_then(|rect| rect.transform(transform))
} else { } else {
Rect::from_xywh(0.0, 0.0, w, h) Rect::from_xywh(0.0, 0.0, w, h)
}; };
@ -460,50 +514,3 @@ pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
} }
} }
} }
// fn handle_link(
// pos: typst_library::layout::Point,
// dest: &Destination,
// size: typst_library::layout::Size,
// ctx: &mut GlobalContext,
// surface: &mut Surface,
// ) {
// let mut min_x = Abs::inf();
// let mut min_y = Abs::inf();
// let mut max_x = -Abs::inf();
// let mut max_y = -Abs::inf();
//
// // Compute the bounding box of the transformed link.
// for point in [
// pos,
// pos + Point::with_x(size.x),
// pos + Point::with_y(size.y),
// pos + size.to_point(),
// ] {
// let t = point.transform(ctx.cur_transform);
// min_x.set_min(t.x);
// min_y.set_min(t.y);
// max_x.set_max(t.x);
// max_y.set_max(t.y);
// }
//
// let x1 = min_x.to_f32();
// let x2 = max_x.to_f32();
// let y1 = min_y.to_f32();
// let y2 = max_y.to_f32();
// let rect = krilla::geom::Rect::from_ltrb(x1, y1, x2, y2).unwrap();
//
// let target = match dest {
// Destination::Url(u) => {
// Target::Action(Action::Link(LinkAction::new(u.to_string())))
// }
// Destination::Position(p) => {
// Target::Destination(krilla::destination::Destination::Xyz(
// XyzDestination::new(p.page.get() - 1, p.point.as_krilla()),
// ))
// }
// Destination::Location(_) => return,
// };
//
// ctx.annotations.push(LinkAnnotation::new(rect, target).into());
// }

View File

@ -158,8 +158,13 @@ pub(crate) fn convert_pattern(
let transform = match pattern.unwrap_relative(on_text) { let transform = match pattern.unwrap_relative(on_text) {
RelativeTo::Self_ => Transform::identity(), RelativeTo::Self_ => Transform::identity(),
RelativeTo::Parent => transforms.transform_chain_.invert().unwrap().pre_concat(transforms.container_transform_chain) RelativeTo::Parent => transforms
}.as_krilla(); .transform_chain_
.invert()
.unwrap()
.pre_concat(transforms.container_transform_chain),
}
.as_krilla();
let mut stream_builder = surface.stream_builder(); let mut stream_builder = surface.stream_builder();
let mut surface = stream_builder.surface(); let mut surface = stream_builder.surface();