mirror of
https://github.com/typst/typst
synced 2025-06-08 13:16:24 +08:00
Add support for patterns
This commit is contained in:
parent
e1895fea7b
commit
01fd27c747
@ -25,8 +25,8 @@ use typst_library::visualize::{
|
|||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
use typst_utils::{Deferred, Numeric, SliceExt};
|
use typst_utils::{Deferred, Numeric, SliceExt};
|
||||||
|
|
||||||
use crate::color_old::PaintEncode;
|
|
||||||
use crate::color_font::ColorFontMap;
|
use crate::color_font::ColorFontMap;
|
||||||
|
use crate::color_old::PaintEncode;
|
||||||
use crate::extg_old::ExtGState;
|
use crate::extg_old::ExtGState;
|
||||||
use crate::image_old::deferred_image;
|
use crate::image_old::deferred_image;
|
||||||
use crate::resources_old::Resources;
|
use crate::resources_old::Resources;
|
||||||
|
@ -29,8 +29,8 @@ impl PdfImage {
|
|||||||
|
|
||||||
impl Hash for PdfImage {
|
impl Hash for PdfImage {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
/// `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`,
|
// `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`,
|
||||||
/// so this is enough. Since `raster` is prehashed, this is also very cheap.
|
// so this is enough. Since `raster` is prehashed, this is also very cheap.
|
||||||
self.raster.hash(state);
|
self.raster.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
use crate::content_old::Transforms;
|
||||||
|
use crate::primitive::{PointExt, SizeExt, TransformExt};
|
||||||
use crate::{paint, AbsExt};
|
use crate::{paint, AbsExt};
|
||||||
use bytemuck::TransparentWrapper;
|
use bytemuck::TransparentWrapper;
|
||||||
use image::{GenericImageView};
|
use image::GenericImageView;
|
||||||
use krilla::action::{Action, LinkAction};
|
use krilla::action::{Action, LinkAction};
|
||||||
use krilla::annotation::{LinkAnnotation, Target};
|
use krilla::annotation::{LinkAnnotation, Target};
|
||||||
use krilla::destination::XyzDestination;
|
use krilla::destination::XyzDestination;
|
||||||
use krilla::font::{GlyphId, GlyphUnits};
|
use krilla::font::{GlyphId, GlyphUnits};
|
||||||
use krilla::geom::{Point, Transform};
|
|
||||||
use krilla::path::PathBuilder;
|
use krilla::path::PathBuilder;
|
||||||
use krilla::surface::Surface;
|
use krilla::surface::Surface;
|
||||||
use krilla::validation::Validator;
|
use krilla::validation::Validator;
|
||||||
@ -16,49 +17,51 @@ 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, Size};
|
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::{FillRule, Geometry, Image, ImageKind, Paint, Path, PathItem, Shape};
|
use typst_library::visualize::{
|
||||||
use crate::content_old::Transforms;
|
FillRule, Geometry, Image, ImageKind, Paint, Path, PathItem, Shape,
|
||||||
use crate::primitive::{PointExt, SizeExt, TransformExt};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct State {
|
struct State {
|
||||||
/// The transform of the current item.
|
/// The transform of the current item.
|
||||||
transform: typst_library::layout::Transform,
|
transform: Transform,
|
||||||
/// The transform of first hard frame in the hierarchy.
|
/// The transform of first hard frame in the hierarchy.
|
||||||
container_transform: typst_library::layout::Transform,
|
container_transform: Transform,
|
||||||
/// The size of the first hard frame in the hierarchy.
|
/// The size of the first hard frame in the hierarchy.
|
||||||
size: Size,
|
size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
/// Creates a new, clean state for a given `size`.
|
/// Creates a new, clean state for a given `size`.
|
||||||
pub fn new(size: Size) -> Self {
|
fn new(size: Size) -> Self {
|
||||||
Self {
|
Self {
|
||||||
transform: typst_library::layout::Transform::identity(),
|
transform: Transform::identity(),
|
||||||
container_transform: typst_library::layout::Transform::identity(),
|
container_transform: Transform::identity(),
|
||||||
size,
|
size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: typst_library::layout::Transform) {
|
pub fn size(&mut self, size: Size) {
|
||||||
self.transform = self.transform.pre_concat(transform);
|
self.size = size;
|
||||||
if self.container_transform.is_identity() {
|
|
||||||
self.container_transform = self.transform;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn group_transform(&mut self, transform: typst_library::layout::Transform) {
|
pub fn transform(&mut self, transform: Transform) {
|
||||||
self.container_transform =
|
self.transform = self.transform.pre_concat(transform);
|
||||||
self.container_transform.pre_concat(transform);
|
}
|
||||||
|
|
||||||
|
fn set_container_transform(&mut self) {
|
||||||
|
self.container_transform = self.transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the [`Transforms`] structure for the current item.
|
/// Creates the [`Transforms`] structure for the current item.
|
||||||
pub fn transforms(&self, size: Size, pos: typst_library::layout::Point) -> Transforms {
|
pub fn transforms(&self, size: Size) -> Transforms {
|
||||||
Transforms {
|
Transforms {
|
||||||
transform: self.transform.pre_concat(typst_library::layout::Transform::translate(pos.x, pos.y)),
|
transform: self.transform,
|
||||||
container_transform: self.container_transform,
|
container_transform: self.container_transform,
|
||||||
container_size: self.size,
|
container_size: self.size,
|
||||||
size,
|
size,
|
||||||
@ -66,15 +69,13 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FrameContext {
|
pub(crate) struct FrameContext {
|
||||||
states: Vec<State>
|
states: Vec<State>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrameContext {
|
impl FrameContext {
|
||||||
pub fn new(size: Size) -> Self {
|
pub fn new(size: Size) -> Self {
|
||||||
Self {
|
Self { states: vec![State::new(size)] }
|
||||||
states: vec![State::new(size)],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self) {
|
pub fn push(&mut self) {
|
||||||
@ -89,7 +90,7 @@ impl FrameContext {
|
|||||||
self.states.last().unwrap()
|
self.states.last().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state_mut(&mut self) -> &State {
|
pub fn state_mut(&mut self) -> &mut State {
|
||||||
self.states.last_mut().unwrap()
|
self.states.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,17 +127,12 @@ impl krilla::font::Glyph for PdfGlyph {
|
|||||||
|
|
||||||
pub struct GlobalContext {
|
pub struct GlobalContext {
|
||||||
fonts: HashMap<Font, krilla::font::Font>,
|
fonts: HashMap<Font, krilla::font::Font>,
|
||||||
cur_transform: typst_library::layout::Transform,
|
|
||||||
annotations: Vec<krilla::annotation::Annotation>,
|
annotations: Vec<krilla::annotation::Annotation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalContext {
|
impl GlobalContext {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { fonts: Default::default(), annotations: vec![] }
|
||||||
fonts: Default::default(),
|
|
||||||
cur_transform: typst_library::layout::Transform::identity(),
|
|
||||||
annotations: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +161,15 @@ pub fn pdf(typst_document: &PagedDocument) -> Vec<u8> {
|
|||||||
);
|
);
|
||||||
let mut page = document.start_page_with(settings);
|
let mut page = document.start_page_with(settings);
|
||||||
let mut surface = page.surface();
|
let mut surface = page.surface();
|
||||||
process_frame(&typst_page.frame, &mut surface, &mut context);
|
let mut fc = FrameContext::new(typst_page.frame.size());
|
||||||
|
// println!("{:?}", &typst_page.frame);
|
||||||
|
process_frame(
|
||||||
|
&mut fc,
|
||||||
|
&typst_page.frame,
|
||||||
|
typst_page.fill_or_transparent(),
|
||||||
|
&mut surface,
|
||||||
|
&mut context,
|
||||||
|
);
|
||||||
surface.finish();
|
surface.finish();
|
||||||
|
|
||||||
let annotations = std::mem::take(&mut context.annotations);
|
let annotations = std::mem::take(&mut context.annotations);
|
||||||
@ -183,47 +187,66 @@ pub fn finish(document: krilla::Document) -> Vec<u8> {
|
|||||||
document.finish().unwrap()
|
document.finish().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_frame(frame: &Frame, fill: Option<Paint>, surface: &mut Surface, gc: &mut GlobalContext) {
|
pub fn process_frame(
|
||||||
let mut fc = FrameContext::new(frame.size());
|
fc: &mut FrameContext,
|
||||||
|
frame: &Frame,
|
||||||
|
fill: Option<Paint>,
|
||||||
|
surface: &mut Surface,
|
||||||
|
gc: &mut GlobalContext,
|
||||||
|
) {
|
||||||
|
fc.push();
|
||||||
|
|
||||||
|
if frame.kind().is_hard() {
|
||||||
|
fc.state_mut().set_container_transform();
|
||||||
|
fc.state_mut().size(frame.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(fill) = fill {
|
||||||
|
let shape = Geometry::Rect(frame.size()).filled(fill);
|
||||||
|
handle_shape(fc, &shape, surface, gc);
|
||||||
|
}
|
||||||
|
|
||||||
for (point, item) in frame.items() {
|
for (point, item) in frame.items() {
|
||||||
surface.push_transform(&Transform::from_translate(
|
fc.push();
|
||||||
point.x.to_f32(),
|
fc.state_mut().transform(Transform::translate(point.x, point.y));
|
||||||
point.y.to_f32(),
|
|
||||||
));
|
|
||||||
|
|
||||||
match item {
|
match item {
|
||||||
FrameItem::Group(g) => handle_group(g, surface, gc),
|
FrameItem::Group(g) => handle_group(fc, g, surface, gc),
|
||||||
FrameItem::Text(t) => handle_text(t, surface, gc),
|
FrameItem::Text(t) => handle_text(fc, t, surface, gc),
|
||||||
FrameItem::Shape(s, _) => handle_shape(s, surface),
|
FrameItem::Shape(s, _) => handle_shape(fc, s, surface, gc),
|
||||||
FrameItem::Image(image, size, span) => {
|
FrameItem::Image(image, size, span) => {
|
||||||
handle_image(image, *size, surface, gc)
|
handle_image(fc, image, *size, surface)
|
||||||
}
|
}
|
||||||
FrameItem::Link(d, s) => handle_link(*point, d, *s, gc, surface),
|
FrameItem::Link(d, s) => {}
|
||||||
FrameItem::Tag(_) => {}
|
FrameItem::Tag(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
surface.pop();
|
fc.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fc.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_group(
|
pub fn handle_group(
|
||||||
|
fc: &mut FrameContext,
|
||||||
group: &GroupItem,
|
group: &GroupItem,
|
||||||
surface: &mut Surface,
|
surface: &mut Surface,
|
||||||
context: &mut GlobalContext,
|
context: &mut GlobalContext,
|
||||||
) {
|
) {
|
||||||
let old = context.cur_transform;
|
fc.push();
|
||||||
context.cur_transform = context.cur_transform.pre_concat(group.transform);
|
fc.state_mut().transform(group.transform);
|
||||||
|
|
||||||
surface.push_transform(&group.transform.as_krilla());
|
process_frame(fc, &group.frame, None, surface, context);
|
||||||
process_frame(&group.frame, surface, context);
|
|
||||||
|
|
||||||
context.cur_transform = old;
|
fc.pop();
|
||||||
surface.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_text(t: &TextItem, surface: &mut Surface, context: &mut GlobalContext) {
|
pub fn handle_text(
|
||||||
let font = context
|
fc: &mut FrameContext,
|
||||||
|
t: &TextItem,
|
||||||
|
surface: &mut Surface,
|
||||||
|
gc: &mut GlobalContext,
|
||||||
|
) {
|
||||||
|
let font = gc
|
||||||
.fonts
|
.fonts
|
||||||
.entry(t.font.clone())
|
.entry(t.font.clone())
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
@ -232,14 +255,23 @@ pub fn handle_text(t: &TextItem, surface: &mut Surface, context: &mut GlobalCont
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
.clone();
|
.clone();
|
||||||
let fill = paint::fill(&t.fill, FillRule::NonZero);
|
let fill = paint::fill(
|
||||||
|
gc,
|
||||||
|
&t.fill,
|
||||||
|
FillRule::NonZero,
|
||||||
|
true,
|
||||||
|
surface,
|
||||||
|
fc.state().transforms(Size::zero()),
|
||||||
|
);
|
||||||
let text = t.text.as_str();
|
let text = t.text.as_str();
|
||||||
let size = t.size;
|
let size = t.size;
|
||||||
|
|
||||||
let glyphs: &[PdfGlyph] = TransparentWrapper::wrap_slice(t.glyphs.as_slice());
|
let glyphs: &[PdfGlyph] = TransparentWrapper::wrap_slice(t.glyphs.as_slice());
|
||||||
|
|
||||||
|
surface.push_transform(&fc.state().transform.as_krilla());
|
||||||
|
|
||||||
surface.fill_glyphs(
|
surface.fill_glyphs(
|
||||||
Point::from_xy(0.0, 0.0),
|
krilla::geom::Point::from_xy(0.0, 0.0),
|
||||||
fill,
|
fill,
|
||||||
&glyphs,
|
&glyphs,
|
||||||
font.clone(),
|
font.clone(),
|
||||||
@ -249,9 +281,13 @@ pub fn handle_text(t: &TextItem, surface: &mut Surface, context: &mut GlobalCont
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(stroke) = t.stroke.as_ref().map(paint::stroke) {
|
if let Some(stroke) = t
|
||||||
|
.stroke
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| paint::stroke(gc, s, true, surface, fc.state().transforms(Size::zero())))
|
||||||
|
{
|
||||||
surface.stroke_glyphs(
|
surface.stroke_glyphs(
|
||||||
Point::from_xy(0.0, 0.0),
|
krilla::geom::Point::from_xy(0.0, 0.0),
|
||||||
stroke,
|
stroke,
|
||||||
&glyphs,
|
&glyphs,
|
||||||
font.clone(),
|
font.clone(),
|
||||||
@ -261,14 +297,18 @@ pub fn handle_text(t: &TextItem, surface: &mut Surface, context: &mut GlobalCont
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
surface.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_image(
|
pub fn handle_image(
|
||||||
|
fc: &mut FrameContext,
|
||||||
image: &Image,
|
image: &Image,
|
||||||
size: Size,
|
size: Size,
|
||||||
surface: &mut Surface,
|
surface: &mut Surface,
|
||||||
_: &mut GlobalContext,
|
|
||||||
) {
|
) {
|
||||||
|
surface.push_transform(&fc.state().transform.as_krilla());
|
||||||
|
|
||||||
match image.kind() {
|
match image.kind() {
|
||||||
ImageKind::Raster(raster) => {
|
ImageKind::Raster(raster) => {
|
||||||
// TODO: Don't unwrap
|
// TODO: Don't unwrap
|
||||||
@ -276,12 +316,26 @@ pub fn handle_image(
|
|||||||
surface.draw_image(image, size.as_krilla());
|
surface.draw_image(image, size.as_krilla());
|
||||||
}
|
}
|
||||||
ImageKind::Svg(svg) => {
|
ImageKind::Svg(svg) => {
|
||||||
surface.draw_svg(svg.tree(), size.as_krilla(), SvgSettings::default());
|
surface.draw_svg(
|
||||||
|
svg.tree(),
|
||||||
|
size.as_krilla(),
|
||||||
|
SvgSettings {
|
||||||
|
embed_text: !svg.flatten_text(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
surface.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_shape(fc: &FrameContext, pos: Point, shape: &Shape, surface: &mut Surface) {
|
pub fn handle_shape(
|
||||||
|
fc: &mut FrameContext,
|
||||||
|
shape: &Shape,
|
||||||
|
surface: &mut Surface,
|
||||||
|
gc: &mut GlobalContext,
|
||||||
|
) {
|
||||||
let mut path_builder = PathBuilder::new();
|
let mut path_builder = PathBuilder::new();
|
||||||
|
|
||||||
match &shape.geometry {
|
match &shape.geometry {
|
||||||
@ -300,11 +354,17 @@ pub fn handle_shape(fc: &FrameContext, pos: Point, shape: &Shape, surface: &mut
|
|||||||
}
|
}
|
||||||
|
|
||||||
surface.push_transform(&fc.state().transform.as_krilla());
|
surface.push_transform(&fc.state().transform.as_krilla());
|
||||||
surface.push_transform(&Transform::from_translate(pos.x, pos.y));
|
|
||||||
|
|
||||||
if let Some(path) = path_builder.finish() {
|
if let Some(path) = path_builder.finish() {
|
||||||
if let Some(paint) = &shape.fill {
|
if let Some(paint) = &shape.fill {
|
||||||
let fill = paint::fill(paint, shape.fill_rule);
|
let fill = paint::fill(
|
||||||
|
gc,
|
||||||
|
&paint,
|
||||||
|
shape.fill_rule,
|
||||||
|
false,
|
||||||
|
surface,
|
||||||
|
fc.state().transforms(Size::zero()),
|
||||||
|
);
|
||||||
surface.fill_path(&path, fill);
|
surface.fill_path(&path, fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,13 +377,18 @@ pub fn handle_shape(fc: &FrameContext, pos: Point, shape: &Shape, surface: &mut
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(stroke) = &stroke {
|
if let Some(stroke) = &stroke {
|
||||||
let stroke = paint::stroke(stroke);
|
let stroke = paint::stroke(
|
||||||
|
gc,
|
||||||
|
stroke,
|
||||||
|
false,
|
||||||
|
surface,
|
||||||
|
fc.state().transforms(Size::zero()),
|
||||||
|
);
|
||||||
surface.stroke_path(&path, stroke);
|
surface.stroke_path(&path, stroke);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
surface.pop();
|
surface.pop();
|
||||||
surface.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
|
pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
|
||||||
@ -344,49 +409,49 @@ pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_link(
|
// fn handle_link(
|
||||||
pos: typst_library::layout::Point,
|
// pos: typst_library::layout::Point,
|
||||||
dest: &Destination,
|
// dest: &Destination,
|
||||||
size: typst_library::layout::Size,
|
// size: typst_library::layout::Size,
|
||||||
ctx: &mut GlobalContext,
|
// ctx: &mut GlobalContext,
|
||||||
surface: &mut Surface,
|
// surface: &mut Surface,
|
||||||
) {
|
// ) {
|
||||||
let mut min_x = Abs::inf();
|
// let mut min_x = Abs::inf();
|
||||||
let mut min_y = Abs::inf();
|
// let mut min_y = Abs::inf();
|
||||||
let mut max_x = -Abs::inf();
|
// let mut max_x = -Abs::inf();
|
||||||
let mut max_y = -Abs::inf();
|
// let mut max_y = -Abs::inf();
|
||||||
|
//
|
||||||
// Compute the bounding box of the transformed link.
|
// // Compute the bounding box of the transformed link.
|
||||||
for point in [
|
// for point in [
|
||||||
pos,
|
// pos,
|
||||||
pos + typst_library::layout::Point::with_x(size.x),
|
// pos + Point::with_x(size.x),
|
||||||
pos + typst_library::layout::Point::with_y(size.y),
|
// pos + Point::with_y(size.y),
|
||||||
pos + size.to_point(),
|
// pos + size.to_point(),
|
||||||
] {
|
// ] {
|
||||||
let t = point.transform(ctx.cur_transform);
|
// let t = point.transform(ctx.cur_transform);
|
||||||
min_x.set_min(t.x);
|
// min_x.set_min(t.x);
|
||||||
min_y.set_min(t.y);
|
// min_y.set_min(t.y);
|
||||||
max_x.set_max(t.x);
|
// max_x.set_max(t.x);
|
||||||
max_y.set_max(t.y);
|
// max_y.set_max(t.y);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let x1 = min_x.to_f32();
|
// let x1 = min_x.to_f32();
|
||||||
let x2 = max_x.to_f32();
|
// let x2 = max_x.to_f32();
|
||||||
let y1 = min_y.to_f32();
|
// let y1 = min_y.to_f32();
|
||||||
let y2 = max_y.to_f32();
|
// let y2 = max_y.to_f32();
|
||||||
let rect = krilla::geom::Rect::from_ltrb(x1, y1, x2, y2).unwrap();
|
// let rect = krilla::geom::Rect::from_ltrb(x1, y1, x2, y2).unwrap();
|
||||||
|
//
|
||||||
let target = match dest {
|
// let target = match dest {
|
||||||
Destination::Url(u) => {
|
// Destination::Url(u) => {
|
||||||
Target::Action(Action::Link(LinkAction::new(u.to_string())))
|
// Target::Action(Action::Link(LinkAction::new(u.to_string())))
|
||||||
}
|
// }
|
||||||
Destination::Position(p) => {
|
// Destination::Position(p) => {
|
||||||
Target::Destination(krilla::destination::Destination::Xyz(
|
// Target::Destination(krilla::destination::Destination::Xyz(
|
||||||
XyzDestination::new(p.page.get() - 1, p.point.as_krilla()),
|
// XyzDestination::new(p.page.get() - 1, p.point.as_krilla()),
|
||||||
))
|
// ))
|
||||||
}
|
// }
|
||||||
Destination::Location(_) => return,
|
// Destination::Location(_) => return,
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
ctx.annotations.push(LinkAnnotation::new(rect, target).into());
|
// ctx.annotations.push(LinkAnnotation::new(rect, target).into());
|
||||||
}
|
// }
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Exporting of Typst documents into PDFs.
|
//! Exporting of Typst documents into PDFs.
|
||||||
|
|
||||||
mod catalog_old;
|
mod catalog_old;
|
||||||
mod color_old;
|
|
||||||
mod color_font;
|
mod color_font;
|
||||||
|
mod color_old;
|
||||||
mod content_old;
|
mod content_old;
|
||||||
mod extg_old;
|
mod extg_old;
|
||||||
mod font_old;
|
mod font_old;
|
||||||
@ -35,8 +35,8 @@ use typst_syntax::Span;
|
|||||||
use typst_utils::Deferred;
|
use typst_utils::Deferred;
|
||||||
|
|
||||||
use crate::catalog_old::write_catalog;
|
use crate::catalog_old::write_catalog;
|
||||||
use crate::color_old::{alloc_color_functions_refs, ColorFunctionRefs};
|
|
||||||
use crate::color_font::{write_color_fonts, ColorFontSlice};
|
use crate::color_font::{write_color_fonts, ColorFontSlice};
|
||||||
|
use crate::color_old::{alloc_color_functions_refs, ColorFunctionRefs};
|
||||||
use crate::extg_old::{write_graphic_states, ExtGState};
|
use crate::extg_old::{write_graphic_states, ExtGState};
|
||||||
use crate::font_old::write_fonts;
|
use crate::font_old::write_fonts;
|
||||||
use crate::gradient_old::{write_gradients, PdfGradient};
|
use crate::gradient_old::{write_gradients, PdfGradient};
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
//! Convert paint types from typst to krilla.
|
//! Convert paint types from typst to krilla.
|
||||||
|
|
||||||
use std::num::NonZeroUsize;
|
use crate::content_old::Transforms;
|
||||||
|
use crate::krilla::{process_frame, FrameContext, GlobalContext};
|
||||||
|
use crate::primitive::{FillRuleExt, LineCapExt, LineJoinExt, TransformExt};
|
||||||
|
use crate::AbsExt;
|
||||||
use krilla::geom::NormalizedF32;
|
use krilla::geom::NormalizedF32;
|
||||||
use krilla::page::{NumberingStyle, PageLabel};
|
use krilla::page::{NumberingStyle, PageLabel};
|
||||||
use typst_library::layout::{Abs, Angle, Quadrant, Ratio, Transform};
|
use krilla::surface::Surface;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
use typst_library::layout::Abs;
|
||||||
use typst_library::model::Numbering;
|
use typst_library::model::Numbering;
|
||||||
use typst_library::visualize::{ColorSpace, DashPattern, FillRule, FixedStroke, Gradient, Paint, RelativeTo};
|
use typst_library::visualize::{
|
||||||
|
ColorSpace, DashPattern, FillRule, FixedStroke, Paint, Pattern, RelativeTo,
|
||||||
|
};
|
||||||
use typst_utils::Numeric;
|
use typst_utils::Numeric;
|
||||||
use crate::{content_old, AbsExt};
|
|
||||||
use crate::content_old::Transforms;
|
|
||||||
use crate::gradient_old::PdfGradient;
|
|
||||||
use crate::primitive::{FillRuleExt, LineCapExt, LineJoinExt};
|
|
||||||
|
|
||||||
pub(crate) fn fill(paint_: &Paint, fill_rule_: FillRule) -> krilla::path::Fill {
|
pub(crate) fn fill(
|
||||||
let (paint, opacity) = paint(paint_);
|
gc: &mut GlobalContext,
|
||||||
|
paint_: &Paint,
|
||||||
|
fill_rule_: FillRule,
|
||||||
|
on_text: bool,
|
||||||
|
surface: &mut Surface,
|
||||||
|
transforms: Transforms,
|
||||||
|
) -> krilla::path::Fill {
|
||||||
|
let (paint, opacity) = paint(gc, paint_, on_text, surface, transforms);
|
||||||
|
|
||||||
krilla::path::Fill {
|
krilla::path::Fill {
|
||||||
paint,
|
paint,
|
||||||
@ -22,8 +32,14 @@ pub(crate) fn fill(paint_: &Paint, fill_rule_: FillRule) -> krilla::path::Fill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn stroke(stroke: &FixedStroke) -> krilla::path::Stroke {
|
pub(crate) fn stroke(
|
||||||
let (paint, opacity) = paint(&stroke.paint);
|
fc: &mut GlobalContext,
|
||||||
|
stroke: &FixedStroke,
|
||||||
|
on_text: bool,
|
||||||
|
surface: &mut Surface,
|
||||||
|
transforms: Transforms,
|
||||||
|
) -> krilla::path::Stroke {
|
||||||
|
let (paint, opacity) = paint(fc, &stroke.paint, on_text, surface, transforms);
|
||||||
krilla::path::Stroke {
|
krilla::path::Stroke {
|
||||||
paint,
|
paint,
|
||||||
width: stroke.thickness.to_f32(),
|
width: stroke.thickness.to_f32(),
|
||||||
@ -42,7 +58,13 @@ fn dash(dash: &DashPattern<Abs, Abs>) -> krilla::path::StrokeDash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(paint: &Paint) -> (krilla::paint::Paint, u8) {
|
fn paint(
|
||||||
|
gc: &mut GlobalContext,
|
||||||
|
paint: &Paint,
|
||||||
|
on_text: bool,
|
||||||
|
surface: &mut Surface,
|
||||||
|
transforms: Transforms,
|
||||||
|
) -> (krilla::paint::Paint, u8) {
|
||||||
match paint {
|
match paint {
|
||||||
Paint::Solid(c) => {
|
Paint::Solid(c) => {
|
||||||
let components = c.to_space(ColorSpace::Srgb).to_vec4_u8();
|
let components = c.to_space(ColorSpace::Srgb).to_vec4_u8();
|
||||||
@ -57,7 +79,7 @@ fn paint(paint: &Paint) -> (krilla::paint::Paint, u8) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Paint::Gradient(_) => (krilla::color::rgb::Color::black().into(), 255),
|
Paint::Gradient(_) => (krilla::color::rgb::Color::black().into(), 255),
|
||||||
Paint::Pattern(_) => (krilla::color::rgb::Color::black().into(), 255),
|
Paint::Pattern(p) => convert_pattern(gc, p, on_text, surface, transforms),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,8 +102,8 @@ impl PageLabelExt for PageLabel {
|
|||||||
// If there is a suffix, we cannot use the common style optimisation,
|
// If there is a suffix, we cannot use the common style optimisation,
|
||||||
// since PDF does not provide a suffix field.
|
// since PDF does not provide a suffix field.
|
||||||
let style = if pat.suffix.is_empty() {
|
let style = if pat.suffix.is_empty() {
|
||||||
use typst_library::model::NumberingKind as Kind;
|
|
||||||
use krilla::page::NumberingStyle as Style;
|
use krilla::page::NumberingStyle as Style;
|
||||||
|
use typst_library::model::NumberingKind as Kind;
|
||||||
match kind {
|
match kind {
|
||||||
Kind::Arabic => Some(Style::Arabic),
|
Kind::Arabic => Some(Style::Arabic),
|
||||||
Kind::LowerRoman => Some(Style::LowerRoman),
|
Kind::LowerRoman => Some(Style::LowerRoman),
|
||||||
@ -116,13 +138,13 @@ impl PageLabelExt for PageLabel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Anti-aliasing
|
pub(crate) fn convert_pattern(
|
||||||
|
gc: &mut GlobalContext,
|
||||||
fn convert_gradient(
|
pattern: &Pattern,
|
||||||
gradient: &Gradient,
|
|
||||||
on_text: bool,
|
on_text: bool,
|
||||||
|
surface: &mut Surface,
|
||||||
mut transforms: Transforms,
|
mut transforms: Transforms,
|
||||||
) -> usize {
|
) -> (krilla::paint::Paint, u8) {
|
||||||
// Edge cases for strokes.
|
// Edge cases for strokes.
|
||||||
if transforms.size.x.is_zero() {
|
if transforms.size.x.is_zero() {
|
||||||
transforms.size.x = Abs::pt(1.0);
|
transforms.size.x = Abs::pt(1.0);
|
||||||
@ -131,52 +153,82 @@ fn convert_gradient(
|
|||||||
if transforms.size.y.is_zero() {
|
if transforms.size.y.is_zero() {
|
||||||
transforms.size.y = Abs::pt(1.0);
|
transforms.size.y = Abs::pt(1.0);
|
||||||
}
|
}
|
||||||
let size = match gradient.unwrap_relative(on_text) {
|
|
||||||
RelativeTo::Self_ => transforms.size,
|
|
||||||
RelativeTo::Parent => transforms.container_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
let rotation = gradient.angle().unwrap_or_else(Angle::zero);
|
let transform = surface.ctm().invert().unwrap().pre_concat(
|
||||||
|
match pattern.unwrap_relative(on_text) {
|
||||||
let transform = match gradient.unwrap_relative(on_text) {
|
RelativeTo::Self_ => transforms.transform,
|
||||||
RelativeTo::Self_ => transforms.transform,
|
RelativeTo::Parent => transforms.container_transform,
|
||||||
RelativeTo::Parent => transforms.container_transform,
|
|
||||||
};
|
|
||||||
|
|
||||||
let scale_offset = match gradient {
|
|
||||||
Gradient::Conic(_) => 4.0_f64,
|
|
||||||
_ => 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let transform = transform
|
|
||||||
.pre_concat(Transform::translate(
|
|
||||||
offset_x * scale_offset,
|
|
||||||
offset_y * scale_offset,
|
|
||||||
))
|
|
||||||
.pre_concat(Transform::scale(
|
|
||||||
Ratio::new(size.x.to_pt() * scale_offset),
|
|
||||||
Ratio::new(size.y.to_pt() * scale_offset),
|
|
||||||
));
|
|
||||||
|
|
||||||
let angle = Gradient::correct_aspect_ratio(rotation, size.aspect_ratio());
|
|
||||||
|
|
||||||
match &gradient {
|
|
||||||
Gradient::Linear(_) => {
|
|
||||||
let (mut sin, mut cos) = (angle.sin(), angle.cos());
|
|
||||||
|
|
||||||
// Scale to edges of unit square.
|
|
||||||
let factor = cos.abs() + sin.abs();
|
|
||||||
sin *= factor;
|
|
||||||
cos *= factor;
|
|
||||||
|
|
||||||
let (x1, y1, x2, y2): (f64, f64, f64, f64) = match angle.quadrant() {
|
|
||||||
Quadrant::First => (0.0, 0.0, cos, sin),
|
|
||||||
Quadrant::Second => (1.0, 0.0, cos + 1.0, sin),
|
|
||||||
Quadrant::Third => (1.0, 1.0, cos + 1.0, sin + 1.0),
|
|
||||||
Quadrant::Fourth => (0.0, 1.0, cos, sin + 1.0),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Gradient::Radial(_) => {}
|
.as_krilla(),
|
||||||
Gradient::Conic(_) => {}
|
);
|
||||||
}
|
|
||||||
|
let mut stream_builder = surface.stream_builder();
|
||||||
|
let mut surface = stream_builder.surface();
|
||||||
|
let mut fc = FrameContext::new(pattern.frame().size());
|
||||||
|
process_frame(&mut fc, pattern.frame(), None, &mut surface, gc);
|
||||||
|
surface.finish();
|
||||||
|
let stream = stream_builder.finish();
|
||||||
|
let pattern = krilla::paint::Pattern {
|
||||||
|
stream,
|
||||||
|
transform,
|
||||||
|
width: (pattern.size().x + pattern.spacing().x).to_pt() as _,
|
||||||
|
height: (pattern.size().y + pattern.spacing().y).to_pt() as _,
|
||||||
|
};
|
||||||
|
|
||||||
|
(pattern.into(), 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Anti-aliasing
|
||||||
|
|
||||||
|
// fn convert_gradient(
|
||||||
|
// gradient: &Gradient,
|
||||||
|
// on_text: bool,
|
||||||
|
// mut transforms: Transforms,
|
||||||
|
// ) -> usize {
|
||||||
|
// // Edge cases for strokes.
|
||||||
|
// if transforms.size.x.is_zero() {
|
||||||
|
// transforms.size.x = Abs::pt(1.0);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if transforms.size.y.is_zero() {
|
||||||
|
// transforms.size.y = Abs::pt(1.0);
|
||||||
|
// }
|
||||||
|
// let size = match gradient.unwrap_relative(on_text) {
|
||||||
|
// RelativeTo::Self_ => transforms.size,
|
||||||
|
// RelativeTo::Parent => transforms.container_size,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let rotation = gradient.angle().unwrap_or_else(Angle::zero);
|
||||||
|
//
|
||||||
|
// let transform = match gradient.unwrap_relative(on_text) {
|
||||||
|
// RelativeTo::Self_ => transforms.transform,
|
||||||
|
// RelativeTo::Parent => transforms.container_transform,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let scale_offset = match gradient {
|
||||||
|
// Gradient::Conic(_) => 4.0_f64,
|
||||||
|
// _ => 1.0,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// let angle = Gradient::correct_aspect_ratio(rotation, size.aspect_ratio());
|
||||||
|
//
|
||||||
|
// match &gradient {
|
||||||
|
// Gradient::Linear(_) => {
|
||||||
|
// let (mut sin, mut cos) = (angle.sin(), angle.cos());
|
||||||
|
//
|
||||||
|
// // Scale to edges of unit square.
|
||||||
|
// let factor = cos.abs() + sin.abs();
|
||||||
|
// sin *= factor;
|
||||||
|
// cos *= factor;
|
||||||
|
//
|
||||||
|
// let (x1, y1, x2, y2): (f64, f64, f64, f64) = match angle.quadrant() {
|
||||||
|
// Quadrant::First => (0.0, 0.0, cos, sin),
|
||||||
|
// Quadrant::Second => (1.0, 0.0, cos + 1.0, sin),
|
||||||
|
// Quadrant::Third => (1.0, 1.0, cos + 1.0, sin + 1.0),
|
||||||
|
// Quadrant::Fourth => (0.0, 1.0, cos, sin + 1.0),
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// Gradient::Radial(_) => {}
|
||||||
|
// Gradient::Conic(_) => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
@ -18,8 +18,8 @@ use typst_library::visualize::Image;
|
|||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
use typst_utils::Deferred;
|
use typst_utils::Deferred;
|
||||||
|
|
||||||
use crate::color_old::ColorSpaces;
|
|
||||||
use crate::color_font::ColorFontMap;
|
use crate::color_font::ColorFontMap;
|
||||||
|
use crate::color_old::ColorSpaces;
|
||||||
use crate::extg_old::ExtGState;
|
use crate::extg_old::ExtGState;
|
||||||
use crate::gradient_old::PdfGradient;
|
use crate::gradient_old::PdfGradient;
|
||||||
use crate::image_old::EncodedImage;
|
use crate::image_old::EncodedImage;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user