use crate::color::{DashPattern, DeviceColor, FillRule, LineCap, LineJoin};
use crate::display_list::DisplayList;
use crate::icc::ProfileHash;
use std::sync::Arc;
use stet_fonts::geometry::{Matrix, PsPath};
pub type TransferTable = Arc<Vec<f64>>;
#[derive(Clone, Debug, Default)]
pub struct TransferState {
pub gray: Option<TransferTable>,
pub color: Option<[Option<TransferTable>; 4]>,
}
impl TransferState {
pub fn has_functions(&self) -> bool {
if self.gray.is_some() {
return true;
}
if let Some(ref color) = self.color {
return color.iter().any(|t| t.is_some());
}
false
}
}
#[derive(Clone, Debug)]
pub struct HalftoneScreen {
pub frequency: f64,
pub angle: f64,
pub type4_tokens: Option<Arc<Vec<u8>>>,
pub sampled_2d: Option<Arc<Vec<f64>>>,
}
#[derive(Clone, Debug, Default)]
pub struct BgUcrState {
pub bg: Option<Arc<Vec<f64>>>,
pub ucr: Option<Arc<Vec<f64>>>,
}
#[derive(Clone, Debug, Default)]
pub struct HalftoneState {
pub gray: Option<Arc<HalftoneScreen>>,
pub color: Option<[Option<Arc<HalftoneScreen>>; 4]>,
}
#[derive(Clone, Debug)]
pub struct SpotColor {
pub tint_values: Vec<f64>,
pub color_space: SpotColorSpace,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum SpotColorSpace {
Separation {
name: Vec<u8>,
alt: SimpleColorSpace,
tint_table: Arc<TintLookupTable>,
},
DeviceN {
names: Vec<Vec<u8>>,
alt: SimpleColorSpace,
tint_table: Arc<TintLookupTable>,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum SimpleColorSpace {
DeviceGray,
DeviceRGB,
DeviceCMYK,
}
pub const CMYK_C: u8 = 1 << 0;
pub const CMYK_M: u8 = 1 << 1;
pub const CMYK_Y: u8 = 1 << 2;
pub const CMYK_K: u8 = 1 << 3;
pub const CMYK_ALL: u8 = CMYK_C | CMYK_M | CMYK_Y | CMYK_K;
pub fn cmyk_channel_for_name(name: &[u8]) -> u8 {
match name {
b"Cyan" => CMYK_C,
b"Magenta" => CMYK_M,
b"Yellow" => CMYK_Y,
b"Black" => CMYK_K,
b"All" => CMYK_ALL,
b"None" => 0,
_ => 0,
}
}
#[derive(Clone, Debug)]
pub struct FillParams {
pub color: DeviceColor,
pub fill_rule: FillRule,
pub ctm: Matrix,
pub is_text_glyph: bool,
pub overprint: bool,
pub overprint_mode: i32,
pub opm_paired: bool,
pub painted_channels: u8,
pub is_device_cmyk: bool,
pub spot_color: Option<SpotColor>,
pub rendering_intent: u8,
pub transfer: TransferState,
pub halftone: HalftoneState,
pub bg_ucr: BgUcrState,
pub alpha: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
}
#[derive(Clone, Debug)]
pub struct TextParams {
pub text: Vec<u8>,
pub start_x: f64,
pub start_y: f64,
pub font_entity: u32,
pub font_name: Vec<u8>,
pub font_type: i32,
pub font_size: f64,
pub color: DeviceColor,
pub ctm: [f64; 6],
pub font_matrix: [f64; 6],
pub paint_type: i32,
pub stroke_width: f64,
pub spot_color: Option<SpotColor>,
pub rendering_intent: u8,
pub transfer: TransferState,
pub halftone: HalftoneState,
pub bg_ucr: BgUcrState,
pub fill_opacity: f64,
pub stroke_opacity: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
pub text_knockout: bool,
}
#[derive(Clone, Debug)]
pub struct StrokeParams {
pub color: DeviceColor,
pub line_width: f64,
pub line_cap: LineCap,
pub line_join: LineJoin,
pub miter_limit: f64,
pub dash_pattern: DashPattern,
pub ctm: Matrix,
pub stroke_adjust: bool,
pub is_text_glyph: bool,
pub overprint: bool,
pub overprint_mode: i32,
pub opm_paired: bool,
pub painted_channels: u8,
pub is_device_cmyk: bool,
pub spot_color: Option<SpotColor>,
pub rendering_intent: u8,
pub transfer: TransferState,
pub halftone: HalftoneState,
pub bg_ucr: BgUcrState,
pub alpha: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
}
#[derive(Clone, Debug)]
pub struct ClipParams {
pub fill_rule: FillRule,
pub ctm: Matrix,
pub stroke_params: Option<StrokeParams>,
}
#[derive(Clone, Debug)]
pub struct TintLookupTable {
pub num_inputs: u32,
pub num_outputs: u32,
pub samples_per_dim: u32,
pub data: Vec<f32>,
}
impl TintLookupTable {
#[inline]
pub fn lookup_1d(&self, tint: f32, out: &mut [f32]) {
let n = self.samples_per_dim as usize;
let no = self.num_outputs as usize;
let idx = tint * (n - 1) as f32;
let i0 = (idx as usize).min(n - 2);
let frac = idx - i0 as f32;
let base0 = i0 * no;
let base1 = (i0 + 1) * no;
for (c, out_val) in out[..no].iter_mut().enumerate() {
*out_val = self.data[base0 + c] * (1.0 - frac) + self.data[base1 + c] * frac;
}
}
pub fn lookup_nd(&self, inputs: &[f32], out: &mut [f32]) {
let ni = self.num_inputs as usize;
let no = self.num_outputs as usize;
let n = self.samples_per_dim as usize;
let mut idx = [0usize; 8];
let mut frac = [0.0f32; 8];
for d in 0..ni {
let fi = inputs[d] * (n - 1) as f32;
idx[d] = (fi as usize).min(n - 2);
frac[d] = fi - idx[d] as f32;
}
let corners = 1usize << ni;
for out_val in out[..no].iter_mut() {
*out_val = 0.0;
}
for corner in 0..corners {
let mut weight = 1.0f32;
let mut linear_idx = 0usize;
for d in 0..ni {
let bit = (corner >> d) & 1;
let dim_idx = idx[d] + bit;
weight *= if bit == 1 { frac[d] } else { 1.0 - frac[d] };
let stride = n.pow((ni - 1 - d) as u32);
linear_idx += dim_idx * stride;
}
let base = linear_idx * no;
for (c, out_val) in out[..no].iter_mut().enumerate() {
*out_val += weight * self.data.get(base + c).copied().unwrap_or(0.0);
}
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ImageColorSpace {
DeviceGray,
DeviceRGB,
DeviceCMYK,
ICCBased {
n: u32,
profile_hash: ProfileHash,
profile_data: Arc<Vec<u8>>,
},
Indexed {
base: Box<ImageColorSpace>,
hival: u32,
lookup: Vec<u8>,
},
CIEBasedABC {
params: Arc<crate::color::CieAbcParams>,
},
CIEBasedA {
params: Arc<crate::color::CieAParams>,
},
Lab {
white_point: [f64; 3],
range: [f64; 4],
},
Separation {
name: Vec<u8>,
alt_space: Box<ImageColorSpace>,
tint_table: Arc<TintLookupTable>,
},
DeviceN {
names: Vec<Vec<u8>>,
alt_space: Box<ImageColorSpace>,
tint_table: Arc<TintLookupTable>,
},
Mask {
color: DeviceColor,
polarity: bool,
},
PreconvertedRGBA,
}
impl ImageColorSpace {
pub fn num_components(&self) -> u32 {
match self {
ImageColorSpace::DeviceGray => 1,
ImageColorSpace::DeviceRGB => 3,
ImageColorSpace::DeviceCMYK => 4,
ImageColorSpace::ICCBased { n, .. } => *n,
ImageColorSpace::Indexed { .. } => 1,
ImageColorSpace::CIEBasedABC { .. } => 3,
ImageColorSpace::CIEBasedA { .. } => 1,
ImageColorSpace::Lab { .. } => 3,
ImageColorSpace::Separation { .. } => 1,
ImageColorSpace::DeviceN { tint_table, .. } => tint_table.num_inputs,
ImageColorSpace::Mask { .. } => 1,
ImageColorSpace::PreconvertedRGBA => 4,
}
}
}
#[derive(Clone, Debug)]
pub struct ImageParams {
pub width: u32,
pub height: u32,
pub color_space: ImageColorSpace,
pub bits_per_component: u8,
pub ctm: Matrix,
pub image_matrix: Matrix,
pub interpolate: bool,
pub mask_color: Option<Vec<u8>>,
pub alpha: f64,
pub blend_mode: u8,
pub overprint: bool,
pub overprint_mode: i32,
pub opm_paired: bool,
pub painted_channels: u8,
pub alpha_is_shape: bool,
pub rendering_intent: u8,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ShadingColorSpace {
DeviceGray,
DeviceRGB,
DeviceCMYK,
ICCBased {
n: u32,
profile_hash: ProfileHash,
profile_data: Arc<Vec<u8>>,
},
CalRGB {
white_point: [f64; 3],
matrix: Option<[f64; 9]>,
gamma: Option<[f64; 3]>,
},
CalGray {
white_point: [f64; 3],
gamma: Option<f64>,
},
}
impl ShadingColorSpace {
pub fn num_components(&self) -> usize {
match self {
ShadingColorSpace::DeviceGray | ShadingColorSpace::CalGray { .. } => 1,
ShadingColorSpace::DeviceRGB | ShadingColorSpace::CalRGB { .. } => 3,
ShadingColorSpace::DeviceCMYK => 4,
ShadingColorSpace::ICCBased { n, .. } => *n as usize,
}
}
}
#[derive(Clone, Debug)]
pub struct ColorStop {
pub position: f64,
pub color: DeviceColor,
pub raw_components: Vec<f64>,
}
#[derive(Clone, Debug)]
pub struct AxialShadingParams {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
pub color_stops: Vec<ColorStop>,
pub extend_start: bool,
pub extend_end: bool,
pub ctm: Matrix,
pub bbox: Option<[f64; 4]>,
pub color_space: ShadingColorSpace,
pub overprint: bool,
pub painted_channels: u8,
pub alpha: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
pub spot_tint_blend: bool,
}
#[derive(Clone, Debug)]
pub struct RadialShadingParams {
pub x0: f64,
pub y0: f64,
pub r0: f64,
pub x1: f64,
pub y1: f64,
pub r1: f64,
pub color_stops: Vec<ColorStop>,
pub extend_start: bool,
pub extend_end: bool,
pub ctm: Matrix,
pub bbox: Option<[f64; 4]>,
pub color_space: ShadingColorSpace,
pub overprint: bool,
pub painted_channels: u8,
pub alpha: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
pub spot_tint_blend: bool,
}
#[derive(Clone, Debug)]
pub struct ShadingVertex {
pub x: f64,
pub y: f64,
pub color: DeviceColor,
pub raw_components: Vec<f64>,
}
#[derive(Clone, Debug)]
pub struct ShadingTriangle {
pub v0: ShadingVertex,
pub v1: ShadingVertex,
pub v2: ShadingVertex,
}
#[derive(Clone, Debug)]
pub struct MeshShadingParams {
pub triangles: Vec<ShadingTriangle>,
pub ctm: Matrix,
pub bbox: Option<[f64; 4]>,
pub color_space: ShadingColorSpace,
pub overprint: bool,
pub painted_channels: u8,
pub color_lut: Option<Arc<Vec<DeviceColor>>>,
pub alpha: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
}
#[derive(Clone, Debug)]
pub struct ShadingPatch {
pub points: Vec<(f64, f64)>,
pub colors: [DeviceColor; 4],
pub raw_colors: [Vec<f64>; 4],
}
#[derive(Clone, Debug)]
pub struct PatchShadingParams {
pub patches: Vec<ShadingPatch>,
pub ctm: Matrix,
pub bbox: Option<[f64; 4]>,
pub color_space: ShadingColorSpace,
pub overprint: bool,
pub painted_channels: u8,
pub color_lut: Option<Arc<Vec<DeviceColor>>>,
pub alpha: f64,
pub blend_mode: u8,
pub alpha_is_shape: bool,
}
#[derive(Clone)]
pub struct PatternFillParams {
pub path: PsPath,
pub fill_rule: FillRule,
pub tile: DisplayList,
pub pattern_matrix: Matrix,
pub bbox: [f64; 4],
pub xstep: f64,
pub ystep: f64,
pub paint_type: i32,
pub underlying_color: Option<DeviceColor>,
pub pattern_id: u32,
pub device_space_tile: bool,
pub flip_tile_y: bool,
pub stroke_params: Option<StrokeParams>,
pub overprint_mode: i32,
}
impl Default for FillParams {
fn default() -> Self {
Self {
color: DeviceColor::default(),
fill_rule: FillRule::default(),
ctm: Matrix::default(),
is_text_glyph: false,
overprint: false,
overprint_mode: 0,
opm_paired: false,
painted_channels: 0,
is_device_cmyk: false,
spot_color: None,
rendering_intent: 0,
transfer: TransferState::default(),
halftone: HalftoneState::default(),
bg_ucr: BgUcrState::default(),
alpha: 1.0,
blend_mode: 0,
alpha_is_shape: false,
}
}
}
impl Default for StrokeParams {
fn default() -> Self {
Self {
color: DeviceColor::default(),
line_width: 1.0,
line_cap: LineCap::default(),
line_join: LineJoin::default(),
miter_limit: 10.0,
dash_pattern: DashPattern::default(),
ctm: Matrix::default(),
stroke_adjust: false,
is_text_glyph: false,
overprint: false,
overprint_mode: 0,
opm_paired: false,
painted_channels: 0,
is_device_cmyk: false,
spot_color: None,
rendering_intent: 0,
transfer: TransferState::default(),
halftone: HalftoneState::default(),
bg_ucr: BgUcrState::default(),
alpha: 1.0,
blend_mode: 0,
alpha_is_shape: false,
}
}
}
impl Default for ClipParams {
fn default() -> Self {
Self {
fill_rule: FillRule::default(),
ctm: Matrix::default(),
stroke_params: None,
}
}
}
impl Default for TextParams {
fn default() -> Self {
Self {
text: Vec::new(),
start_x: 0.0,
start_y: 0.0,
font_entity: 0,
font_name: Vec::new(),
font_type: 1,
font_size: 0.0,
color: DeviceColor::default(),
ctm: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
font_matrix: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
paint_type: 0,
stroke_width: 0.0,
spot_color: None,
rendering_intent: 0,
transfer: TransferState::default(),
halftone: HalftoneState::default(),
bg_ucr: BgUcrState::default(),
fill_opacity: 1.0,
stroke_opacity: 1.0,
blend_mode: 0,
alpha_is_shape: false,
text_knockout: true,
}
}
}
impl Default for ImageColorSpace {
fn default() -> Self {
ImageColorSpace::DeviceGray
}
}
impl Default for ImageParams {
fn default() -> Self {
Self {
width: 0,
height: 0,
color_space: ImageColorSpace::default(),
bits_per_component: 8,
ctm: Matrix::default(),
image_matrix: Matrix::default(),
interpolate: false,
mask_color: None,
alpha: 1.0,
blend_mode: 0,
overprint: false,
overprint_mode: 0,
opm_paired: false,
painted_channels: 0,
alpha_is_shape: false,
rendering_intent: 0,
}
}
}
impl Default for ShadingColorSpace {
fn default() -> Self {
ShadingColorSpace::DeviceRGB
}
}
impl Default for ColorStop {
fn default() -> Self {
Self {
position: 0.0,
color: DeviceColor::default(),
raw_components: Vec::new(),
}
}
}
impl Default for AxialShadingParams {
fn default() -> Self {
Self {
x0: 0.0,
y0: 0.0,
x1: 0.0,
y1: 0.0,
color_stops: Vec::new(),
extend_start: false,
extend_end: false,
ctm: Matrix::default(),
bbox: None,
color_space: ShadingColorSpace::default(),
overprint: false,
painted_channels: 0,
alpha: 1.0,
blend_mode: 0,
alpha_is_shape: false,
spot_tint_blend: false,
}
}
}
impl Default for RadialShadingParams {
fn default() -> Self {
Self {
x0: 0.0,
y0: 0.0,
r0: 0.0,
x1: 0.0,
y1: 0.0,
r1: 0.0,
color_stops: Vec::new(),
extend_start: false,
extend_end: false,
ctm: Matrix::default(),
bbox: None,
color_space: ShadingColorSpace::default(),
overprint: false,
painted_channels: 0,
alpha: 1.0,
blend_mode: 0,
alpha_is_shape: false,
spot_tint_blend: false,
}
}
}
impl Default for MeshShadingParams {
fn default() -> Self {
Self {
triangles: Vec::new(),
ctm: Matrix::default(),
bbox: None,
color_space: ShadingColorSpace::default(),
overprint: false,
painted_channels: 0,
color_lut: None,
alpha: 1.0,
blend_mode: 0,
alpha_is_shape: false,
}
}
}
impl Default for PatchShadingParams {
fn default() -> Self {
Self {
patches: Vec::new(),
ctm: Matrix::default(),
bbox: None,
color_space: ShadingColorSpace::default(),
overprint: false,
painted_channels: 0,
color_lut: None,
alpha: 1.0,
blend_mode: 0,
alpha_is_shape: false,
}
}
}
pub trait PageSink: Send {
fn begin_page(&mut self, width: u32, height: u32) -> Result<(), String>;
fn write_rows(&mut self, rgba_rows: &[u8], num_rows: u32) -> Result<(), String>;
fn end_page(&mut self) -> Result<(), String>;
}
pub trait PageSinkFactory: Send + Sync {
fn create_sink(&self, output_path: &str) -> Result<Box<dyn PageSink>, String>;
}