#![allow(unused_variables)]
use crate::indices::FontIndex;
use lopdf;
use lopdf::content::Operation;
use lopdf::Object::*;
use std::collections::HashMap;
use std::collections::HashSet;
use std::string::String;
pub(crate) const LINE_WIDTH: &str = "line_width";
pub(crate) const LINE_CAP: &str = "line_cap";
pub(crate) const LINE_JOIN: &str = "line_join";
pub(crate) const MITER_LIMIT: &str = "miter_limit";
pub(crate) const LINE_DASH_PATTERN: &str = "line_dash_pattern";
pub(crate) const RENDERING_INTENT: &str = "rendering_intent";
pub(crate) const OVERPRINT_STROKE: &str = "overprint_stroke";
pub(crate) const OVERPRINT_FILL: &str = "overprint_fill";
pub(crate) const OVERPRINT_MODE: &str = "overprint_mode";
pub(crate) const FONT: &str = "font";
pub(crate) const BLACK_GENERATION: &str = "black_generation";
pub(crate) const BLACK_GENERATION_EXTRA: &str = "black_generation_extra";
pub(crate) const UNDERCOLOR_REMOVAL: &str = "under_color_removal";
pub(crate) const UNDERCOLOR_REMOVAL_EXTRA: &str = "undercolor_removal_extra";
pub(crate) const TRANSFER_FUNCTION: &str = "transfer_function";
pub(crate) const TRANSFER_FUNCTION_EXTRA: &str = "transfer_function_extra";
pub(crate) const HALFTONE_DICTIONARY: &str = "halftone_dictionary";
pub(crate) const FLATNESS_TOLERANCE: &str = "flatness_tolerance";
pub(crate) const SMOOTHNESS_TOLERANCE: &str = "smoothness_tolerance";
pub(crate) const STROKE_ADJUSTMENT: &str = "stroke_adjustment";
pub(crate) const BLEND_MODE: &str = "blend_mode";
pub(crate) const SOFT_MASK: &str = "soft_mask";
pub(crate) const CURRENT_STROKE_ALPHA: &str = "current_stroke_alpha";
pub(crate) const CURRENT_FILL_ALPHA: &str = "current_fill_alpha";
pub(crate) const ALPHA_IS_SHAPE: &str = "alpha_is_shape";
pub(crate) const TEXT_KNOCKOUT: &str = "text_knockout";
#[derive(Debug, Clone, Default)]
pub struct ExtendedGraphicsStateList {
pub(crate) latest_graphics_state: (usize, ExtendedGraphicsState),
pub(crate) all_graphics_states: HashMap<String, (usize, ExtendedGraphicsState)>,
}
impl ExtendedGraphicsStateList {
pub fn new() -> Self {
Self::default()
}
pub fn add_graphics_state(
&mut self,
added_state: ExtendedGraphicsState,
) -> ExtendedGraphicsStateRef {
let gs_ref = ExtendedGraphicsStateRef::new(self.all_graphics_states.len());
self.all_graphics_states.insert(
gs_ref.gs_name.clone(),
(self.latest_graphics_state.0, added_state.clone()),
);
self.latest_graphics_state = (self.latest_graphics_state.0, added_state);
gs_ref
}
}
impl From<ExtendedGraphicsStateList> for lopdf::Dictionary {
fn from(val: ExtendedGraphicsStateList) -> Self {
let mut ext_g_state_resources = lopdf::Dictionary::new();
for (name, (_, graphics_state)) in val.all_graphics_states {
let gs: lopdf::Object = graphics_state.into();
ext_g_state_resources.set(name.to_string(), gs);
}
ext_g_state_resources
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ExtendedGraphicsState {
pub(crate) changed_fields: HashSet<&'static str>,
pub(crate) line_width: f32,
pub(crate) line_cap: LineCapStyle,
pub(crate) line_join: LineJoinStyle,
pub(crate) miter_limit: f32,
pub(crate) line_dash_pattern: Option<LineDashPattern>,
pub(crate) rendering_intent: RenderingIntent,
pub(crate) overprint_stroke: bool,
pub(crate) overprint_fill: bool,
pub(crate) overprint_mode: OverprintMode,
pub(crate) font: Option<FontIndex>,
pub(crate) black_generation: Option<BlackGenerationFunction>,
pub(crate) black_generation_extra: Option<BlackGenerationExtraFunction>,
pub(crate) under_color_removal: Option<UnderColorRemovalFunction>,
pub(crate) under_color_removal_extra: Option<UnderColorRemovalExtraFunction>,
pub(crate) transfer_function: Option<TransferFunction>,
pub(crate) transfer_extra_function: Option<TransferExtraFunction>,
pub(crate) halftone_dictionary: Option<HalftoneType>,
pub(crate) flatness_tolerance: f32,
pub(crate) smoothness_tolerance: f32,
pub(crate) stroke_adjustment: bool,
pub(crate) blend_mode: BlendMode,
pub(crate) soft_mask: Option<SoftMask>,
pub(crate) current_stroke_alpha: f32,
pub(crate) current_fill_alpha: f32,
pub(crate) alpha_is_shape: bool,
pub(crate) text_knockout: bool,
}
#[derive(Debug, Clone, Default)]
pub struct ExtendedGraphicsStateBuilder {
gs: ExtendedGraphicsState,
}
impl ExtendedGraphicsStateBuilder {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn with_line_width(mut self, line_width: f32) -> Self {
self.gs.line_width = line_width;
self.gs.changed_fields.insert(LINE_WIDTH);
self
}
#[inline]
pub fn with_line_cap(mut self, line_cap: LineCapStyle) -> Self {
self.gs.line_cap = line_cap;
self.gs.changed_fields.insert(LINE_CAP);
self
}
#[inline]
pub fn with_line_join(mut self, line_join: LineJoinStyle) -> Self {
self.gs.line_join = line_join;
self.gs.changed_fields.insert(LINE_JOIN);
self
}
#[inline]
pub fn with_miter_limit(mut self, miter_limit: f32) -> Self {
self.gs.miter_limit = miter_limit;
self.gs.changed_fields.insert(MITER_LIMIT);
self
}
#[inline]
pub fn with_rendering_intent(mut self, rendering_intent: RenderingIntent) -> Self {
self.gs.rendering_intent = rendering_intent;
self.gs.changed_fields.insert(RENDERING_INTENT);
self
}
#[inline]
pub fn with_overprint_stroke(mut self, overprint_stroke: bool) -> Self {
self.gs.overprint_stroke = overprint_stroke;
self.gs.changed_fields.insert(OVERPRINT_STROKE);
self
}
#[inline]
pub fn with_overprint_fill(mut self, overprint_fill: bool) -> Self {
self.gs.overprint_fill = overprint_fill;
self.gs.changed_fields.insert(OVERPRINT_FILL);
self
}
#[inline]
pub fn with_overprint_mode(mut self, overprint_mode: OverprintMode) -> Self {
self.gs.overprint_mode = overprint_mode;
self.gs.changed_fields.insert(OVERPRINT_MODE);
self
}
#[inline]
pub fn with_font(mut self, font: Option<FontIndex>) -> Self {
self.gs.font = font;
self.gs.changed_fields.insert(FONT);
self
}
#[inline]
pub fn with_black_generation(
mut self,
black_generation: Option<BlackGenerationFunction>,
) -> Self {
self.gs.black_generation = black_generation;
self.gs.changed_fields.insert(BLACK_GENERATION);
self
}
#[inline]
pub fn with_black_generation_extra(
mut self,
black_generation_extra: Option<BlackGenerationExtraFunction>,
) -> Self {
self.gs.black_generation_extra = black_generation_extra;
self.gs.changed_fields.insert(BLACK_GENERATION_EXTRA);
self
}
#[inline]
pub fn with_undercolor_removal(
mut self,
under_color_removal: Option<UnderColorRemovalFunction>,
) -> Self {
self.gs.under_color_removal = under_color_removal;
self.gs.changed_fields.insert(UNDERCOLOR_REMOVAL);
self
}
#[inline]
pub fn with_undercolor_removal_extra(
mut self,
under_color_removal_extra: Option<UnderColorRemovalExtraFunction>,
) -> Self {
self.gs.under_color_removal_extra = under_color_removal_extra;
self.gs.changed_fields.insert(UNDERCOLOR_REMOVAL_EXTRA);
self
}
#[inline]
pub fn with_transfer(mut self, transfer_function: Option<TransferFunction>) -> Self {
self.gs.transfer_function = transfer_function;
self.gs.changed_fields.insert(TRANSFER_FUNCTION);
self
}
#[inline]
pub fn with_transfer_extra(
mut self,
transfer_extra_function: Option<TransferExtraFunction>,
) -> Self {
self.gs.transfer_extra_function = transfer_extra_function;
self.gs.changed_fields.insert(TRANSFER_FUNCTION_EXTRA);
self
}
#[inline]
pub fn with_halftone(mut self, halftone_type: Option<HalftoneType>) -> Self {
self.gs.halftone_dictionary = halftone_type;
self.gs.changed_fields.insert(HALFTONE_DICTIONARY);
self
}
#[inline]
pub fn with_flatness_tolerance(mut self, flatness_tolerance: f32) -> Self {
self.gs.flatness_tolerance = flatness_tolerance;
self.gs.changed_fields.insert(FLATNESS_TOLERANCE);
self
}
#[inline]
pub fn with_smoothness_tolerance(mut self, smoothness_tolerance: f32) -> Self {
self.gs.smoothness_tolerance = smoothness_tolerance;
self.gs.changed_fields.insert(SMOOTHNESS_TOLERANCE);
self
}
#[inline]
pub fn with_stroke_adjustment(mut self, stroke_adjustment: bool) -> Self {
self.gs.stroke_adjustment = stroke_adjustment;
self.gs.changed_fields.insert(STROKE_ADJUSTMENT);
self
}
#[inline]
pub fn with_blend_mode(mut self, blend_mode: BlendMode) -> Self {
self.gs.blend_mode = blend_mode;
self.gs.changed_fields.insert(BLEND_MODE);
self
}
#[inline]
pub fn with_soft_mask(mut self, soft_mask: Option<SoftMask>) -> Self {
self.gs.soft_mask = soft_mask;
self.gs.changed_fields.insert(SOFT_MASK);
self
}
#[inline]
pub fn with_current_stroke_alpha(mut self, current_stroke_alpha: f32) -> Self {
self.gs.current_stroke_alpha = current_stroke_alpha;
self.gs.changed_fields.insert(CURRENT_STROKE_ALPHA);
self
}
#[inline]
pub fn with_current_fill_alpha(mut self, current_fill_alpha: f32) -> Self {
self.gs.current_fill_alpha = current_fill_alpha;
self.gs.changed_fields.insert(CURRENT_FILL_ALPHA);
self
}
#[inline]
pub fn with_alpha_is_shape(mut self, alpha_is_shape: bool) -> Self {
self.gs.alpha_is_shape = alpha_is_shape;
self.gs.changed_fields.insert(ALPHA_IS_SHAPE);
self
}
#[inline]
pub fn with_text_knockout(mut self, text_knockout: bool) -> Self {
self.gs.text_knockout = text_knockout;
self.gs.changed_fields.insert(TEXT_KNOCKOUT);
self
}
#[inline]
pub fn build(self) -> ExtendedGraphicsState {
self.gs
}
}
impl Default for ExtendedGraphicsState {
fn default() -> Self {
Self {
changed_fields: HashSet::new(),
line_width: 1.0,
line_cap: LineCapStyle::Butt,
line_join: LineJoinStyle::Miter,
miter_limit: 0.0,
line_dash_pattern: None,
rendering_intent: RenderingIntent::RelativeColorimetric,
overprint_stroke: false,
overprint_fill: false,
overprint_mode: OverprintMode::EraseUnderlying,
font: None,
black_generation: None,
black_generation_extra: None,
under_color_removal: None,
under_color_removal_extra: None,
transfer_function: None,
transfer_extra_function: None,
halftone_dictionary: None,
flatness_tolerance: 0.0,
smoothness_tolerance: 0.0,
stroke_adjustment: true,
blend_mode: BlendMode::Seperable(SeperableBlendMode::Normal),
soft_mask: None,
current_stroke_alpha: 1.0,
current_fill_alpha: 1.0,
alpha_is_shape: false,
text_knockout: false,
}
}
}
impl From<ExtendedGraphicsState> for lopdf::Object {
fn from(val: ExtendedGraphicsState) -> Self {
let mut gs_operations = Vec::<(String, lopdf::Object)>::new();
if val.changed_fields.contains(LINE_WIDTH) {
gs_operations.push(("LW".to_string(), val.line_width.into()));
}
if val.changed_fields.contains(LINE_CAP) {
gs_operations.push(("LC".to_string(), val.line_cap.into()));
}
if val.changed_fields.contains(LINE_JOIN) {
gs_operations.push(("LJ".to_string(), val.line_join.into()));
}
if val.changed_fields.contains(MITER_LIMIT) {
gs_operations.push(("ML".to_string(), val.miter_limit.into()));
}
if val.changed_fields.contains(FLATNESS_TOLERANCE) {
gs_operations.push(("FL".to_string(), val.flatness_tolerance.into()));
}
if val.changed_fields.contains(RENDERING_INTENT) {
gs_operations.push(("RI".to_string(), val.rendering_intent.into()));
}
if val.changed_fields.contains(STROKE_ADJUSTMENT) {
gs_operations.push(("SA".to_string(), val.stroke_adjustment.into()));
}
if val.changed_fields.contains(OVERPRINT_FILL) {
gs_operations.push(("OP".to_string(), val.overprint_fill.into()));
}
if val.changed_fields.contains(OVERPRINT_STROKE) {
gs_operations.push(("op".to_string(), val.overprint_stroke.into()));
}
if val.changed_fields.contains(OVERPRINT_MODE) {
gs_operations.push(("OPM".to_string(), val.overprint_mode.into()));
}
if val.changed_fields.contains(CURRENT_FILL_ALPHA) {
gs_operations.push(("CA".to_string(), val.current_fill_alpha.into()));
}
if val.changed_fields.contains(CURRENT_STROKE_ALPHA) {
gs_operations.push(("ca".to_string(), val.current_stroke_alpha.into()));
}
if val.changed_fields.contains(BLEND_MODE) {
gs_operations.push(("BM".to_string(), val.blend_mode.into()));
}
if val.changed_fields.contains(ALPHA_IS_SHAPE) {
gs_operations.push(("AIS".to_string(), val.alpha_is_shape.into()));
}
if val.changed_fields.contains(TEXT_KNOCKOUT) {
gs_operations.push(("TK".to_string(), val.text_knockout.into()));
}
if let Some(ldp) = val.line_dash_pattern {
if val.changed_fields.contains(LINE_DASH_PATTERN) {
let pattern: lopdf::Object = ldp.into();
gs_operations.push(("D".to_string(), pattern));
}
}
if let Some(ref font) = val.font {
if val.changed_fields.contains(FONT) {
}
}
if val.changed_fields.contains(BLACK_GENERATION) {
if let Some(ref black_generation) = val.black_generation {}
}
if val.changed_fields.contains(BLACK_GENERATION_EXTRA) {
if let Some(ref black_generation_extra) = val.black_generation_extra {}
}
if val.changed_fields.contains(UNDERCOLOR_REMOVAL) {
if let Some(ref under_color_removal) = val.under_color_removal {}
}
if val.changed_fields.contains(UNDERCOLOR_REMOVAL_EXTRA) {
if let Some(ref under_color_removal_extra) = val.under_color_removal_extra {}
}
if val.changed_fields.contains(TRANSFER_FUNCTION) {
if let Some(ref transfer_function) = val.transfer_function {}
}
if val.changed_fields.contains(TRANSFER_FUNCTION_EXTRA) {
if let Some(ref transfer_extra_function) = val.transfer_extra_function {}
}
if val.changed_fields.contains(HALFTONE_DICTIONARY) {
if let Some(ref halftone_dictionary) = val.halftone_dictionary {}
}
if val.changed_fields.contains(SOFT_MASK) {
if let Some(ref soft_mask) = val.soft_mask {
} else {
gs_operations.push(("SM".to_string(), Name("None".as_bytes().to_vec())));
}
}
if !gs_operations.is_empty() {
gs_operations.push(("Type".to_string(), "ExtGState".into()));
}
let graphics_state = lopdf::Dictionary::from_iter(gs_operations);
Dictionary(graphics_state)
}
}
pub struct ExtendedGraphicsStateRef {
pub(crate) gs_name: String,
}
impl ExtendedGraphicsStateRef {
#[inline]
pub fn new(index: usize) -> Self {
Self {
gs_name: format!("GS{index:?}"),
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum OverprintMode {
EraseUnderlying,
KeepUnderlying,
}
impl From<OverprintMode> for lopdf::Object {
fn from(val: OverprintMode) -> Self {
use self::OverprintMode::*;
match val {
EraseUnderlying => Integer(0),
KeepUnderlying => Integer(1),
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BlackGenerationFunction {
Default,
WithUnderColorRemoval,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BlackGenerationExtraFunction {}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum UnderColorRemovalFunction {
Default,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum UnderColorRemovalExtraFunction {}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum TransferFunction {}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum TransferExtraFunction {}
#[derive(Debug, PartialEq, Clone)]
pub enum HalftoneType {
Type1(f32, f32, SpotFunction),
Type5(Vec<HalftoneType>),
Type6(Vec<u8>),
Type10(Vec<u8>),
Type16(Vec<u16>),
}
impl HalftoneType {
pub fn get_type(&self) -> i64 {
use self::HalftoneType::*;
match *self {
Type1(_, _, _) => 1,
Type5(_) => 5,
Type6(_) => 6,
Type10(_) => 10,
Type16(_) => 16,
}
}
pub fn into_obj(self) -> Vec<lopdf::Object> {
vec![Dictionary(lopdf::Dictionary::from_iter(vec![
("Type", "Halftone".into()),
("HalftoneType", self.get_type().into()),
]))]
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SpotFunction {
SimpleDot,
InvertedSimpleDot,
DoubleDot,
InvertedDoubleDot,
CosineDot,
Double,
InvertedDouble,
Line,
LineX,
LineY,
Round,
Ellipse,
EllipseA,
InvertedEllipseA,
EllipseB,
EllipseC,
InvertedEllipseC,
Square,
Cross,
Rhomboid,
Diamond,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BlendMode {
Seperable(SeperableBlendMode),
NonSeperable(NonSeperableBlendMode),
}
impl From<BlendMode> for lopdf::Object {
fn from(val: BlendMode) -> Self {
use self::BlendMode::*;
use self::NonSeperableBlendMode::*;
use self::SeperableBlendMode::*;
let blend_mode_str = match val {
Seperable(s) => match s {
Normal => "Normal",
Multiply => "Multiply",
Screen => "Screen",
Overlay => "Overlay",
Darken => "Darken",
Lighten => "Lighten",
ColorDodge => "ColorDodge",
ColorBurn => "ColorBurn",
HardLight => "HardLight",
SoftLight => "SoftLight",
Difference => "Difference",
Exclusion => "Exclusion",
},
NonSeperable(n) => match n {
Hue => "Hue",
Saturation => "Saturation",
Color => "Color",
Luminosity => "Luminosity",
},
};
Name(blend_mode_str.as_bytes().to_vec())
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SeperableBlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum NonSeperableBlendMode {
Hue,
Saturation,
Color,
Luminosity,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum RenderingIntent {
AbsoluteColorimetric,
RelativeColorimetric,
Saturation,
Perceptual,
}
impl RenderingIntent {
pub fn into_stream_op(self) -> Vec<Operation> {
use self::RenderingIntent::*;
let rendering_intent_string = match self {
AbsoluteColorimetric => "AbsoluteColorimetric",
RelativeColorimetric => "RelativeColorimetric",
Saturation => "Saturation",
Perceptual => "Perceptual",
};
vec![Operation::new(
"ri",
vec![Name(rendering_intent_string.as_bytes().to_vec())],
)]
}
}
impl From<RenderingIntent> for lopdf::Object {
fn from(val: RenderingIntent) -> Self {
use self::RenderingIntent::*;
let rendering_intent_string = match val {
AbsoluteColorimetric => "AbsoluteColorimetric",
RelativeColorimetric => "RelativeColorimetric",
Saturation => "Saturation",
Perceptual => "Perceptual",
};
Name(rendering_intent_string.as_bytes().to_vec())
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct SoftMask {
data: Vec<u8>,
bits_per_component: u8,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoftMaskFunction {
GroupAlpha,
GroupLuminosity,
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LineJoinStyle {
Miter,
Round,
Limit,
}
impl From<LineJoinStyle> for i64 {
fn from(val: LineJoinStyle) -> Self {
use self::LineJoinStyle::*;
match val {
Miter => 0,
Round => 1,
Limit => 2,
}
}
}
impl From<LineJoinStyle> for Operation {
fn from(val: LineJoinStyle) -> Self {
let line_join_num: i64 = val.into();
Operation::new("j", vec![Integer(line_join_num)])
}
}
impl From<LineJoinStyle> for lopdf::Object {
fn from(val: LineJoinStyle) -> Self {
Integer(val.into())
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LineCapStyle {
Butt,
Round,
ProjectingSquare,
}
impl From<LineCapStyle> for i64 {
fn from(val: LineCapStyle) -> Self {
use self::LineCapStyle::*;
match val {
Butt => 0,
Round => 1,
ProjectingSquare => 2,
}
}
}
impl From<LineCapStyle> for Operation {
fn from(val: LineCapStyle) -> Self {
Operation::new("J", vec![Integer(val.into())])
}
}
impl From<LineCapStyle> for lopdf::Object {
fn from(val: LineCapStyle) -> Self {
Integer(val.into())
}
}
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct LineDashPattern {
pub offset: i64,
pub dash_1: Option<i64>,
pub gap_1: Option<i64>,
pub dash_2: Option<i64>,
pub gap_2: Option<i64>,
pub dash_3: Option<i64>,
pub gap_3: Option<i64>,
}
impl From<LineDashPattern> for (Vec<i64>, i64) {
fn from(val: LineDashPattern) -> Self {
(
[
val.dash_1, val.gap_1, val.dash_2, val.gap_2, val.dash_3, val.gap_3,
]
.iter()
.copied()
.take_while(Option::is_some)
.flatten()
.collect(),
val.offset,
)
}
}
impl From<LineDashPattern> for Operation {
fn from(val: LineDashPattern) -> Self {
let (dash_array, offset) = val.into();
let dash_array_ints = dash_array.into_iter().map(Integer).collect();
Operation::new("d", vec![Array(dash_array_ints), Integer(offset)])
}
}
impl From<LineDashPattern> for lopdf::Object {
fn from(val: LineDashPattern) -> Self {
use lopdf::Object::*;
let (dash_array, offset) = val.into();
let mut dash_array_ints: Vec<lopdf::Object> = dash_array.into_iter().map(Integer).collect();
dash_array_ints.push(Integer(offset));
Array(dash_array_ints)
}
}