#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AnnotationSubtype {
Text,
Link,
FreeText,
Line,
Square,
Circle,
Polygon,
PolyLine,
Highlight,
Underline,
Squiggly,
StrikeOut,
Stamp,
Caret,
Ink,
Popup,
FileAttachment,
Sound,
Movie,
Widget,
Screen,
PrinterMark,
TrapNet,
Watermark,
ThreeD,
Redact,
RichMedia,
Unknown,
}
impl AnnotationSubtype {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::Text => "Text",
Self::Link => "Link",
Self::FreeText => "FreeText",
Self::Line => "Line",
Self::Square => "Square",
Self::Circle => "Circle",
Self::Polygon => "Polygon",
Self::PolyLine => "PolyLine",
Self::Highlight => "Highlight",
Self::Underline => "Underline",
Self::Squiggly => "Squiggly",
Self::StrikeOut => "StrikeOut",
Self::Stamp => "Stamp",
Self::Caret => "Caret",
Self::Ink => "Ink",
Self::Popup => "Popup",
Self::FileAttachment => "FileAttachment",
Self::Sound => "Sound",
Self::Movie => "Movie",
Self::Widget => "Widget",
Self::Screen => "Screen",
Self::PrinterMark => "PrinterMark",
Self::TrapNet => "TrapNet",
Self::Watermark => "Watermark",
Self::ThreeD => "3D",
Self::Redact => "Redact",
Self::RichMedia => "RichMedia",
Self::Unknown => "Unknown",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"Text" => Self::Text,
"Link" => Self::Link,
"FreeText" => Self::FreeText,
"Line" => Self::Line,
"Square" => Self::Square,
"Circle" => Self::Circle,
"Polygon" => Self::Polygon,
"PolyLine" => Self::PolyLine,
"Highlight" => Self::Highlight,
"Underline" => Self::Underline,
"Squiggly" => Self::Squiggly,
"StrikeOut" => Self::StrikeOut,
"Stamp" => Self::Stamp,
"Caret" => Self::Caret,
"Ink" => Self::Ink,
"Popup" => Self::Popup,
"FileAttachment" => Self::FileAttachment,
"Sound" => Self::Sound,
"Movie" => Self::Movie,
"Widget" => Self::Widget,
"Screen" => Self::Screen,
"PrinterMark" => Self::PrinterMark,
"TrapNet" => Self::TrapNet,
"Watermark" => Self::Watermark,
"3D" => Self::ThreeD,
"Redact" => Self::Redact,
"RichMedia" => Self::RichMedia,
_ => Self::Unknown,
}
}
pub fn is_markup(&self) -> bool {
matches!(
self,
Self::Text
| Self::FreeText
| Self::Line
| Self::Square
| Self::Circle
| Self::Polygon
| Self::PolyLine
| Self::Highlight
| Self::Underline
| Self::Squiggly
| Self::StrikeOut
| Self::Stamp
| Self::Caret
| Self::Ink
| Self::FileAttachment
| Self::Sound
| Self::Redact
)
}
pub fn is_text_markup(&self) -> bool {
matches!(
self,
Self::Highlight | Self::Underline | Self::Squiggly | Self::StrikeOut
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct AnnotationFlags(u32);
impl AnnotationFlags {
pub const INVISIBLE: u32 = 1 << 0;
pub const HIDDEN: u32 = 1 << 1;
pub const PRINT: u32 = 1 << 2;
pub const NO_ZOOM: u32 = 1 << 3;
pub const NO_ROTATE: u32 = 1 << 4;
pub const NO_VIEW: u32 = 1 << 5;
pub const READ_ONLY: u32 = 1 << 6;
pub const LOCKED: u32 = 1 << 7;
pub const TOGGLE_NO_VIEW: u32 = 1 << 8;
pub const LOCKED_CONTENTS: u32 = 1 << 9;
pub fn new(value: u32) -> Self {
Self(value)
}
pub fn empty() -> Self {
Self(0)
}
pub fn printable() -> Self {
Self(Self::PRINT)
}
pub fn bits(&self) -> u32 {
self.0
}
pub fn contains(&self, flag: u32) -> bool {
(self.0 & flag) != 0
}
pub fn set(&mut self, flag: u32) {
self.0 |= flag;
}
pub fn clear(&mut self, flag: u32) {
self.0 &= !flag;
}
pub fn is_invisible(&self) -> bool {
self.contains(Self::INVISIBLE)
}
pub fn is_hidden(&self) -> bool {
self.contains(Self::HIDDEN)
}
pub fn is_printable(&self) -> bool {
self.contains(Self::PRINT)
}
pub fn is_no_zoom(&self) -> bool {
self.contains(Self::NO_ZOOM)
}
pub fn is_no_rotate(&self) -> bool {
self.contains(Self::NO_ROTATE)
}
pub fn is_read_only(&self) -> bool {
self.contains(Self::READ_ONLY)
}
pub fn is_locked(&self) -> bool {
self.contains(Self::LOCKED)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BorderStyleType {
#[default]
Solid,
Dashed,
Beveled,
Inset,
Underline,
}
impl BorderStyleType {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::Solid => "S",
Self::Dashed => "D",
Self::Beveled => "B",
Self::Inset => "I",
Self::Underline => "U",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"S" => Self::Solid,
"D" => Self::Dashed,
"B" => Self::Beveled,
"I" => Self::Inset,
"U" => Self::Underline,
_ => Self::Solid,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct AnnotationBorderStyle {
pub width: f32,
pub style: BorderStyleType,
pub dash_pattern: Option<Vec<f32>>,
}
impl AnnotationBorderStyle {
pub fn solid(width: f32) -> Self {
Self {
width,
style: BorderStyleType::Solid,
dash_pattern: None,
}
}
pub fn dashed(width: f32, dash: f32, gap: f32) -> Self {
Self {
width,
style: BorderStyleType::Dashed,
dash_pattern: Some(vec![dash, gap]),
}
}
pub fn none() -> Self {
Self {
width: 0.0,
style: BorderStyleType::Solid,
dash_pattern: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BorderEffectStyle {
#[default]
None,
Cloudy,
}
impl BorderEffectStyle {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::None => "S",
Self::Cloudy => "C",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"C" => Self::Cloudy,
_ => Self::None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct BorderEffect {
pub style: BorderEffectStyle,
pub intensity: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LineEndingStyle {
#[default]
None,
Square,
Circle,
Diamond,
OpenArrow,
ClosedArrow,
Butt,
ROpenArrow,
RClosedArrow,
Slash,
}
impl LineEndingStyle {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::None => "None",
Self::Square => "Square",
Self::Circle => "Circle",
Self::Diamond => "Diamond",
Self::OpenArrow => "OpenArrow",
Self::ClosedArrow => "ClosedArrow",
Self::Butt => "Butt",
Self::ROpenArrow => "ROpenArrow",
Self::RClosedArrow => "RClosedArrow",
Self::Slash => "Slash",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"None" => Self::None,
"Square" => Self::Square,
"Circle" => Self::Circle,
"Diamond" => Self::Diamond,
"OpenArrow" => Self::OpenArrow,
"ClosedArrow" => Self::ClosedArrow,
"Butt" => Self::Butt,
"ROpenArrow" => Self::ROpenArrow,
"RClosedArrow" => Self::RClosedArrow,
"Slash" => Self::Slash,
_ => Self::None,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub enum AnnotationColor {
#[default]
None,
Gray(f32),
Rgb(f32, f32, f32),
Cmyk(f32, f32, f32, f32),
}
impl AnnotationColor {
pub fn yellow() -> Self {
Self::Rgb(1.0, 1.0, 0.0)
}
pub fn red() -> Self {
Self::Rgb(1.0, 0.0, 0.0)
}
pub fn green() -> Self {
Self::Rgb(0.0, 1.0, 0.0)
}
pub fn blue() -> Self {
Self::Rgb(0.0, 0.0, 1.0)
}
pub fn black() -> Self {
Self::Gray(0.0)
}
pub fn white() -> Self {
Self::Gray(1.0)
}
pub fn to_array(&self) -> Option<Vec<f32>> {
match self {
Self::None => None,
Self::Gray(g) => Some(vec![*g]),
Self::Rgb(r, g, b) => Some(vec![*r, *g, *b]),
Self::Cmyk(c, m, y, k) => Some(vec![*c, *m, *y, *k]),
}
}
pub fn from_array(arr: &[f32]) -> Self {
match arr.len() {
0 => Self::None,
1 => Self::Gray(arr[0]),
3 => Self::Rgb(arr[0], arr[1], arr[2]),
4 => Self::Cmyk(arr[0], arr[1], arr[2], arr[3]),
_ => Self::None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextAnnotationIcon {
Comment,
Key,
#[default]
Note,
Help,
NewParagraph,
Paragraph,
Insert,
}
impl TextAnnotationIcon {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::Comment => "Comment",
Self::Key => "Key",
Self::Note => "Note",
Self::Help => "Help",
Self::NewParagraph => "NewParagraph",
Self::Paragraph => "Paragraph",
Self::Insert => "Insert",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"Comment" => Self::Comment,
"Key" => Self::Key,
"Note" => Self::Note,
"Help" => Self::Help,
"NewParagraph" => Self::NewParagraph,
"Paragraph" => Self::Paragraph,
"Insert" => Self::Insert,
_ => Self::Note,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextMarkupType {
Highlight,
Underline,
Squiggly,
StrikeOut,
}
impl TextMarkupType {
pub fn subtype(&self) -> AnnotationSubtype {
match self {
Self::Highlight => AnnotationSubtype::Highlight,
Self::Underline => AnnotationSubtype::Underline,
Self::Squiggly => AnnotationSubtype::Squiggly,
Self::StrikeOut => AnnotationSubtype::StrikeOut,
}
}
pub fn default_color(&self) -> AnnotationColor {
match self {
Self::Highlight => AnnotationColor::yellow(),
Self::Underline => AnnotationColor::green(),
Self::Squiggly => AnnotationColor::Rgb(1.0, 0.5, 0.0),
Self::StrikeOut => AnnotationColor::red(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum StampType {
Approved,
Experimental,
NotApproved,
AsIs,
Expired,
NotForPublicRelease,
Confidential,
Final,
Sold,
Departmental,
ForComment,
TopSecret,
#[default]
Draft,
ForPublicRelease,
Custom(String),
}
impl StampType {
pub fn pdf_name(&self) -> String {
match self {
Self::Approved => "Approved".to_string(),
Self::Experimental => "Experimental".to_string(),
Self::NotApproved => "NotApproved".to_string(),
Self::AsIs => "AsIs".to_string(),
Self::Expired => "Expired".to_string(),
Self::NotForPublicRelease => "NotForPublicRelease".to_string(),
Self::Confidential => "Confidential".to_string(),
Self::Final => "Final".to_string(),
Self::Sold => "Sold".to_string(),
Self::Departmental => "Departmental".to_string(),
Self::ForComment => "ForComment".to_string(),
Self::TopSecret => "TopSecret".to_string(),
Self::Draft => "Draft".to_string(),
Self::ForPublicRelease => "ForPublicRelease".to_string(),
Self::Custom(name) => name.clone(),
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"Approved" => Self::Approved,
"Experimental" => Self::Experimental,
"NotApproved" => Self::NotApproved,
"AsIs" => Self::AsIs,
"Expired" => Self::Expired,
"NotForPublicRelease" => Self::NotForPublicRelease,
"Confidential" => Self::Confidential,
"Final" => Self::Final,
"Sold" => Self::Sold,
"Departmental" => Self::Departmental,
"ForComment" => Self::ForComment,
"TopSecret" => Self::TopSecret,
"Draft" => Self::Draft,
"ForPublicRelease" => Self::ForPublicRelease,
other => Self::Custom(other.to_string()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FreeTextIntent {
#[default]
FreeText,
FreeTextCallout,
FreeTextTypeWriter,
}
impl FreeTextIntent {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::FreeText => "FreeText",
Self::FreeTextCallout => "FreeTextCallout",
Self::FreeTextTypeWriter => "FreeTextTypeWriter",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"FreeTextCallout" => Self::FreeTextCallout,
"FreeTextTypeWriter" => Self::FreeTextTypeWriter,
_ => Self::FreeText,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TextAlignment {
#[default]
Left,
Center,
Right,
}
impl TextAlignment {
pub fn to_pdf_int(&self) -> i32 {
match self {
Self::Left => 0,
Self::Center => 1,
Self::Right => 2,
}
}
pub fn from_pdf_int(value: i32) -> Self {
match value {
1 => Self::Center,
2 => Self::Right,
_ => Self::Left,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CaretSymbol {
#[default]
None,
Paragraph,
}
impl CaretSymbol {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::None => "None",
Self::Paragraph => "P",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"P" => Self::Paragraph,
_ => Self::None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FileAttachmentIcon {
GraphPushPin,
#[default]
PaperclipTag,
PushPin,
}
impl FileAttachmentIcon {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::GraphPushPin => "GraphPushPin",
Self::PaperclipTag => "PaperclipTag",
Self::PushPin => "PushPin",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"GraphPushPin" => Self::GraphPushPin,
"PushPin" => Self::PushPin,
_ => Self::PaperclipTag,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ReplyType {
#[default]
Reply,
Group,
}
impl ReplyType {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::Reply => "R",
Self::Group => "Group",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"Group" => Self::Group,
_ => Self::Reply,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HighlightMode {
None,
#[default]
Invert,
Outline,
Push,
}
impl HighlightMode {
pub fn pdf_name(&self) -> &'static str {
match self {
Self::None => "N",
Self::Invert => "I",
Self::Outline => "O",
Self::Push => "P",
}
}
pub fn from_pdf_name(name: &str) -> Self {
match name {
"N" => Self::None,
"O" => Self::Outline,
"P" => Self::Push,
_ => Self::Invert,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub enum WidgetFieldType {
#[default]
Text,
Checkbox {
checked: bool,
},
Radio {
selected: Option<String>,
},
Button,
Choice {
options: Vec<String>,
selected: Option<String>,
},
Signature,
Unknown,
}
pub type QuadPoint = [f64; 8];
pub mod quad_points {
use super::QuadPoint;
use crate::geometry::Rect;
pub fn from_rect(rect: &Rect) -> QuadPoint {
let x1 = rect.x as f64;
let y1 = rect.y as f64;
let x2 = (rect.x + rect.width) as f64;
let y2 = rect.y as f64;
let x3 = (rect.x + rect.width) as f64;
let y3 = (rect.y + rect.height) as f64;
let x4 = rect.x as f64;
let y4 = (rect.y + rect.height) as f64;
[x1, y1, x2, y2, x3, y3, x4, y4]
}
pub fn bounding_rect(quad: &QuadPoint) -> Rect {
let min_x = quad[0].min(quad[2]).min(quad[4]).min(quad[6]) as f32;
let max_x = quad[0].max(quad[2]).max(quad[4]).max(quad[6]) as f32;
let min_y = quad[1].min(quad[3]).min(quad[5]).min(quad[7]) as f32;
let max_y = quad[1].max(quad[3]).max(quad[5]).max(quad[7]) as f32;
Rect::new(min_x, min_y, max_x - min_x, max_y - min_y)
}
pub fn parse(arr: &[f64]) -> Vec<QuadPoint> {
arr.as_chunks::<8>()
.0
.iter()
.map(|chunk| {
[
chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
]
})
.collect()
}
pub fn flatten(quads: &[QuadPoint]) -> Vec<f64> {
quads.iter().flat_map(|q| q.iter().copied()).collect()
}
}
#[cfg(test)]
mod tests;