Add support for patterns

This commit is contained in:
Laurenz Stampfl 2024-12-14 22:37:59 +01:00
parent e1895fea7b
commit 01fd27c747
7 changed files with 302 additions and 185 deletions

View File

@ -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;

View File

@ -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);
} }
} }

View File

@ -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());
} // }

View File

@ -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};

View File

@ -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(_) => {}
// }
// }

View File

@ -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;