use crate::font::FontKey;
use crate::graphics::{Color, GraphicsElem};
use crate::length::Length;
use crate::math::MathGlyph;
use crate::tabular::TabularBox;
use crate::vbox::VertBox;
#[derive(Clone, Debug, PartialEq)]
pub struct HorzStringInfo {
pub font: FontKey,
pub size: Length,
pub rising: Length,
pub color: Color,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ImageId(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct HookId(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DecoId(pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GraphicsFnId(pub usize);
#[derive(Clone, Debug, PartialEq)]
pub struct ImageResource {
pub samples: Vec<u8>,
pub px_w: u32,
pub px_h: u32,
pub jpeg_dct: Option<JpegDct>,
pub pdf: Option<PdfPageResource>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PdfPageResource {
pub media_box: (f64, f64, f64, f64),
pub content: Vec<u8>,
pub resources: ImportedObjects,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ImportedObjects(pub Vec<(u32, ObjRepr)>);
#[derive(Clone, Debug, PartialEq)]
pub enum ObjRepr {
Null,
Bool(bool),
Int(i64),
Real(f64),
Name(Vec<u8>),
String(Vec<u8>),
Ref(u32),
Array(Vec<ObjRepr>),
Dict(Vec<(Vec<u8>, ObjRepr)>),
Stream(Vec<(Vec<u8>, ObjRepr)>, Vec<u8>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct JpegDct {
pub bytes: Vec<u8>,
pub components: u8,
}
impl ImageResource {
pub fn sniff_baseline_jpeg_dct(bytes: Vec<u8>) -> Option<JpegDct> {
if bytes.len() < 4 || bytes[0] != 0xFF || bytes[1] != 0xD8 {
return None; }
let mut i = 2usize;
while i < bytes.len() {
if bytes[i] != 0xFF {
return None; }
let mut j = i + 1;
while j < bytes.len() && bytes[j] == 0xFF {
j += 1;
}
if j >= bytes.len() {
return None;
}
let marker = bytes[j];
i = j + 1;
if marker == 0xD8 || marker == 0xD9 || marker == 0x01 || (0xD0..=0xD7).contains(&marker)
{
continue;
}
if i + 2 > bytes.len() {
return None;
}
let seg_len = u16::from_be_bytes([bytes[i], bytes[i + 1]]) as usize;
if seg_len < 2 || i + seg_len > bytes.len() {
return None;
}
if marker == 0xDA {
return None; }
let is_sof = (0xC0..=0xCF).contains(&marker) && ![0xC4, 0xC8, 0xCC].contains(&marker);
if is_sof {
if marker != 0xC0 && marker != 0xC1 {
return None; }
let payload = &bytes[i + 2..i + seg_len];
if payload.len() < 6 {
return None;
}
let precision = payload[0];
let components = payload[5];
return if precision == 8 && (components == 1 || components == 3) {
Some(JpegDct { bytes, components })
} else {
None
};
}
i += seg_len;
}
None
}
pub fn intrinsic_dims_pt(&self) -> (f64, f64) {
if let Some(pdf) = &self.pdf {
let (x0, y0, x1, y1) = pdf.media_box;
(x1 - x0, y1 - y0)
} else {
(self.px_w as f64, self.px_h as f64)
}
}
}
#[derive(Clone, Debug, PartialEq, syan::visit::Ast)]
#[subast(
crate::hbox::PureHorzBox,
crate::vbox::VertBox,
crate::graphics::GraphicsElem,
crate::tabular::TabularBox
)]
pub enum PureHorzBox {
InnerString {
info: HorzStringInfo,
text: String,
width: Length,
height: Length,
depth: Length,
},
OuterEmpty {
natural: Length,
shrinkable: Length,
stretchable: Length,
},
OuterFil,
FixedEmpty { width: Length },
Image {
width: Length,
height: Length,
image: ImageId,
},
Discretionary {
penalty: i32,
pre_break: Vec<PureHorzBox>,
post_break: Vec<PureHorzBox>,
no_break: Vec<PureHorzBox>,
},
Graphics {
width: Length,
height: Length,
depth: Length,
elems: Vec<GraphicsElem>,
origin_independent: bool,
},
GraphicsOuter {
height: Length,
depth: Length,
width: Length,
fn_id: GraphicsFnId,
},
Math {
width: Length,
height: Length,
depth: Length,
glyphs: Vec<MathGlyph>,
rules: Vec<GraphicsElem>,
},
HookPageBreak { id: HookId },
Tabular(TabularBox),
EmbeddedBlock {
width: Length,
height: Length,
depth: Length,
block: Vec<VertBox>,
anchor_last: bool,
breakable: bool,
},
Frame {
width: Length,
height: Length,
depth: Length,
deco: DecoId,
contents: Vec<(Length, PureHorzBox)>,
},
FrameMarker { id: DecoId, end: bool },
InlineFrameMarker {
id: DecoId,
end: bool,
height: Length,
depth: Length,
},
Footnote { block: Vec<VertBox> },
InlineMark(InlineMarkKind),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InlineMarkKind {
EmphStart { strong: bool },
EmphEnd,
BulletStart,
BulletEnd,
BreakHyphen,
}
pub const FORCED_BREAK_PENALTY: i32 = -10_000;
pub const NO_BREAK_PENALTY: i32 = i32::MAX;
impl PureHorzBox {
pub fn natural_width(&self) -> Length {
match self {
PureHorzBox::InnerString { width, .. } => *width,
PureHorzBox::OuterEmpty { natural, .. } => *natural,
PureHorzBox::OuterFil => Length::ZERO,
PureHorzBox::FixedEmpty { width } => *width,
PureHorzBox::Image { width, .. } => *width,
PureHorzBox::Discretionary { no_break, .. } => no_break
.iter()
.map(PureHorzBox::natural_width)
.fold(Length::ZERO, |acc, w| acc + w),
PureHorzBox::Graphics { width, .. } => *width,
PureHorzBox::GraphicsOuter { .. } => Length::ZERO,
PureHorzBox::Math { width, .. } => *width,
PureHorzBox::HookPageBreak { .. } => Length::ZERO,
PureHorzBox::Tabular(tab) => tab.width,
PureHorzBox::EmbeddedBlock { width, .. } => *width,
PureHorzBox::Frame { width, .. } => *width,
PureHorzBox::FrameMarker { .. } => Length::ZERO,
PureHorzBox::InlineFrameMarker { .. } => Length::ZERO,
PureHorzBox::Footnote { .. } => Length::ZERO,
PureHorzBox::InlineMark(_) => Length::ZERO,
}
}
pub fn is_glue(&self) -> bool {
matches!(
self,
PureHorzBox::OuterEmpty { .. } | PureHorzBox::OuterFil
)
}
pub fn is_break_point(&self) -> bool {
match self {
PureHorzBox::Discretionary { penalty, .. } => *penalty != NO_BREAK_PENALTY,
_ => self.is_glue(),
}
}
pub(crate) fn break_penalty(&self) -> i32 {
match self {
PureHorzBox::Discretionary { penalty, .. } => *penalty,
_ => 0,
}
}
pub(crate) fn is_forced_break(&self) -> bool {
self.break_penalty() <= FORCED_BREAK_PENALTY
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum HorzBox {
Pure(PureHorzBox),
}