mirror of
https://github.com/typst/typst
synced 2025-08-15 07:28:32 +08:00
Reformat + add support for exif
This commit is contained in:
parent
c990ced644
commit
8f0cb71db8
@ -3,17 +3,17 @@ use std::hash::{Hash, Hasher};
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::diag::{bail, StrResult};
|
||||
use crate::foundations::{cast, dict, Bytes, Cast, Dict, Smart, Value};
|
||||
use ecow::{eco_format, EcoString};
|
||||
use image::codecs::gif::GifDecoder;
|
||||
use image::codecs::jpeg::JpegDecoder;
|
||||
use image::codecs::png::PngDecoder;
|
||||
use image::imageops::rotate180;
|
||||
use image::{
|
||||
guess_format, DynamicImage, ImageBuffer, ImageDecoder, ImageResult, Limits, Pixel,
|
||||
};
|
||||
|
||||
use crate::diag::{bail, StrResult};
|
||||
use crate::foundations::{cast, dict, Bytes, Cast, Dict, Smart, Value};
|
||||
|
||||
/// A decoded raster image.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct RasterImage(Arc<Repr>);
|
||||
@ -23,7 +23,7 @@ struct Repr {
|
||||
data: Bytes,
|
||||
format: RasterFormat,
|
||||
dynamic: Arc<DynamicImage>,
|
||||
is_rotated: bool,
|
||||
exif_rotation: Option<u32>,
|
||||
icc: Option<Bytes>,
|
||||
dpi: Option<f64>,
|
||||
}
|
||||
@ -51,7 +51,7 @@ impl RasterImage {
|
||||
format: RasterFormat,
|
||||
icc: Smart<Bytes>,
|
||||
) -> StrResult<RasterImage> {
|
||||
let mut is_rotated = false;
|
||||
let mut exif_rot = None;
|
||||
|
||||
let (dynamic, icc, dpi) = match format {
|
||||
RasterFormat::Exchange(format) => {
|
||||
@ -88,7 +88,7 @@ impl RasterImage {
|
||||
// Apply rotation from EXIF metadata.
|
||||
if let Some(rotation) = exif.as_ref().and_then(exif_rotation) {
|
||||
apply_rotation(&mut dynamic, rotation);
|
||||
is_rotated = rotation != 1;
|
||||
exif_rot = Some(rotation);
|
||||
}
|
||||
|
||||
// Extract pixel density.
|
||||
@ -143,7 +143,7 @@ impl RasterImage {
|
||||
Ok(Self(Arc::new(Repr {
|
||||
data,
|
||||
format,
|
||||
is_rotated,
|
||||
exif_rotation: exif_rot,
|
||||
dynamic: Arc::new(dynamic),
|
||||
icc,
|
||||
dpi,
|
||||
@ -170,9 +170,9 @@ impl RasterImage {
|
||||
self.dynamic().height()
|
||||
}
|
||||
|
||||
/// Whether the image has been rotated due to EXIF metadata.
|
||||
pub fn is_rotated(&self) -> bool {
|
||||
self.0.is_rotated
|
||||
/// TODO.
|
||||
pub fn exif_rotation(&self) -> Option<u32> {
|
||||
self.0.exif_rotation
|
||||
}
|
||||
|
||||
/// The image's pixel density in pixels per inch, if known.
|
||||
|
@ -3,13 +3,13 @@ use std::num::NonZeroU64;
|
||||
|
||||
use ecow::EcoVec;
|
||||
use krilla::error::KrillaError;
|
||||
use krilla::interactive::annotation::Annotation;
|
||||
use krilla::interactive::destination::{NamedDestination, XyzDestination};
|
||||
use krilla::interchange::embed::EmbedError;
|
||||
use krilla::page::PageLabel;
|
||||
use krilla::path::PathBuilder;
|
||||
use krilla::surface::Surface;
|
||||
use krilla::{Configuration, Document, PageSettings, SerializeSettings, ValidationError};
|
||||
use krilla::interactive::annotation::Annotation;
|
||||
use krilla::interactive::destination::{NamedDestination, XyzDestination};
|
||||
use krilla::interchange::embed::EmbedError;
|
||||
use krilla_svg::render_svg_glyph;
|
||||
use typst_library::diag::{bail, error, SourceResult};
|
||||
use typst_library::foundations::NativeElement;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use krilla::Document;
|
||||
use krilla::interchange::embed::{AssociationKind, EmbeddedFile};
|
||||
use krilla::Document;
|
||||
use typst_library::diag::{bail, SourceResult};
|
||||
use typst_library::foundations::{NativeElement, StyleChain};
|
||||
use typst_library::layout::PagedDocument;
|
||||
|
@ -7,7 +7,7 @@ use krilla::surface::Surface;
|
||||
use krilla_svg::{SurfaceExt, SvgSettings};
|
||||
use typst_library::diag::{bail, SourceResult};
|
||||
use typst_library::foundations::Smart;
|
||||
use typst_library::layout::Size;
|
||||
use typst_library::layout::{Abs, Angle, Ratio, Size, Transform};
|
||||
use typst_library::visualize::{
|
||||
ExchangeFormat, Image, ImageKind, ImageScaling, RasterFormat, RasterImage,
|
||||
};
|
||||
@ -31,6 +31,8 @@ pub(crate) fn handle_image(
|
||||
|
||||
match image.kind() {
|
||||
ImageKind::Raster(raster) => {
|
||||
let (exif_transform, new_size) = exif_transform(raster, size);
|
||||
surface.push_transform(&exif_transform.to_krilla());
|
||||
let image = match convert_raster(raster.clone(), interpolate) {
|
||||
None => bail!(span, "failed to process image"),
|
||||
Some(i) => i,
|
||||
@ -41,7 +43,8 @@ pub(crate) fn handle_image(
|
||||
gc.image_spans.insert(span);
|
||||
}
|
||||
|
||||
surface.draw_image(image, size.to_krilla());
|
||||
surface.draw_image(image, new_size.to_krilla());
|
||||
surface.pop();
|
||||
}
|
||||
ImageKind::Svg(svg) => {
|
||||
surface.draw_svg(
|
||||
@ -168,19 +171,61 @@ fn convert_raster(
|
||||
match raster.format() {
|
||||
RasterFormat::Exchange(e) => match e {
|
||||
ExchangeFormat::Jpg => {
|
||||
if !raster.is_rotated() {
|
||||
let image_data: Arc<dyn AsRef<[u8]> + Send + Sync> =
|
||||
Arc::new(raster.data().clone());
|
||||
krilla::graphics::image::Image::from_jpeg(image_data.into(), interpolate)
|
||||
} else {
|
||||
// Can't embed original JPEG data if it had to be rotated.
|
||||
krilla::graphics::image::Image::from_custom(PdfImage::new(raster), interpolate)
|
||||
}
|
||||
}
|
||||
_ => krilla::graphics::image::Image::from_custom(PdfImage::new(raster), interpolate),
|
||||
_ => krilla::graphics::image::Image::from_custom(
|
||||
PdfImage::new(raster),
|
||||
interpolate,
|
||||
),
|
||||
},
|
||||
RasterFormat::Pixel(_) => {
|
||||
krilla::graphics::image::Image::from_custom(PdfImage::new(raster), interpolate)
|
||||
}
|
||||
RasterFormat::Pixel(_) => krilla::graphics::image::Image::from_custom(
|
||||
PdfImage::new(raster),
|
||||
interpolate,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn exif_transform(image: &RasterImage, size: Size) -> (Transform, Size) {
|
||||
let base = |hp: bool, vp: bool, mut base_ts: Transform, size: Size| {
|
||||
if hp {
|
||||
// Flip horizontally in-place.
|
||||
base_ts = base_ts.pre_concat(
|
||||
Transform::scale(-Ratio::one(), Ratio::one())
|
||||
.pre_concat(Transform::translate(-size.x, Abs::zero())),
|
||||
)
|
||||
}
|
||||
|
||||
if vp {
|
||||
// Flip vertically in-place.
|
||||
base_ts = base_ts.pre_concat(
|
||||
Transform::scale(Ratio::one(), -Ratio::one())
|
||||
.pre_concat(Transform::translate(Abs::zero(), -size.y)),
|
||||
)
|
||||
}
|
||||
|
||||
base_ts
|
||||
};
|
||||
|
||||
let no_flipping =
|
||||
|hp: bool, vp: bool| (base(hp, vp, Transform::identity(), size), size);
|
||||
|
||||
let with_flipping = |hp: bool, vp: bool| {
|
||||
let base_ts = Transform::rotate_at(Angle::deg(90.0), Abs::zero(), Abs::zero())
|
||||
.pre_concat(Transform::scale(Ratio::one(), -Ratio::one()));
|
||||
let inv_size = Size::new(size.y, size.x);
|
||||
(base(hp, vp, base_ts, inv_size), inv_size)
|
||||
};
|
||||
|
||||
match image.exif_rotation() {
|
||||
Some(2) => no_flipping(true, false),
|
||||
Some(3) => no_flipping(true, true),
|
||||
Some(4) => no_flipping(false, true),
|
||||
Some(5) => with_flipping(false, false),
|
||||
Some(6) => with_flipping(false, true),
|
||||
Some(7) => with_flipping(true, true),
|
||||
Some(8) => with_flipping(true, false),
|
||||
_ => no_flipping(false, false),
|
||||
}
|
||||
}
|
||||
|
@ -65,9 +65,11 @@ pub(crate) fn handle_link(
|
||||
LinkAnnotation::new(
|
||||
rect,
|
||||
None,
|
||||
Target::Destination(krilla::interactive::destination::Destination::Named(
|
||||
Target::Destination(
|
||||
krilla::interactive::destination::Destination::Named(
|
||||
nd.clone(),
|
||||
)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
@ -1,19 +1,15 @@
|
||||
use crate::convert::GlobalContext;
|
||||
use crate::Timezone;
|
||||
use ecow::EcoString;
|
||||
use krilla::interchange::metadata::{Metadata, TextDirection};
|
||||
use typst_library::foundations::{Datetime, Smart};
|
||||
use typst_library::layout::Dir;
|
||||
use typst_library::text::Lang;
|
||||
use crate::convert::GlobalContext;
|
||||
use crate::Timezone;
|
||||
|
||||
pub(crate) fn build_metadata(gc: &GlobalContext) -> Metadata {
|
||||
let creator = format!("Typst {}", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
let lang = gc
|
||||
.languages
|
||||
.iter()
|
||||
.max_by_key(|(_, &count)| count)
|
||||
.map(|(&l, _)| l);
|
||||
let lang = gc.languages.iter().max_by_key(|(_, &count)| count).map(|(&l, _)| l);
|
||||
|
||||
let dir = if lang.map(Lang::dir) == Some(Dir::RTL) {
|
||||
TextDirection::RightToLeft
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::num::NonZeroUsize;
|
||||
use krilla::interactive::destination::XyzDestination;
|
||||
use krilla::interchange::outline::{Outline, OutlineNode};
|
||||
use std::num::NonZeroUsize;
|
||||
use typst_library::foundations::{NativeElement, Packed, StyleChain};
|
||||
use typst_library::layout::Abs;
|
||||
use typst_library::model::HeadingElem;
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
use krilla::geom::NormalizedF32;
|
||||
use krilla::graphics::color::{cmyk, luma, rgb};
|
||||
use krilla::graphics::paint::{Fill, LinearGradient, Pattern, RadialGradient, SpreadMethod, Stop, Stroke, StrokeDash, SweepGradient};
|
||||
use krilla::graphics::paint::{
|
||||
Fill, LinearGradient, Pattern, RadialGradient, SpreadMethod, Stop, Stroke,
|
||||
StrokeDash, SweepGradient,
|
||||
};
|
||||
use krilla::surface::Surface;
|
||||
use typst_library::diag::SourceResult;
|
||||
use typst_library::layout::{Abs, Angle, Quadrant, Ratio, Size, Transform};
|
||||
@ -240,9 +243,7 @@ fn convert_gradient(
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_gradient_stops(
|
||||
gradient: &Gradient,
|
||||
) -> Vec<Stop<rgb::Color>> {
|
||||
fn convert_gradient_stops(gradient: &Gradient) -> Vec<Stop<rgb::Color>> {
|
||||
let mut stops: Vec<Stop<rgb::Color>> = vec![];
|
||||
|
||||
let mut add_single = |color: &Color, offset: Ratio| {
|
||||
|
@ -2,8 +2,8 @@ use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytemuck::TransparentWrapper;
|
||||
use krilla::text::GlyphId;
|
||||
use krilla::surface::{Location, Surface};
|
||||
use krilla::text::GlyphId;
|
||||
use typst_library::diag::{bail, SourceResult};
|
||||
use typst_library::layout::{Abs, Size};
|
||||
use typst_library::text::{Font, Glyph, TextItem};
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Basic utilities for converting typst types to krilla.
|
||||
|
||||
use krilla::graphics::color::rgb as kr;
|
||||
use krilla::geom as kg;
|
||||
use krilla::graphics::color::rgb as kr;
|
||||
use krilla::graphics::paint as kp;
|
||||
use krilla::path::PathBuilder;
|
||||
use typst_library::layout::{Abs, Point, Size, Transform};
|
||||
|
Loading…
x
Reference in New Issue
Block a user