use core::fmt;
use std::collections::HashSet;
use lopdf::Dictionary as LoDictionary;
use serde_derive::{Deserialize, Serialize};
use crate::{
units::{Mm, Pt},
BuiltinFont, FontId,
};
pub const OP_PATH_PAINT_FILL_NZ: &str = "f";
pub const OP_PATH_PAINT_FILL_EO: &str = "f*";
pub const OP_PATH_PAINT_FILL_STROKE_NZ: &str = "B";
pub const OP_PATH_PAINT_FILL_STROKE_CLOSE_NZ: &str = "b";
pub const OP_PATH_PAINT_FILL_STROKE_EO: &str = "B*";
pub const OP_PATH_PAINT_FILL_STROKE_CLOSE_EO: &str = "b*";
pub const OP_PATH_CONST_CLIP_NZ: &str = "W";
pub const OP_PATH_CONST_CLIP_EO: &str = "W*";
#[derive(PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Rect {
pub x: Pt,
pub y: Pt,
pub width: Pt,
pub height: Pt,
pub mode: Option<PaintMode>,
pub winding_order: Option<WindingOrder>,
}
impl fmt::Debug for Rect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}x{} @ {} - {}",
self.width.0, self.height.0, self.x.0, self.y.0
)
}
}
impl Rect {
pub fn lower_left(&self) -> Point {
Point {
x: self.x,
y: self.y,
}
}
pub fn upper_right(&self) -> Point {
Point {
x: self.x + self.width,
y: self.y + self.height,
}
}
pub fn from_wh(width: Pt, height: Pt) -> Self {
Self {
x: Pt(0.0),
y: Pt(0.0),
width,
height,
mode: None,
winding_order: None,
}
}
pub fn from_xywh(x: Pt, y: Pt, width: Pt, height: Pt) -> Self {
Self {
x,
y,
width,
height,
mode: None,
winding_order: None,
}
}
pub fn to_polygon(&self) -> Polygon {
Polygon {
rings: vec![PolygonRing {
points: self.gen_points(),
}],
mode: self.mode.unwrap_or(PaintMode::Fill),
winding_order: self.winding_order.unwrap_or(WindingOrder::NonZero),
}
}
pub fn to_line(&self) -> Line {
Line {
points: self.gen_points(),
is_closed: true,
}
}
fn gen_points(&self) -> Vec<LinePoint> {
let top = Pt(self.y.0 + self.height.0);
let bottom = self.y;
let left = self.x;
let right = Pt(self.x.0 + self.width.0);
let tl = Point { x: left, y: top };
let tr = Point { x: right, y: top };
let br = Point {
x: right,
y: bottom,
};
let bl = Point { x: left, y: bottom };
vec![
LinePoint {
p: tl,
bezier: false,
},
LinePoint {
p: tr,
bezier: false,
},
LinePoint {
p: br,
bezier: false,
},
LinePoint {
p: bl,
bezier: false,
},
]
}
pub fn to_array(&self) -> Vec<lopdf::Object> {
vec![
(self.x.0.round() as i64).into(),
(self.y.0.round() as i64).into(),
(self.width.0.round() as i64).into(),
(self.height.0.round() as i64).into(),
]
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum WindingOrder {
EvenOdd,
#[default]
NonZero,
}
impl WindingOrder {
#[must_use]
pub fn get_clip_op(&self) -> &'static str {
match self {
WindingOrder::NonZero => OP_PATH_CONST_CLIP_NZ,
WindingOrder::EvenOdd => OP_PATH_CONST_CLIP_EO,
}
}
#[must_use]
pub fn get_fill_op(&self) -> &'static str {
match self {
WindingOrder::NonZero => OP_PATH_PAINT_FILL_NZ,
WindingOrder::EvenOdd => OP_PATH_PAINT_FILL_EO,
}
}
#[must_use]
pub fn get_fill_stroke_close_op(&self) -> &'static str {
match self {
WindingOrder::NonZero => OP_PATH_PAINT_FILL_STROKE_CLOSE_NZ,
WindingOrder::EvenOdd => OP_PATH_PAINT_FILL_STROKE_CLOSE_EO,
}
}
#[must_use]
pub fn get_fill_stroke_op(&self) -> &'static str {
match self {
WindingOrder::NonZero => OP_PATH_PAINT_FILL_STROKE_NZ,
WindingOrder::EvenOdd => OP_PATH_PAINT_FILL_STROKE_EO,
}
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PaintMode {
Clip,
#[default]
Fill,
Stroke,
FillStroke,
}
#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Point {
pub x: Pt,
pub y: Pt,
}
impl Point {
#[inline]
pub fn new(x: Mm, y: Mm) -> Self {
Self {
x: x.into(),
y: y.into(),
}
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
if self.x.0.is_normal()
&& other.x.0.is_normal()
&& self.y.0.is_normal()
&& other.y.0.is_normal()
{
let x_eq = self.x == other.x;
if !x_eq {
return false;
}
let y_eq = self.y == other.y;
if y_eq {
return true;
}
}
false
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LinePoint {
pub p: Point,
pub bezier: bool,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Line {
pub points: Vec<LinePoint>,
pub is_closed: bool,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Polygon {
pub rings: Vec<PolygonRing>,
pub mode: PaintMode,
pub winding_order: WindingOrder,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PolygonRing {
pub points: Vec<LinePoint>,
}
impl FromIterator<(Point, bool)> for Polygon {
fn from_iter<I: IntoIterator<Item = (Point, bool)>>(iter: I) -> Self {
let mut points = Vec::new();
for i in iter {
points.push(LinePoint {
p: i.0,
bezier: i.1,
});
}
Polygon {
rings: vec![PolygonRing { points }],
..Default::default()
}
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LineDashPattern {
pub offset: i64,
#[serde(default)]
pub dash_1: Option<i64>,
#[serde(default)]
pub gap_1: Option<i64>,
#[serde(default)]
pub dash_2: Option<i64>,
#[serde(default)]
pub gap_2: Option<i64>,
#[serde(default)]
pub dash_3: Option<i64>,
#[serde(default)]
pub gap_3: Option<i64>,
}
impl LineDashPattern {
pub fn as_array(&self) -> Vec<i64> {
[
self.dash_1,
self.gap_1,
self.dash_2,
self.gap_2,
self.dash_3,
self.gap_3,
]
.iter()
.copied()
.take_while(Option::is_some)
.flatten()
.collect()
}
pub fn get_svg_id(&self) -> String {
let dash_array = self.as_array();
dash_array
.iter()
.map(|num| num.to_string())
.collect::<Vec<_>>()
.join(",")
}
pub fn from_array(dashes: &[i64], offset: i64) -> Self {
let mut pat = LineDashPattern::default();
pat.offset = offset;
match dashes.len() {
0 => {
}
1 => {
pat.dash_1 = Some(dashes[0]);
}
2 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
}
3 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
}
4 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
pat.gap_2 = Some(dashes[3]);
}
5 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
pat.gap_2 = Some(dashes[3]);
pat.dash_3 = Some(dashes[4]);
}
_ => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
pat.gap_2 = Some(dashes[3]);
pat.dash_3 = Some(dashes[4]);
pat.gap_3 = Some(dashes[5]);
}
}
pat
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LineJoinStyle {
Miter,
Round,
Bevel,
}
impl LineJoinStyle {
pub fn id(&self) -> i64 {
match self {
LineJoinStyle::Miter => 0,
LineJoinStyle::Round => 1,
LineJoinStyle::Bevel => 2,
}
}
pub fn to_svg_string(&self) -> &'static str {
match self {
LineJoinStyle::Miter => "miter",
LineJoinStyle::Round => "round",
LineJoinStyle::Bevel => "bevel",
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum TextRenderingMode {
Fill,
Stroke,
FillStroke,
Invisible,
FillClip,
StrokeClip,
FillStrokeClip,
Clip,
}
impl TextRenderingMode {
pub fn from_i64(i: i64) -> Self {
match i {
0 => TextRenderingMode::Fill,
1 => TextRenderingMode::Stroke,
2 => TextRenderingMode::FillStroke,
3 => TextRenderingMode::Invisible,
4 => TextRenderingMode::FillClip,
5 => TextRenderingMode::StrokeClip,
6 => TextRenderingMode::FillStrokeClip,
7 => TextRenderingMode::Clip,
_ => TextRenderingMode::Fill,
}
}
pub fn id(&self) -> i64 {
match self {
TextRenderingMode::Fill => 0,
TextRenderingMode::Stroke => 1,
TextRenderingMode::FillStroke => 2,
TextRenderingMode::Invisible => 3,
TextRenderingMode::FillClip => 4,
TextRenderingMode::StrokeClip => 5,
TextRenderingMode::FillStrokeClip => 6,
TextRenderingMode::Clip => 7,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LineCapStyle {
Butt,
Round,
ProjectingSquare,
}
impl LineCapStyle {
pub fn id(&self) -> i64 {
match self {
LineCapStyle::Butt => 0,
LineCapStyle::Round => 1,
LineCapStyle::ProjectingSquare => 2,
}
}
pub fn get_svg_id(&self) -> &'static str {
match self {
LineCapStyle::Butt => "butt",
LineCapStyle::Round => "round",
LineCapStyle::ProjectingSquare => "square",
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename = "kebab-case")]
pub enum ChangedField {
LineWidth,
LineCap,
LineJoin,
MiterLimit,
LineDashPattern,
RenderingIntent,
OverprintStroke,
OverprintFill,
OverprintMode,
Font,
BlackGeneration,
BlackGenerationExtra,
UnderColorRemoval,
UnderColorRemovalExtra,
TransferFunction,
TransferFunctionExtra,
HalftoneDictionary,
FlatnessTolerance,
SmoothnessTolerance,
StrokeAdjustment,
BlendMode,
SoftMask,
CurrentStrokeAlpha,
CurrentFillAlpha,
AlphaIsShape,
TextKnockout,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
#[serde(rename_all = "kebab-case")]
pub enum BuiltinOrExternalFontId {
Builtin(BuiltinFont),
External(FontId),
}
impl BuiltinOrExternalFontId {
pub fn is_builtin(&self) -> bool {
match self {
BuiltinOrExternalFontId::Builtin(_) => true,
BuiltinOrExternalFontId::External(_) => false,
}
}
pub fn get_id(&self) -> &str {
match self {
BuiltinOrExternalFontId::Builtin(builtin_font) => builtin_font.get_id(),
BuiltinOrExternalFontId::External(font_id) => &font_id.0,
}
}
pub fn from_str(s: &str) -> Self {
if let Some(bf) = BuiltinFont::from_id(s) {
Self::Builtin(bf)
} else {
Self::External(FontId(s.to_string()))
}
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExtendedGraphicsState {
pub(crate) changed_fields: HashSet<ChangedField>,
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<BuiltinOrExternalFontId>,
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,
}
impl ExtendedGraphicsState {
pub fn line_width(&self) -> f32 {
self.line_width
}
pub fn line_cap(&self) -> LineCapStyle {
self.line_cap
}
pub fn line_join(&self) -> LineJoinStyle {
self.line_join
}
pub fn miter_limit(&self) -> f32 {
self.miter_limit
}
pub fn line_dash_pattern(&self) -> &Option<LineDashPattern> {
&self.line_dash_pattern
}
pub fn rendering_intent(&self) -> RenderingIntent {
self.rendering_intent
}
pub fn overprint_stroke(&self) -> bool {
self.overprint_stroke
}
pub fn overprint_fill(&self) -> bool {
self.overprint_fill
}
pub fn overprint_mode(&self) -> OverprintMode {
self.overprint_mode
}
pub fn font(&self) -> &Option<BuiltinOrExternalFontId> {
&self.font
}
pub fn black_generation(&self) -> &Option<BlackGenerationFunction> {
&self.black_generation
}
pub fn black_generation_extra(&self) -> &Option<BlackGenerationExtraFunction> {
&self.black_generation_extra
}
pub fn under_color_removal(&self) -> &Option<UnderColorRemovalFunction> {
&self.under_color_removal
}
pub fn under_color_removal_extra(&self) -> &Option<UnderColorRemovalExtraFunction> {
&self.under_color_removal_extra
}
pub fn transfer_function(&self) -> &Option<TransferFunction> {
&self.transfer_function
}
pub fn transfer_extra_function(&self) -> &Option<TransferExtraFunction> {
&self.transfer_extra_function
}
pub fn halftone_dictionary(&self) -> &Option<HalftoneType> {
&self.halftone_dictionary
}
pub fn flatness_tolerance(&self) -> f32 {
self.flatness_tolerance
}
pub fn smoothness_tolerance(&self) -> f32 {
self.smoothness_tolerance
}
pub fn stroke_adjustment(&self) -> bool {
self.stroke_adjustment
}
pub fn blend_mode(&self) -> &BlendMode {
&self.blend_mode
}
pub fn soft_mask(&self) -> &Option<SoftMask> {
&self.soft_mask
}
pub fn current_stroke_alpha(&self) -> f32 {
self.current_stroke_alpha
}
pub fn current_fill_alpha(&self) -> f32 {
self.current_fill_alpha
}
pub fn alpha_is_shape(&self) -> bool {
self.alpha_is_shape
}
pub fn text_knockout(&self) -> bool {
self.text_knockout
}
pub fn set_line_width(&mut self, value: f32) {
self.line_width = value;
self.changed_fields.insert(ChangedField::LineWidth);
}
pub fn set_line_cap(&mut self, value: LineCapStyle) {
self.line_cap = value;
self.changed_fields.insert(ChangedField::LineCap);
}
pub fn set_line_join(&mut self, value: LineJoinStyle) {
self.line_join = value;
self.changed_fields.insert(ChangedField::LineJoin);
}
pub fn set_miter_limit(&mut self, value: f32) {
self.miter_limit = value;
self.changed_fields.insert(ChangedField::MiterLimit);
}
pub fn set_line_dash_pattern(&mut self, value: Option<LineDashPattern>) {
self.line_dash_pattern = value;
self.changed_fields.insert(ChangedField::LineDashPattern);
}
pub fn set_rendering_intent(&mut self, value: RenderingIntent) {
self.rendering_intent = value;
self.changed_fields.insert(ChangedField::RenderingIntent);
}
pub fn set_overprint_stroke(&mut self, value: bool) {
self.overprint_stroke = value;
self.changed_fields.insert(ChangedField::OverprintStroke);
}
pub fn set_overprint_fill(&mut self, value: bool) {
self.overprint_fill = value;
self.changed_fields.insert(ChangedField::OverprintFill);
}
pub fn set_overprint_mode(&mut self, value: OverprintMode) {
self.overprint_mode = value;
self.changed_fields.insert(ChangedField::OverprintMode);
}
pub fn set_font(&mut self, value: Option<BuiltinOrExternalFontId>) {
self.font = value;
self.changed_fields.insert(ChangedField::Font);
}
pub fn set_black_generation(&mut self, value: Option<BlackGenerationFunction>) {
self.black_generation = value;
self.changed_fields.insert(ChangedField::BlackGeneration);
}
pub fn set_black_generation_extra(&mut self, value: Option<BlackGenerationExtraFunction>) {
self.black_generation_extra = value;
self.changed_fields
.insert(ChangedField::BlackGenerationExtra);
}
pub fn set_under_color_removal(&mut self, value: Option<UnderColorRemovalFunction>) {
self.under_color_removal = value;
self.changed_fields.insert(ChangedField::UnderColorRemoval);
}
pub fn set_under_color_removal_extra(&mut self, value: Option<UnderColorRemovalExtraFunction>) {
self.under_color_removal_extra = value;
self.changed_fields
.insert(ChangedField::UnderColorRemovalExtra);
}
pub fn set_transfer_function(&mut self, value: Option<TransferFunction>) {
self.transfer_function = value;
self.changed_fields.insert(ChangedField::TransferFunction);
}
pub fn set_transfer_extra_function(&mut self, value: Option<TransferExtraFunction>) {
self.transfer_extra_function = value;
self.changed_fields
.insert(ChangedField::TransferFunctionExtra);
}
pub fn set_halftone_dictionary(&mut self, value: Option<HalftoneType>) {
self.halftone_dictionary = value;
self.changed_fields.insert(ChangedField::HalftoneDictionary);
}
pub fn set_flatness_tolerance(&mut self, value: f32) {
self.flatness_tolerance = value;
self.changed_fields.insert(ChangedField::FlatnessTolerance);
}
pub fn set_smoothness_tolerance(&mut self, value: f32) {
self.smoothness_tolerance = value;
self.changed_fields
.insert(ChangedField::SmoothnessTolerance);
}
pub fn set_stroke_adjustment(&mut self, value: bool) {
self.stroke_adjustment = value;
self.changed_fields.insert(ChangedField::StrokeAdjustment);
}
pub fn set_blend_mode(&mut self, value: BlendMode) {
self.blend_mode = value;
self.changed_fields.insert(ChangedField::BlendMode);
}
pub fn set_soft_mask(&mut self, value: Option<SoftMask>) {
self.soft_mask = value;
self.changed_fields.insert(ChangedField::SoftMask);
}
pub fn set_current_stroke_alpha(&mut self, value: f32) {
self.current_stroke_alpha = value;
self.changed_fields.insert(ChangedField::CurrentStrokeAlpha);
}
pub fn set_current_fill_alpha(&mut self, value: f32) {
self.current_fill_alpha = value;
self.changed_fields.insert(ChangedField::CurrentFillAlpha);
}
pub fn set_alpha_is_shape(&mut self, value: bool) {
self.alpha_is_shape = value;
self.changed_fields.insert(ChangedField::AlphaIsShape);
}
pub fn set_text_knockout(&mut self, value: bool) {
self.text_knockout = value;
self.changed_fields.insert(ChangedField::TextKnockout);
}
pub fn has_changed(&self, field: ChangedField) -> bool {
self.changed_fields.contains(&field)
}
pub fn with_line_width(mut self, width: f32) -> Self {
self.set_line_width(width);
self
}
pub fn with_line_cap(mut self, cap: LineCapStyle) -> Self {
self.set_line_cap(cap);
self
}
pub fn with_line_join(mut self, join: LineJoinStyle) -> Self {
self.set_line_join(join);
self
}
pub fn with_miter_limit(mut self, limit: f32) -> Self {
self.set_miter_limit(limit);
self
}
pub fn with_line_dash_pattern(mut self, pattern: Option<LineDashPattern>) -> Self {
self.set_line_dash_pattern(pattern);
self
}
pub fn with_rendering_intent(mut self, intent: RenderingIntent) -> Self {
self.set_rendering_intent(intent);
self
}
pub fn with_overprint_stroke(mut self, overprint: bool) -> Self {
self.set_overprint_stroke(overprint);
self
}
pub fn with_overprint_fill(mut self, overprint: bool) -> Self {
self.set_overprint_fill(overprint);
self
}
pub fn with_overprint_mode(mut self, mode: OverprintMode) -> Self {
self.set_overprint_mode(mode);
self
}
pub fn with_font(mut self, font: Option<BuiltinOrExternalFontId>) -> Self {
self.set_font(font);
self
}
pub fn with_black_generation(mut self, func: Option<BlackGenerationFunction>) -> Self {
self.set_black_generation(func);
self
}
pub fn with_black_generation_extra(
mut self,
func: Option<BlackGenerationExtraFunction>,
) -> Self {
self.set_black_generation_extra(func);
self
}
pub fn with_under_color_removal(mut self, func: Option<UnderColorRemovalFunction>) -> Self {
self.set_under_color_removal(func);
self
}
pub fn with_under_color_removal_extra(
mut self,
func: Option<UnderColorRemovalExtraFunction>,
) -> Self {
self.set_under_color_removal_extra(func);
self
}
pub fn with_transfer_function(mut self, func: Option<TransferFunction>) -> Self {
self.set_transfer_function(func);
self
}
pub fn with_transfer_extra_function(mut self, func: Option<TransferExtraFunction>) -> Self {
self.set_transfer_extra_function(func);
self
}
pub fn with_halftone_dictionary(mut self, dict: Option<HalftoneType>) -> Self {
self.set_halftone_dictionary(dict);
self
}
pub fn with_flatness_tolerance(mut self, tolerance: f32) -> Self {
self.set_flatness_tolerance(tolerance);
self
}
pub fn with_smoothness_tolerance(mut self, tolerance: f32) -> Self {
self.set_smoothness_tolerance(tolerance);
self
}
pub fn with_stroke_adjustment(mut self, adjustment: bool) -> Self {
self.set_stroke_adjustment(adjustment);
self
}
pub fn with_blend_mode(mut self, mode: BlendMode) -> Self {
self.set_blend_mode(mode);
self
}
pub fn with_soft_mask(mut self, mask: Option<SoftMask>) -> Self {
self.set_soft_mask(mask);
self
}
pub fn with_current_stroke_alpha(mut self, alpha: f32) -> Self {
self.set_current_stroke_alpha(alpha);
self
}
pub fn with_current_fill_alpha(mut self, alpha: f32) -> Self {
self.set_current_fill_alpha(alpha);
self
}
pub fn with_alpha_is_shape(mut self, is_shape: bool) -> Self {
self.set_alpha_is_shape(is_shape);
self
}
pub fn with_text_knockout(mut self, knockout: bool) -> Self {
self.set_text_knockout(knockout);
self
}
}
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,
}
}
}
pub fn extgstate_to_dict(val: &ExtendedGraphicsState) -> LoDictionary {
use std::string::String;
use lopdf::Object::*;
let mut gs_operations = Vec::<(String, lopdf::Object)>::new();
if val.changed_fields.contains(&ChangedField::LineWidth) {
gs_operations.push(("LW".to_string(), Real(val.line_width)));
}
if val.changed_fields.contains(&ChangedField::LineCap) {
gs_operations.push(("LC".to_string(), Integer(val.line_cap.id())));
}
if val.changed_fields.contains(&ChangedField::LineJoin) {
gs_operations.push(("LJ".to_string(), Integer(val.line_join.id())));
}
if val.changed_fields.contains(&ChangedField::MiterLimit) {
gs_operations.push(("ML".to_string(), Real(val.miter_limit)));
}
if val
.changed_fields
.contains(&ChangedField::FlatnessTolerance)
{
gs_operations.push(("FL".to_string(), Real(val.flatness_tolerance)));
}
if val.changed_fields.contains(&ChangedField::RenderingIntent) {
gs_operations.push(("RI".to_string(), Name(val.rendering_intent.get_id().into())));
}
if val.changed_fields.contains(&ChangedField::StrokeAdjustment) {
gs_operations.push(("SA".to_string(), Boolean(val.stroke_adjustment)));
}
if val.changed_fields.contains(&ChangedField::OverprintFill) {
gs_operations.push(("OP".to_string(), Boolean(val.overprint_fill)));
}
if val.changed_fields.contains(&ChangedField::OverprintStroke) {
gs_operations.push(("op".to_string(), Boolean(val.overprint_stroke)));
}
if val.changed_fields.contains(&ChangedField::OverprintMode) {
gs_operations.push(("OPM".to_string(), Integer(val.overprint_mode.get_id())));
}
if val.changed_fields.contains(&ChangedField::CurrentFillAlpha) {
gs_operations.push(("CA".to_string(), Real(val.current_fill_alpha)));
}
if val
.changed_fields
.contains(&ChangedField::CurrentStrokeAlpha)
{
gs_operations.push(("ca".to_string(), Real(val.current_stroke_alpha)));
}
if val.changed_fields.contains(&ChangedField::BlendMode) {
gs_operations.push(("BM".to_string(), Name(val.blend_mode.get_id().into())));
}
if val.changed_fields.contains(&ChangedField::AlphaIsShape) {
gs_operations.push(("AIS".to_string(), Boolean(val.alpha_is_shape)));
}
if val.changed_fields.contains(&ChangedField::TextKnockout) {
gs_operations.push(("TK".to_string(), Boolean(val.text_knockout)));
}
if let Some(ldp) = val.line_dash_pattern {
if val.changed_fields.contains(&ChangedField::LineDashPattern) {
let array = ldp.as_array().into_iter().map(Integer).collect();
gs_operations.push(("D".to_string(), Array(array)));
}
}
if let Some(font) = val.font.as_ref() {
if val.changed_fields.contains(&ChangedField::Font) {
gs_operations.push(("Font".to_string(), Name(font.get_id().as_bytes().to_vec())));
}
}
if val.changed_fields.contains(&ChangedField::BlackGeneration) {
if let Some(ref _black_generation) = val.black_generation {
}
}
if val
.changed_fields
.contains(&ChangedField::BlackGenerationExtra)
{
if let Some(ref _black_generation_extra) = val.black_generation_extra {
}
}
if val
.changed_fields
.contains(&ChangedField::UnderColorRemoval)
{
if let Some(ref _under_color_removal) = val.under_color_removal {
}
}
if val
.changed_fields
.contains(&ChangedField::UnderColorRemovalExtra)
{
if let Some(ref _under_color_removal_extra) = val.under_color_removal_extra {
}
}
if val.changed_fields.contains(&ChangedField::TransferFunction) {
if let Some(ref _transfer_function) = val.transfer_function {
}
}
if val
.changed_fields
.contains(&ChangedField::TransferFunctionExtra)
{
if let Some(ref _transfer_extra_function) = val.transfer_extra_function {
}
}
if val
.changed_fields
.contains(&ChangedField::HalftoneDictionary)
{
if let Some(ref _halftone_dictionary) = val.halftone_dictionary {
}
}
if val.changed_fields.contains(&ChangedField::SoftMask) {
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()));
}
LoDictionary::from_iter(gs_operations)
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum OverprintMode {
EraseUnderlying,
KeepUnderlying,
}
impl OverprintMode {
pub fn get_id(&self) -> i64 {
match self {
OverprintMode::EraseUnderlying => 0,
OverprintMode::KeepUnderlying => 1,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BlackGenerationFunction {
Default,
WithUnderColorRemoval,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BlackGenerationExtraFunction {}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum UnderColorRemovalFunction {
Default,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum UnderColorRemovalExtraFunction {}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum TransferFunction {}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum TransferExtraFunction {}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "type", content = "data")]
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,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
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, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", untagged)]
pub enum BlendMode {
Seperable(SeperableBlendMode),
NonSeperable(NonSeperableBlendMode),
}
impl BlendMode {
pub fn normal() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Normal)
}
pub fn multiply() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Multiply)
}
pub fn screen() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Screen)
}
pub fn overlay() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Overlay)
}
pub fn darken() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Darken)
}
pub fn lighten() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Lighten)
}
pub fn color_dodge() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::ColorDodge)
}
pub fn color_burn() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::ColorBurn)
}
pub fn hard_light() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::HardLight)
}
pub fn soft_light() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::SoftLight)
}
pub fn difference() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Difference)
}
pub fn exclusion() -> BlendMode {
BlendMode::Seperable(SeperableBlendMode::Exclusion)
}
pub fn hue() -> BlendMode {
BlendMode::NonSeperable(NonSeperableBlendMode::Hue)
}
pub fn saturation() -> BlendMode {
BlendMode::NonSeperable(NonSeperableBlendMode::Saturation)
}
pub fn color() -> BlendMode {
BlendMode::NonSeperable(NonSeperableBlendMode::Color)
}
pub fn luminosity() -> BlendMode {
BlendMode::NonSeperable(NonSeperableBlendMode::Luminosity)
}
pub fn get_id(&self) -> &'static str {
use self::{BlendMode::*, NonSeperableBlendMode::*, SeperableBlendMode::*};
match self {
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",
},
}
}
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum SeperableBlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum NonSeperableBlendMode {
Hue,
Saturation,
Color,
Luminosity,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum RenderingIntent {
AbsoluteColorimetric,
RelativeColorimetric,
Saturation,
Perceptual,
}
impl RenderingIntent {
pub fn get_id(&self) -> &'static str {
use self::RenderingIntent::*;
match self {
AbsoluteColorimetric => "AbsoluteColorimetric",
RelativeColorimetric => "RelativeColorimetric",
Saturation => "Saturation",
Perceptual => "Perceptual",
}
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SoftMask {
data: Vec<u8>,
bits_per_component: u8,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum SoftMaskFunction {
GroupAlpha,
GroupLuminosity,
}