use std::marker::PhantomData;
use std::num::{NonZeroU16, NonZeroU32};
use smallvec::SmallVec;
use crate::geom::Rect;
use crate::surface::Location;
include!("generated.rs");
impl TagKind {
pub fn location(&self) -> Option<Location> {
self.as_any().location
}
pub fn set_location(&mut self, location: Option<Location>) {
self.as_any_mut().location = location;
}
pub fn with_location(mut self, location: Option<Location>) -> Self {
self.as_any_mut().location = location;
self
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Tag<T> {
inner: AnyTag,
pub(crate) ty: PhantomData<T>,
}
impl<T> Tag<T> {
pub(crate) const fn new() -> Self {
Self {
inner: AnyTag::new(),
ty: PhantomData,
}
}
pub fn as_any(&self) -> &AnyTag {
&self.inner
}
pub fn as_any_mut(&mut self) -> &mut AnyTag {
&mut self.inner
}
pub fn location(&self) -> Option<Location> {
self.as_any().location
}
pub fn set_location(&mut self, location: Option<Location>) {
self.as_any_mut().location = location;
}
pub fn with_location(mut self, location: Option<Location>) -> Self {
self.as_any_mut().location = location;
self
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AnyTag {
pub location: Option<Location>,
pub(crate) attrs: OrdinalSet<Attr>,
}
impl AnyTag {
pub(crate) const fn new() -> Self {
Self {
attrs: OrdinalSet::new(),
location: None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct OrdinalSet<A> {
items: SmallVec<[A; 1]>,
}
impl<A> OrdinalSet<A> {
pub(crate) const fn new() -> Self {
Self {
items: SmallVec::new_const(),
}
}
}
impl<A: Ordinal> OrdinalSet<A> {
pub(crate) fn iter(&self) -> impl Iterator<Item = &A> {
self.items.iter()
}
pub(crate) fn set(&mut self, attr: A) {
for (i, item) in self.items.iter().enumerate() {
if item.ordinal() == attr.ordinal() {
self.items[i] = attr;
return;
}
if item.ordinal() > attr.ordinal() {
self.items.insert(i, attr);
return;
}
}
self.items.push(attr);
}
pub(crate) fn remove(&mut self, ordinal: usize) {
for (i, item) in self.items.iter().enumerate() {
if item.ordinal() == ordinal {
self.items.remove(i);
return;
}
if item.ordinal() > ordinal {
break;
}
}
}
pub(crate) fn get(&self, ordinal: usize) -> Option<&A> {
for item in self.items.iter() {
if item.ordinal() == ordinal {
return Some(item);
}
if item.ordinal() > ordinal {
break;
}
}
None
}
pub(crate) fn set_or_remove(&mut self, ordinal: usize, attr: Option<A>) {
match attr {
Some(attr) => self.set(attr),
None => self.remove(ordinal),
}
}
}
pub(crate) trait Ordinal {
fn ordinal(&self) -> usize;
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TagId(pub(crate) SmallVec<[u8; 16]>);
impl<I: IntoIterator<Item = u8>> From<I> for TagId {
fn from(value: I) -> Self {
let bytes = std::iter::once(b'U').chain(value).collect();
TagId(bytes)
}
}
impl TagId {
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ListNumbering {
None,
Disc,
Circle,
Square,
Decimal,
LowerRoman,
UpperRoman,
LowerAlpha,
UpperAlpha,
}
impl ListNumbering {
pub(crate) fn to_pdf(self) -> pdf_writer::types::ListNumbering {
match self {
ListNumbering::None => pdf_writer::types::ListNumbering::None,
ListNumbering::Disc => pdf_writer::types::ListNumbering::Disc,
ListNumbering::Circle => pdf_writer::types::ListNumbering::Circle,
ListNumbering::Square => pdf_writer::types::ListNumbering::Square,
ListNumbering::Decimal => pdf_writer::types::ListNumbering::Decimal,
ListNumbering::LowerRoman => pdf_writer::types::ListNumbering::LowerRoman,
ListNumbering::UpperRoman => pdf_writer::types::ListNumbering::UpperRoman,
ListNumbering::LowerAlpha => pdf_writer::types::ListNumbering::LowerAlpha,
ListNumbering::UpperAlpha => pdf_writer::types::ListNumbering::UpperAlpha,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TableHeaderScope {
Row,
Column,
Both,
}
impl TableHeaderScope {
pub(crate) fn to_pdf(self) -> pdf_writer::types::TableHeaderScope {
match self {
TableHeaderScope::Row => pdf_writer::types::TableHeaderScope::Row,
TableHeaderScope::Column => pdf_writer::types::TableHeaderScope::Column,
TableHeaderScope::Both => pdf_writer::types::TableHeaderScope::Both,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Placement {
Block,
#[default]
Inline,
Before,
Start,
End,
}
impl Placement {
pub(crate) fn to_pdf(self) -> pdf_writer::types::Placement {
match self {
Placement::Block => pdf_writer::types::Placement::Block,
Placement::Inline => pdf_writer::types::Placement::Inline,
Placement::Before => pdf_writer::types::Placement::Before,
Placement::Start => pdf_writer::types::Placement::Start,
Placement::End => pdf_writer::types::Placement::End,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum WritingMode {
#[default]
LrTb,
RlTb,
TbRl,
}
impl WritingMode {
pub(crate) fn to_pdf(self) -> pdf_writer::types::WritingMode {
match self {
WritingMode::LrTb => pdf_writer::types::WritingMode::LtrTtb,
WritingMode::RlTb => pdf_writer::types::WritingMode::RtlTtb,
WritingMode::TbRl => pdf_writer::types::WritingMode::TtbRtl,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BBox {
pub page_idx: usize,
pub rect: Rect,
}
impl BBox {
pub fn new(page_idx: usize, rect: Rect) -> Self {
Self { page_idx, rect }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct NaiveRgbColor {
pub red: u8,
pub green: u8,
pub blue: u8,
}
impl NaiveRgbColor {
pub fn new(red: u8, green: u8, blue: u8) -> Self {
Self { red, green, blue }
}
pub fn new_f32(red: f32, green: f32, blue: f32) -> Self {
if !(0.0..=1.0).contains(&red)
|| !(0.0..=1.0).contains(&green)
|| !(0.0..=1.0).contains(&blue)
{
panic!("RGB color components must be in the range [0.0, 1.0]");
}
Self {
red: (255.0 * red).round() as u8,
green: (255.0 * green).round() as u8,
blue: (255.0 * blue).round() as u8,
}
}
pub fn into_f32_array(self) -> [f32; 3] {
let normalize = |n| n as f32 / 255.0;
[self.red, self.green, self.blue].map(normalize)
}
}
impl From<NaiveRgbColor> for crate::graphics::color::rgb::Color {
fn from(color: NaiveRgbColor) -> Self {
crate::graphics::color::rgb::Color::new(color.red, color.green, color.blue)
}
}
impl From<NaiveRgbColor> for [f32; 3] {
fn from(color: NaiveRgbColor) -> Self {
color.into_f32_array()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum BorderStyle {
None,
Hidden,
Solid,
Dashed,
Dotted,
Double,
Groove,
Ridge,
Inset,
Outset,
}
impl BorderStyle {
pub(super) fn to_pdf(self) -> pdf_writer::types::LayoutBorderStyle {
match self {
BorderStyle::None => pdf_writer::types::LayoutBorderStyle::None,
BorderStyle::Hidden => pdf_writer::types::LayoutBorderStyle::Hidden,
BorderStyle::Solid => pdf_writer::types::LayoutBorderStyle::Solid,
BorderStyle::Dashed => pdf_writer::types::LayoutBorderStyle::Dashed,
BorderStyle::Dotted => pdf_writer::types::LayoutBorderStyle::Dotted,
BorderStyle::Double => pdf_writer::types::LayoutBorderStyle::Double,
BorderStyle::Groove => pdf_writer::types::LayoutBorderStyle::Groove,
BorderStyle::Ridge => pdf_writer::types::LayoutBorderStyle::Ridge,
BorderStyle::Inset => pdf_writer::types::LayoutBorderStyle::Inset,
BorderStyle::Outset => pdf_writer::types::LayoutBorderStyle::Outset,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TextAlign {
Start,
Center,
End,
Justify,
}
impl TextAlign {
pub(super) fn to_pdf(self) -> pdf_writer::types::TextAlign {
match self {
TextAlign::Start => pdf_writer::types::TextAlign::Start,
TextAlign::Center => pdf_writer::types::TextAlign::Center,
TextAlign::End => pdf_writer::types::TextAlign::End,
TextAlign::Justify => pdf_writer::types::TextAlign::Justify,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum BlockAlign {
Begin,
Middle,
After,
Justify,
}
impl BlockAlign {
pub(super) fn to_pdf(self) -> pdf_writer::types::BlockAlign {
match self {
BlockAlign::Begin => pdf_writer::types::BlockAlign::Before,
BlockAlign::Middle => pdf_writer::types::BlockAlign::Middle,
BlockAlign::After => pdf_writer::types::BlockAlign::After,
BlockAlign::Justify => pdf_writer::types::BlockAlign::Justify,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum InlineAlign {
Start,
Center,
End,
}
impl InlineAlign {
pub(super) fn to_pdf(self) -> pdf_writer::types::InlineAlign {
match self {
InlineAlign::Start => pdf_writer::types::InlineAlign::Start,
InlineAlign::Center => pdf_writer::types::InlineAlign::Center,
InlineAlign::End => pdf_writer::types::InlineAlign::End,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LineHeight {
Normal,
Auto,
Custom(f32),
}
impl LineHeight {
pub(super) fn to_pdf(self) -> pdf_writer::types::LineHeight {
match self {
LineHeight::Auto => pdf_writer::types::LineHeight::Auto,
LineHeight::Normal => pdf_writer::types::LineHeight::Normal,
LineHeight::Custom(height) => pdf_writer::types::LineHeight::Custom(height),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TextDecorationType {
None,
Underline,
Overline,
LineThrough,
}
impl TextDecorationType {
pub(super) fn to_pdf(self) -> pdf_writer::types::TextDecorationType {
match self {
Self::None => pdf_writer::types::TextDecorationType::None,
Self::Underline => pdf_writer::types::TextDecorationType::Underline,
Self::Overline => pdf_writer::types::TextDecorationType::Overline,
Self::LineThrough => pdf_writer::types::TextDecorationType::LineThrough,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum GlyphOrientationVertical {
Auto,
None,
Clockwise90,
CounterClockwise90,
Clockwise180,
CounterClockwise180,
Clockwise270,
}
impl GlyphOrientationVertical {
pub(super) fn to_pdf(self) -> pdf_writer::types::GlyphOrientationVertical {
let angle = match self {
GlyphOrientationVertical::Auto => {
return pdf_writer::types::GlyphOrientationVertical::Auto
}
GlyphOrientationVertical::None => 0,
GlyphOrientationVertical::Clockwise90 => 90,
GlyphOrientationVertical::CounterClockwise90 => -90,
GlyphOrientationVertical::Clockwise180 => 180,
GlyphOrientationVertical::CounterClockwise180 => -180,
GlyphOrientationVertical::Clockwise270 => 270,
};
pdf_writer::types::GlyphOrientationVertical::Angle(angle)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Sides<T> {
pub before: T,
pub after: T,
pub start: T,
pub end: T,
}
impl<T> Sides<T> {
pub fn new(before: T, after: T, start: T, end: T) -> Self {
Self {
before,
after,
start,
end,
}
}
pub fn uniform(value: T) -> Self
where
T: Copy,
{
Sides {
before: value,
after: value,
start: value,
end: value,
}
}
pub(crate) fn is_uniform(&self) -> bool
where
T: PartialEq,
{
self.before == self.after && self.before == self.start && self.before == self.end
}
pub(super) fn into_array(self) -> [T; 4] {
[self.before, self.after, self.start, self.end]
}
pub(super) fn into_pdf(self) -> pdf_writer::types::Sides<T> {
pdf_writer::types::Sides::from_array(self.into_array())
}
pub(super) fn map_pdf<P>(self, to_pdf: impl Fn(T) -> P) -> pdf_writer::types::Sides<P> {
pdf_writer::types::Sides::from_array(self.into_array().map(to_pdf))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ColumnDimensions {
All(f32),
Specific(Vec<f32>),
}
impl ColumnDimensions {
pub fn all(value: f32) -> Self {
ColumnDimensions::All(value)
}
pub fn specific(values: Vec<f32>) -> Self {
ColumnDimensions::Specific(values)
}
}