mirror of
https://github.com/typst/typst
synced 2025-06-08 13:16:24 +08:00
more
This commit is contained in:
parent
4ad90c1f38
commit
e1895fea7b
@ -1,12 +1,11 @@
|
|||||||
use crate::{paint, primitive, AbsExt};
|
use crate::{paint, AbsExt};
|
||||||
use bytemuck::TransparentWrapper;
|
use bytemuck::TransparentWrapper;
|
||||||
use image::{DynamicImage, GenericImageView, Rgba};
|
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::geom::{Point, Transform};
|
||||||
use krilla::image::{BitsPerComponent, CustomImage, ImageColorspace};
|
|
||||||
use krilla::path::PathBuilder;
|
use krilla::path::PathBuilder;
|
||||||
use krilla::surface::Surface;
|
use krilla::surface::Surface;
|
||||||
use krilla::validation::Validator;
|
use krilla::validation::Validator;
|
||||||
@ -15,17 +14,86 @@ use krilla::{PageSettings, SerializeSettings, SvgSettings};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::Arc;
|
||||||
use svg2pdf::usvg::Rect;
|
use svg2pdf::usvg::Rect;
|
||||||
use typst_library::layout::{Abs, Frame, FrameItem, GroupItem, Page, Size};
|
use typst_library::layout::{Abs, Frame, FrameItem, GroupItem, PagedDocument, Size};
|
||||||
use typst_library::model::{Destination, Document};
|
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::{FillRule, Geometry, Image, ImageKind, Paint, Path, PathItem, Shape};
|
||||||
FillRule, Geometry, Image, ImageKind, Path, PathItem, RasterFormat, RasterImage,
|
use crate::content_old::Transforms;
|
||||||
Shape,
|
|
||||||
};
|
|
||||||
use crate::primitive::{PointExt, SizeExt, TransformExt};
|
use crate::primitive::{PointExt, SizeExt, TransformExt};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct State {
|
||||||
|
/// The transform of the current item.
|
||||||
|
transform: typst_library::layout::Transform,
|
||||||
|
/// The transform of first hard frame in the hierarchy.
|
||||||
|
container_transform: typst_library::layout::Transform,
|
||||||
|
/// The size of the first hard frame in the hierarchy.
|
||||||
|
size: Size,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
/// Creates a new, clean state for a given `size`.
|
||||||
|
pub fn new(size: Size) -> Self {
|
||||||
|
Self {
|
||||||
|
transform: typst_library::layout::Transform::identity(),
|
||||||
|
container_transform: typst_library::layout::Transform::identity(),
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform(&mut self, transform: typst_library::layout::Transform) {
|
||||||
|
self.transform = self.transform.pre_concat(transform);
|
||||||
|
if self.container_transform.is_identity() {
|
||||||
|
self.container_transform = self.transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group_transform(&mut self, transform: typst_library::layout::Transform) {
|
||||||
|
self.container_transform =
|
||||||
|
self.container_transform.pre_concat(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the [`Transforms`] structure for the current item.
|
||||||
|
pub fn transforms(&self, size: Size, pos: typst_library::layout::Point) -> Transforms {
|
||||||
|
Transforms {
|
||||||
|
transform: self.transform.pre_concat(typst_library::layout::Transform::translate(pos.x, pos.y)),
|
||||||
|
container_transform: self.container_transform,
|
||||||
|
container_size: self.size,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FrameContext {
|
||||||
|
states: Vec<State>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameContext {
|
||||||
|
pub fn new(size: Size) -> Self {
|
||||||
|
Self {
|
||||||
|
states: vec![State::new(size)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self) {
|
||||||
|
self.states.push(self.states.last().unwrap().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) {
|
||||||
|
self.states.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state(&self) -> &State {
|
||||||
|
self.states.last().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state_mut(&mut self) -> &State {
|
||||||
|
self.states.last_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(TransparentWrapper)]
|
#[derive(TransparentWrapper)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct PdfGlyph(Glyph);
|
struct PdfGlyph(Glyph);
|
||||||
@ -56,13 +124,13 @@ impl krilla::font::Glyph for PdfGlyph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExportContext {
|
pub struct GlobalContext {
|
||||||
fonts: HashMap<Font, krilla::font::Font>,
|
fonts: HashMap<Font, krilla::font::Font>,
|
||||||
cur_transform: typst_library::layout::Transform,
|
cur_transform: typst_library::layout::Transform,
|
||||||
annotations: Vec<krilla::annotation::Annotation>,
|
annotations: Vec<krilla::annotation::Annotation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExportContext {
|
impl GlobalContext {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fonts: Default::default(),
|
fonts: Default::default(),
|
||||||
@ -75,7 +143,7 @@ impl ExportContext {
|
|||||||
// TODO: Change rustybuzz cluster behavior so it works with ActualText
|
// TODO: Change rustybuzz cluster behavior so it works with ActualText
|
||||||
|
|
||||||
#[typst_macros::time(name = "write pdf")]
|
#[typst_macros::time(name = "write pdf")]
|
||||||
pub fn pdf(typst_document: &Document) -> Vec<u8> {
|
pub fn pdf(typst_document: &PagedDocument) -> Vec<u8> {
|
||||||
let settings = SerializeSettings {
|
let settings = SerializeSettings {
|
||||||
compress_content_streams: true,
|
compress_content_streams: true,
|
||||||
no_device_cs: false,
|
no_device_cs: false,
|
||||||
@ -88,7 +156,7 @@ pub fn pdf(typst_document: &Document) -> Vec<u8> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut document = krilla::Document::new_with(settings);
|
let mut document = krilla::Document::new_with(settings);
|
||||||
let mut context = ExportContext::new();
|
let mut context = GlobalContext::new();
|
||||||
|
|
||||||
for typst_page in &typst_document.pages {
|
for typst_page in &typst_document.pages {
|
||||||
let settings = PageSettings::new(
|
let settings = PageSettings::new(
|
||||||
@ -115,10 +183,34 @@ 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) {
|
||||||
|
let mut fc = FrameContext::new(frame.size());
|
||||||
|
|
||||||
|
for (point, item) in frame.items() {
|
||||||
|
surface.push_transform(&Transform::from_translate(
|
||||||
|
point.x.to_f32(),
|
||||||
|
point.y.to_f32(),
|
||||||
|
));
|
||||||
|
|
||||||
|
match item {
|
||||||
|
FrameItem::Group(g) => handle_group(g, surface, gc),
|
||||||
|
FrameItem::Text(t) => handle_text(t, surface, gc),
|
||||||
|
FrameItem::Shape(s, _) => handle_shape(s, surface),
|
||||||
|
FrameItem::Image(image, size, span) => {
|
||||||
|
handle_image(image, *size, surface, gc)
|
||||||
|
}
|
||||||
|
FrameItem::Link(d, s) => handle_link(*point, d, *s, gc, surface),
|
||||||
|
FrameItem::Tag(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
surface.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_group(
|
pub fn handle_group(
|
||||||
group: &GroupItem,
|
group: &GroupItem,
|
||||||
surface: &mut Surface,
|
surface: &mut Surface,
|
||||||
context: &mut ExportContext,
|
context: &mut GlobalContext,
|
||||||
) {
|
) {
|
||||||
let old = context.cur_transform;
|
let old = context.cur_transform;
|
||||||
context.cur_transform = context.cur_transform.pre_concat(group.transform);
|
context.cur_transform = context.cur_transform.pre_concat(group.transform);
|
||||||
@ -130,7 +222,7 @@ pub fn handle_group(
|
|||||||
surface.pop();
|
surface.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_text(t: &TextItem, surface: &mut Surface, context: &mut ExportContext) {
|
pub fn handle_text(t: &TextItem, surface: &mut Surface, context: &mut GlobalContext) {
|
||||||
let font = context
|
let font = context
|
||||||
.fonts
|
.fonts
|
||||||
.entry(t.font.clone())
|
.entry(t.font.clone())
|
||||||
@ -175,7 +267,7 @@ pub fn handle_image(
|
|||||||
image: &Image,
|
image: &Image,
|
||||||
size: Size,
|
size: Size,
|
||||||
surface: &mut Surface,
|
surface: &mut Surface,
|
||||||
_: &mut ExportContext,
|
_: &mut GlobalContext,
|
||||||
) {
|
) {
|
||||||
match image.kind() {
|
match image.kind() {
|
||||||
ImageKind::Raster(raster) => {
|
ImageKind::Raster(raster) => {
|
||||||
@ -189,7 +281,7 @@ pub fn handle_image(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_shape(shape: &Shape, surface: &mut Surface) {
|
pub fn handle_shape(fc: &FrameContext, pos: Point, shape: &Shape, surface: &mut Surface) {
|
||||||
let mut path_builder = PathBuilder::new();
|
let mut path_builder = PathBuilder::new();
|
||||||
|
|
||||||
match &shape.geometry {
|
match &shape.geometry {
|
||||||
@ -207,17 +299,31 @@ pub fn handle_shape(shape: &Shape, surface: &mut Surface) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(paint, shape.fill_rule);
|
||||||
surface.fill_path(&path, fill);
|
surface.fill_path(&path, fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stroke) = &shape.stroke {
|
let stroke = shape.stroke.as_ref().and_then(|stroke| {
|
||||||
|
if stroke.thickness.to_f32() > 0.0 {
|
||||||
|
Some(stroke)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(stroke) = &stroke {
|
||||||
let stroke = paint::stroke(stroke);
|
let stroke = paint::stroke(stroke);
|
||||||
surface.stroke_path(&path, stroke);
|
surface.stroke_path(&path, stroke);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
surface.pop();
|
||||||
|
surface.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
|
pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
|
||||||
@ -238,33 +344,11 @@ pub fn convert_path(path: &Path, builder: &mut PathBuilder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_frame(frame: &Frame, surface: &mut Surface, context: &mut ExportContext) {
|
|
||||||
for (point, item) in frame.items() {
|
|
||||||
surface.push_transform(&Transform::from_translate(
|
|
||||||
point.x.to_f32(),
|
|
||||||
point.y.to_f32(),
|
|
||||||
));
|
|
||||||
|
|
||||||
match item {
|
|
||||||
FrameItem::Group(g) => handle_group(g, surface, context),
|
|
||||||
FrameItem::Text(t) => handle_text(t, surface, context),
|
|
||||||
FrameItem::Shape(s, _) => handle_shape(s, surface),
|
|
||||||
FrameItem::Image(image, size, span) => {
|
|
||||||
handle_image(image, *size, surface, context)
|
|
||||||
}
|
|
||||||
FrameItem::Link(d, s) => handle_link(*point, d, *s, context, surface),
|
|
||||||
FrameItem::Tag(_) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
surface.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ExportContext,
|
ctx: &mut GlobalContext,
|
||||||
surface: &mut Surface,
|
surface: &mut Surface,
|
||||||
) {
|
) {
|
||||||
let mut min_x = Abs::inf();
|
let mut min_x = Abs::inf();
|
||||||
|
@ -28,8 +28,7 @@ use pdf_writer::{Chunk, Name, Pdf, Ref, Str, TextStr};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typst_library::diag::{bail, SourceResult, StrResult};
|
use typst_library::diag::{bail, SourceResult, StrResult};
|
||||||
use typst_library::foundations::{Datetime, Smart};
|
use typst_library::foundations::{Datetime, Smart};
|
||||||
use typst_library::layout::{Abs, Em, PageRanges, Transform};
|
use typst_library::layout::{Abs, Em, PageRanges, PagedDocument, Transform};
|
||||||
use typst_library::model::Document;
|
|
||||||
use typst_library::text::Font;
|
use typst_library::text::Font;
|
||||||
use typst_library::visualize::Image;
|
use typst_library::visualize::Image;
|
||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
@ -53,7 +52,7 @@ use crate::resources_old::{
|
|||||||
///
|
///
|
||||||
/// Returns the raw bytes making up the PDF file.
|
/// Returns the raw bytes making up the PDF file.
|
||||||
#[typst_macros::time(name = "pdf")]
|
#[typst_macros::time(name = "pdf")]
|
||||||
pub fn pdf(document: &Document, options: &PdfOptions) -> SourceResult<Vec<u8>> {
|
pub fn pdf(document: &PagedDocument, options: &PdfOptions) -> SourceResult<Vec<u8>> {
|
||||||
return Ok(krilla::pdf(document));
|
return Ok(krilla::pdf(document));
|
||||||
PdfBuilder::new(document, options)
|
PdfBuilder::new(document, options)
|
||||||
.phase(|builder| builder.run(traverse_pages))?
|
.phase(|builder| builder.run(traverse_pages))?
|
||||||
@ -181,7 +180,7 @@ struct PdfBuilder<S> {
|
|||||||
/// this phase.
|
/// this phase.
|
||||||
struct WithDocument<'a> {
|
struct WithDocument<'a> {
|
||||||
/// The Typst document that is exported.
|
/// The Typst document that is exported.
|
||||||
document: &'a Document,
|
document: &'a PagedDocument,
|
||||||
/// Settings for PDF export.
|
/// Settings for PDF export.
|
||||||
options: &'a PdfOptions<'a>,
|
options: &'a PdfOptions<'a>,
|
||||||
}
|
}
|
||||||
@ -191,7 +190,7 @@ struct WithDocument<'a> {
|
|||||||
///
|
///
|
||||||
/// This phase allocates some global references.
|
/// This phase allocates some global references.
|
||||||
struct WithResources<'a> {
|
struct WithResources<'a> {
|
||||||
document: &'a Document,
|
document: &'a PagedDocument,
|
||||||
options: &'a PdfOptions<'a>,
|
options: &'a PdfOptions<'a>,
|
||||||
/// The content of the pages encoded as PDF content streams.
|
/// The content of the pages encoded as PDF content streams.
|
||||||
///
|
///
|
||||||
@ -240,7 +239,7 @@ impl<'a> From<(WithDocument<'a>, (Vec<Option<EncodedPage>>, Resources<()>))>
|
|||||||
/// We are now writing objects corresponding to resources, and giving them references,
|
/// We are now writing objects corresponding to resources, and giving them references,
|
||||||
/// that will be collected in [`References`].
|
/// that will be collected in [`References`].
|
||||||
struct WithGlobalRefs<'a> {
|
struct WithGlobalRefs<'a> {
|
||||||
document: &'a Document,
|
document: &'a PagedDocument,
|
||||||
options: &'a PdfOptions<'a>,
|
options: &'a PdfOptions<'a>,
|
||||||
pages: Vec<Option<EncodedPage>>,
|
pages: Vec<Option<EncodedPage>>,
|
||||||
/// Resources are the same as in previous phases, but each dictionary now has a reference.
|
/// Resources are the same as in previous phases, but each dictionary now has a reference.
|
||||||
@ -283,7 +282,7 @@ struct References {
|
|||||||
/// tree is going to be written, and given a reference. It is also at this point that
|
/// tree is going to be written, and given a reference. It is also at this point that
|
||||||
/// the page contents is actually written.
|
/// the page contents is actually written.
|
||||||
struct WithRefs<'a> {
|
struct WithRefs<'a> {
|
||||||
document: &'a Document,
|
document: &'a PagedDocument,
|
||||||
options: &'a PdfOptions<'a>,
|
options: &'a PdfOptions<'a>,
|
||||||
globals: GlobalRefs,
|
globals: GlobalRefs,
|
||||||
pages: Vec<Option<EncodedPage>>,
|
pages: Vec<Option<EncodedPage>>,
|
||||||
@ -309,7 +308,7 @@ impl<'a> From<(WithGlobalRefs<'a>, References)> for WithRefs<'a> {
|
|||||||
///
|
///
|
||||||
/// Each sub-resource gets its own isolated resource dictionary.
|
/// Each sub-resource gets its own isolated resource dictionary.
|
||||||
struct WithEverything<'a> {
|
struct WithEverything<'a> {
|
||||||
document: &'a Document,
|
document: &'a PagedDocument,
|
||||||
options: &'a PdfOptions<'a>,
|
options: &'a PdfOptions<'a>,
|
||||||
globals: GlobalRefs,
|
globals: GlobalRefs,
|
||||||
pages: Vec<Option<EncodedPage>>,
|
pages: Vec<Option<EncodedPage>>,
|
||||||
@ -341,7 +340,7 @@ impl<'a> From<(WithRefs<'a>, Ref)> for WithEverything<'a> {
|
|||||||
|
|
||||||
impl<'a> PdfBuilder<WithDocument<'a>> {
|
impl<'a> PdfBuilder<WithDocument<'a>> {
|
||||||
/// Start building a PDF for a Typst document.
|
/// Start building a PDF for a Typst document.
|
||||||
fn new(document: &'a Document, options: &'a PdfOptions<'a>) -> Self {
|
fn new(document: &'a PagedDocument, options: &'a PdfOptions<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
alloc: Ref::new(1),
|
alloc: Ref::new(1),
|
||||||
pdf: Pdf::new(),
|
pdf: Pdf::new(),
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use krilla::geom::NormalizedF32;
|
use krilla::geom::NormalizedF32;
|
||||||
use krilla::page::{NumberingStyle, PageLabel};
|
use krilla::page::{NumberingStyle, PageLabel};
|
||||||
use typst_library::layout::Abs;
|
use typst_library::layout::{Abs, Angle, Quadrant, Ratio, Transform};
|
||||||
use typst_library::model::Numbering;
|
use typst_library::model::Numbering;
|
||||||
use typst_library::visualize::{ColorSpace, DashPattern, FillRule, FixedStroke, Paint};
|
use typst_library::visualize::{ColorSpace, DashPattern, FillRule, FixedStroke, Gradient, Paint, RelativeTo};
|
||||||
|
use typst_utils::Numeric;
|
||||||
use crate::AbsExt;
|
use crate::{content_old, AbsExt};
|
||||||
|
use crate::content_old::Transforms;
|
||||||
|
use crate::gradient_old::PdfGradient;
|
||||||
use crate::primitive::{FillRuleExt, LineCapExt, LineJoinExt};
|
use crate::primitive::{FillRuleExt, LineCapExt, LineJoinExt};
|
||||||
|
|
||||||
pub(crate) fn fill(paint_: &Paint, fill_rule_: FillRule) -> krilla::path::Fill {
|
pub(crate) fn fill(paint_: &Paint, fill_rule_: FillRule) -> krilla::path::Fill {
|
||||||
@ -113,3 +115,68 @@ impl PageLabelExt for PageLabel {
|
|||||||
PageLabel::new(Some(NumberingStyle::Arabic), None, NonZeroUsize::new(number))
|
PageLabel::new(Some(NumberingStyle::Arabic), None, NonZeroUsize::new(number))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 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(_) => {}
|
||||||
|
Gradient::Conic(_) => {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user