829 lines
27 KiB
Rust
829 lines
27 KiB
Rust
use crate::{
|
|
pdf::{
|
|
PdfObjectOrStreamDictionaryOrOperator, PdfObjects, PdfParser, PdfTokenizer,
|
|
object::{
|
|
NameOr, PdfDictionary, PdfMatrix, PdfName, PdfObject, PdfObjectDirect, PdfRectangle,
|
|
PdfStream, PdfStreamContents, PdfString, PdfStringBytesDebug, PdfStringOrNumber,
|
|
PdfVec2D,
|
|
},
|
|
parse::{
|
|
GetPdfInputPosition, PdfInputPosition, PdfInputPositionKnown,
|
|
PdfInputPositionNoCompare, PdfParse, PdfParseError,
|
|
},
|
|
render::{
|
|
PdfColorDeviceGray, PdfColorDeviceRgb, PdfRenderOperator, PdfRenderState,
|
|
PdfRenderingIntent,
|
|
},
|
|
},
|
|
util::ArcOrRef,
|
|
};
|
|
use std::{fmt, sync::Arc};
|
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct PdfOperatorUnparsed {
|
|
pos: PdfInputPositionNoCompare,
|
|
bytes: ArcOrRef<'static, [u8]>,
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfOperatorUnparsed {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PdfOperatorUnparsed {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
Self::debug_with_name("PdfOperatorUnparsed", &self.bytes, self.pos.0, f)
|
|
}
|
|
}
|
|
|
|
trait PdfParseIter: Sized {
|
|
fn parse_iter(iter: impl IntoIterator<Item = PdfObject>) -> Result<Self, PdfParseError>;
|
|
}
|
|
|
|
impl<T: PdfParse> PdfParseIter for Arc<[T]> {
|
|
fn parse_iter(iter: impl IntoIterator<Item = PdfObject>) -> Result<Self, PdfParseError> {
|
|
FromIterator::from_iter(iter.into_iter().map(T::parse))
|
|
}
|
|
}
|
|
|
|
impl PdfOperatorUnparsed {
|
|
pub fn new(
|
|
pos: impl Into<PdfInputPositionNoCompare>,
|
|
bytes: impl Into<ArcOrRef<'static, [u8]>>,
|
|
) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
bytes: bytes.into(),
|
|
}
|
|
}
|
|
pub const fn new_static(bytes: &'static [u8]) -> Self {
|
|
Self {
|
|
pos: PdfInputPositionNoCompare::empty(),
|
|
bytes: ArcOrRef::Ref(bytes),
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
pub fn bytes(&self) -> &ArcOrRef<'static, [u8]> {
|
|
&self.bytes
|
|
}
|
|
fn debug_with_name(
|
|
name: &str,
|
|
pdf_name: &[u8],
|
|
pos: PdfInputPosition,
|
|
f: &mut fmt::Formatter<'_>,
|
|
) -> fmt::Result {
|
|
write!(f, "{name}(at {pos}, {})", PdfStringBytesDebug(pdf_name))
|
|
}
|
|
pub fn bytes_debug(&self) -> PdfStringBytesDebug<'_> {
|
|
PdfStringBytesDebug(&self.bytes)
|
|
}
|
|
}
|
|
|
|
macro_rules! make_pdf_operator_enum {
|
|
(
|
|
$(#[$($operator_meta:tt)*])*
|
|
$operator_enum_vis:vis enum $PdfOperator:ident;
|
|
|
|
$(#[$($operator_and_operands_meta:tt)*])*
|
|
$enum_vis:vis enum $PdfOperatorAndOperands:ident {
|
|
$(#[$($unknown_variant_meta:tt)*])*
|
|
$Unknown:ident {
|
|
$(#[$($unknown_operands_meta:tt)*])*
|
|
$unknown_operands:ident: $unknown_operands_ty:ty,
|
|
$(#[$($unknown_operator_meta:tt)*])*
|
|
$unknown_operator:ident: $unknown_operator_ty:ty,
|
|
},
|
|
$(
|
|
#[kw = $kw:literal]
|
|
$(#[$($variant_meta:tt)*])*
|
|
$Variant:ident($VariantStruct:ident {
|
|
$pos:ident: PdfInputPositionNoCompare,
|
|
$(
|
|
#[$field_parse:ident($($parse_args:tt)*)]
|
|
$(#[$($field_meta:tt)*])*
|
|
$field:ident: $field_ty:ty,
|
|
)*
|
|
}),
|
|
)*
|
|
}
|
|
) => {
|
|
$(#[$($operator_meta)*])*
|
|
$operator_enum_vis enum $PdfOperator {
|
|
$(#[$($unknown_variant_meta)*])*
|
|
$Unknown($unknown_operator_ty),
|
|
$(
|
|
$(#[$($variant_meta)*])*
|
|
$Variant(PdfInputPositionNoCompare),
|
|
)*
|
|
}
|
|
|
|
impl $PdfOperator {
|
|
$operator_enum_vis fn parse(self, operands: impl IntoIterator<Item = PdfObject>) -> Result<$PdfOperatorAndOperands, PdfParseError> {
|
|
let operands = operands.into_iter();
|
|
Ok(match self {
|
|
Self::$Unknown(operator) => $PdfOperatorAndOperands::$Unknown {
|
|
operands: FromIterator::from_iter(operands.map(Into::into)),
|
|
operator,
|
|
},
|
|
$(Self::$Variant(pos) => $VariantStruct::parse(pos, operands)?.into(),)*
|
|
})
|
|
}
|
|
$operator_enum_vis fn pos(&self) -> PdfInputPosition {
|
|
match *self {
|
|
Self::$Unknown(ref operator) => operator.pos(),
|
|
$(Self::$Variant(pos) => pos.0,)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for $PdfOperator {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::$Unknown(operator) => PdfOperatorUnparsed::debug_with_name("Unknown", &operator.bytes, operator.pos.0, f),
|
|
$(Self::$Variant(pos) => PdfOperatorUnparsed::debug_with_name(stringify!($Variant), $kw, pos.0, f),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<$PdfOperator> for PdfOperatorUnparsed {
|
|
fn from(v: $PdfOperator) -> PdfOperatorUnparsed {
|
|
match v {
|
|
$PdfOperator::$Unknown(operator) => operator,
|
|
$($PdfOperator::$Variant(pos) => PdfOperatorUnparsed { pos, bytes: ArcOrRef::Ref($kw) },)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PdfOperatorUnparsed> for $PdfOperator {
|
|
fn from(v: PdfOperatorUnparsed) -> $PdfOperator {
|
|
match &**v.bytes() {
|
|
$($kw => Self::$Variant(v.pos),)*
|
|
_ => Self::$Unknown(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
$(#[derive(Clone)]
|
|
$(#[$($variant_meta)*])*
|
|
$enum_vis struct $VariantStruct {
|
|
$enum_vis $pos: PdfInputPositionNoCompare,
|
|
$(
|
|
$(#[$($field_meta)*])*
|
|
$enum_vis $field: $field_ty,
|
|
)*
|
|
}
|
|
|
|
impl fmt::Debug for $VariantStruct {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct(stringify!($VariantStruct)).field("pos", &self.pos)$(.field(stringify!($field), &self.$field))*.finish()
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for $VariantStruct {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos()
|
|
}
|
|
}
|
|
|
|
impl From<$VariantStruct> for $PdfOperatorAndOperands {
|
|
fn from(v: $VariantStruct) -> Self {
|
|
Self::$Variant(v)
|
|
}
|
|
}
|
|
|
|
impl $VariantStruct {
|
|
$enum_vis fn operator_from_pos(pos: impl Into<PdfInputPositionNoCompare>) -> $PdfOperator {
|
|
$PdfOperator::$Variant(pos.into())
|
|
}
|
|
$enum_vis fn operator(&self) -> $PdfOperator {
|
|
$PdfOperator::$Variant(self.pos)
|
|
}
|
|
$enum_vis fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
make_pdf_operator_enum! {
|
|
@impl_variant_parse
|
|
$enum_vis enum;
|
|
struct $VariantStruct {
|
|
$pos: PdfInputPositionNoCompare,
|
|
$(
|
|
#[$field_parse($($parse_args)*)]
|
|
$(#[$($field_meta)*])*
|
|
$field: $field_ty,
|
|
)*
|
|
}
|
|
})*
|
|
|
|
$(#[$($operator_and_operands_meta)*])*
|
|
$enum_vis enum $PdfOperatorAndOperands {
|
|
$(#[$($unknown_variant_meta)*])*
|
|
$Unknown {
|
|
$(#[$($unknown_operands_meta)*])*
|
|
$unknown_operands: $unknown_operands_ty,
|
|
$(#[$($unknown_operator_meta)*])*
|
|
$unknown_operator: $unknown_operator_ty,
|
|
},
|
|
$(
|
|
$(#[$($variant_meta)*])*
|
|
$Variant($VariantStruct),
|
|
)*
|
|
}
|
|
|
|
impl $PdfOperatorAndOperands {
|
|
$enum_vis fn operator(&self) -> $PdfOperator {
|
|
match self {
|
|
Self::Unknown { operator, .. } => $PdfOperator::Unknown(operator.clone()),
|
|
$(Self::$Variant(v) => v.operator(),)*
|
|
}
|
|
}
|
|
$enum_vis fn pos(&self) -> PdfInputPosition {
|
|
match self {
|
|
Self::$Unknown { operator, .. } => operator.pos(),
|
|
$(Self::$Variant(v) => v.pos(),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for $PdfOperatorAndOperands {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::$Unknown {
|
|
operands,
|
|
operator,
|
|
} => f.debug_struct("Unknown").field("operator", operator).field("operands", operands).finish(),
|
|
$(Self::$Variant($VariantStruct {
|
|
$pos,
|
|
$($field,)*
|
|
}) => f.debug_struct(stringify!($Variant)).field("pos", $pos)$(.field(stringify!($field), $field))*.finish(),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfRenderOperator for $PdfOperatorAndOperands {
|
|
fn render(&self, state: &mut PdfRenderState) -> Result<(), PdfParseError> {
|
|
match self {
|
|
Self::$Unknown {
|
|
operands,
|
|
operator,
|
|
} => state.handle_unknown_operator(operator, operands),
|
|
$(Self::$Variant(v) => <$VariantStruct as PdfRenderOperator>::render(v, state),)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
(
|
|
@impl_variant_parse
|
|
$enum_vis:vis enum;
|
|
struct $VariantStruct:ident {
|
|
$pos:ident: PdfInputPositionNoCompare,
|
|
$(
|
|
#[$field_parse:ident($($parse_args:ident),* $(,)?)]
|
|
$(#[$($field_meta:tt)*])*
|
|
$field:ident: $field_ty:ty,
|
|
)*
|
|
}
|
|
) => {
|
|
impl $VariantStruct {
|
|
$enum_vis fn parse(pos: impl Into<PdfInputPositionNoCompare>, operands: impl IntoIterator<Item = PdfObject>) -> Result<Self, PdfParseError> {
|
|
let pos = pos.into();
|
|
let mut operands = operands.into_iter();
|
|
$($(let Some($parse_args) = operands.next() else {
|
|
return Err(PdfParseError::OperatorHasTooFewOperands { operator: Self::operator_from_pos(pos) });
|
|
};)*)*
|
|
if operands.next().is_some() {
|
|
return Err(PdfParseError::OperatorHasTooManyOperands { operator: Self::operator_from_pos(pos) });
|
|
}
|
|
Ok(Self {
|
|
pos,
|
|
$($field: <$field_ty>::$field_parse($($parse_args),*)?,)*
|
|
})
|
|
}
|
|
}
|
|
};
|
|
(
|
|
@impl_variant_parse
|
|
$enum_vis:vis enum;
|
|
struct $VariantStruct:ident {
|
|
$pos:ident: PdfInputPositionNoCompare,
|
|
#[$field_parse:ident(...)]
|
|
$(#[$($field_meta:tt)*])*
|
|
$field:ident: $field_ty:ty,
|
|
}
|
|
) => {
|
|
impl $VariantStruct {
|
|
$enum_vis fn parse(pos: impl Into<PdfInputPositionNoCompare>, operands: impl IntoIterator<Item = PdfObject>) -> Result<Self, PdfParseError> {
|
|
let pos = pos.into();
|
|
let operands = operands.into_iter();
|
|
Ok(Self {
|
|
pos,
|
|
$field: <$field_ty>::$field_parse(operands)?,
|
|
})
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
make_pdf_operator_enum! {
|
|
#[derive(Clone)]
|
|
pub enum PdfOperator;
|
|
#[derive(Clone)]
|
|
pub enum PdfOperatorAndOperands {
|
|
Unknown {
|
|
operands: Arc<[PdfObjectDirect]>,
|
|
operator: PdfOperatorUnparsed,
|
|
},
|
|
#[kw = b"b"]
|
|
CloseFillAndStrokePath(PdfOperatorCloseFillAndStrokePath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"B"]
|
|
FillAndStrokePath(PdfOperatorFillAndStrokePath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"b*"]
|
|
CloseFillAndStrokePathEvenOdd(PdfOperatorCloseFillAndStrokePathEvenOdd {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"B*"]
|
|
FillAndStrokePathEvenOdd(PdfOperatorFillAndStrokePathEvenOdd {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"BDC"]
|
|
BeginMarkedContentWithProperties(PdfOperatorBeginMarkedContentWithProperties {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(tag)]
|
|
tag: PdfName,
|
|
#[parse(properties)]
|
|
properties: NameOr<PdfDictionary>,
|
|
}),
|
|
#[kw = b"BI"]
|
|
BeginInlineImage(PdfOperatorBeginInlineImage {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"BMC"]
|
|
BeginMarkedContent(PdfOperatorBeginMarkedContent {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(tag)]
|
|
tag: PdfName,
|
|
}),
|
|
#[kw = b"BT"]
|
|
BeginText(PdfOperatorBeginText {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"BX"]
|
|
BeginCompatibilitySection(PdfOperatorBeginCompatibilitySection {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"c"]
|
|
CurveTo(PdfOperatorCurveTo {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x1, y1)]
|
|
p1: PdfVec2D,
|
|
#[parse(x2, y2)]
|
|
p2: PdfVec2D,
|
|
#[parse(x3, y3)]
|
|
p3: PdfVec2D,
|
|
}),
|
|
#[kw = b"cm"]
|
|
ConcatMatrix(PdfOperatorConcatMatrix {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_flat(a, b, c, d, e, f)]
|
|
matrix: PdfMatrix,
|
|
}),
|
|
#[kw = b"CS"]
|
|
SetStrokeColorSpace(PdfOperatorSetStrokeColorSpace {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(name)]
|
|
name: PdfName,
|
|
}),
|
|
#[kw = b"cs"]
|
|
SetNonStrokeColorSpace(PdfOperatorSetNonStrokeColorSpace {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(name)]
|
|
name: PdfName,
|
|
}),
|
|
#[kw = b"d"]
|
|
SetLineDashPattern(PdfOperatorSetLineDashPattern {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(dash_array)]
|
|
dash_array: PdfObject, // TODO: actually parse
|
|
#[parse(dash_phase)]
|
|
dash_phase: PdfObject, // TODO: actually parse
|
|
}),
|
|
#[kw = b"d0"]
|
|
FontType3SetWidth(PdfOperatorFontType3SetWidth {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x, y)]
|
|
width: PdfVec2D,
|
|
}),
|
|
#[kw = b"d1"]
|
|
FontType3SetWidthAndBBox(PdfOperatorFontType3SetWidthAndBBox {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(width_x, width_y)]
|
|
width: PdfVec2D,
|
|
#[parse_flat(lower_left_x, lower_left_y, upper_right_x, upper_right_y)]
|
|
bbox: PdfRectangle,
|
|
}),
|
|
#[kw = b"Do"]
|
|
PaintXObject(PdfOperatorPaintXObject {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(name)]
|
|
name: PdfName,
|
|
}),
|
|
#[kw = b"DP"]
|
|
DesignateMarkedContentPointWithProperties(PdfOperatorDesignateMarkedContentPointWithProperties {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(tag)]
|
|
tag: PdfName,
|
|
#[parse(properties)]
|
|
properties: NameOr<PdfDictionary>,
|
|
}),
|
|
#[kw = b"EI"]
|
|
EndInlineImage(PdfOperatorEndInlineImage {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"EMC"]
|
|
EndMarkedContent(PdfOperatorEndMarkedContent {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"ET"]
|
|
EndText(PdfOperatorEndText {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"EX"]
|
|
EndCompatibilitySection(PdfOperatorEndCompatibilitySection {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"f"]
|
|
FillPath(PdfOperatorFillPath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"F"]
|
|
FillPathObsolete(PdfOperatorFillPathObsolete {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"f*"]
|
|
FillPathEvenOdd(PdfOperatorFillPathEvenOdd {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"G"]
|
|
SetStrokeGray(PdfOperatorSetStrokeGray {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(gray)]
|
|
gray: PdfColorDeviceGray,
|
|
}),
|
|
#[kw = b"g"]
|
|
SetNonStrokeGray(PdfOperatorSetNonStrokeGray {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(gray)]
|
|
gray: PdfColorDeviceGray,
|
|
}),
|
|
#[kw = b"gs"]
|
|
SetGraphicsState(PdfOperatorSetGraphicsState {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(dictionary_name)]
|
|
dictionary_name: PdfName,
|
|
}),
|
|
#[kw = b"h"]
|
|
CloseSubpath(PdfOperatorCloseSubpath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"i"]
|
|
SetFlatnessTolerance(PdfOperatorSetFlatnessTolerance {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(flatness)]
|
|
flatness: f32,
|
|
}),
|
|
#[kw = b"ID"]
|
|
BeginInlineImageData(PdfOperatorBeginInlineImageData {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"j"]
|
|
SetLineJoinStyle(PdfOperatorSetLineJoinStyle {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(line_join_style)]
|
|
line_join_style: u8, // TODO parse
|
|
}),
|
|
#[kw = b"J"]
|
|
SetLineCapStyle(PdfOperatorSetLineCapStyle {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(line_cap_style)]
|
|
line_cap_style: u8, // TODO parse
|
|
}),
|
|
#[kw = b"K"]
|
|
SetStrokeCmyk(PdfOperatorSetStrokeCmyk {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(c)]
|
|
c: f32,
|
|
#[parse(m)]
|
|
m: f32,
|
|
#[parse(y)]
|
|
y: f32,
|
|
#[parse(k)]
|
|
k: f32,
|
|
}),
|
|
#[kw = b"k"]
|
|
SetNonStrokeCmyk(PdfOperatorSetNonStrokeCmyk {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(c)]
|
|
c: f32,
|
|
#[parse(m)]
|
|
m: f32,
|
|
#[parse(y)]
|
|
y: f32,
|
|
#[parse(k)]
|
|
k: f32,
|
|
}),
|
|
#[kw = b"l"]
|
|
LineTo(PdfOperatorLineTo {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x, y)]
|
|
to: PdfVec2D,
|
|
}),
|
|
#[kw = b"m"]
|
|
MoveTo(PdfOperatorMoveTo {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x, y)]
|
|
to: PdfVec2D,
|
|
}),
|
|
#[kw = b"M"]
|
|
SetMiterLimit(PdfOperatorSetMiterLimit {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(limit)]
|
|
limit: f32,
|
|
}),
|
|
#[kw = b"MP"]
|
|
DesignateMarkedContentPoint(PdfOperatorDesignateMarkedContentPoint {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(tag)]
|
|
tag: PdfName,
|
|
}),
|
|
#[kw = b"n"]
|
|
EndPath(PdfOperatorEndPath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"q"]
|
|
SaveGraphicsState(PdfOperatorSaveGraphicsState {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"Q"]
|
|
RestoreGraphicsState(PdfOperatorRestoreGraphicsState {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"re"]
|
|
Rectangle(PdfOperatorRectangle {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x, y)]
|
|
p: PdfVec2D,
|
|
#[parse(width, height)]
|
|
size: PdfVec2D,
|
|
}),
|
|
#[kw = b"RG"]
|
|
SetStrokeRgb(PdfOperatorSetStrokeRgb {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_flat(r, g, b)]
|
|
color: PdfColorDeviceRgb,
|
|
}),
|
|
#[kw = b"rg"]
|
|
SetNonStrokeRgb(PdfOperatorSetNonStrokeRgb {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_flat(r, g, b)]
|
|
color: PdfColorDeviceRgb,
|
|
}),
|
|
#[kw = b"ri"]
|
|
SetColorRenderingIntent(PdfOperatorSetColorRenderingIntent {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(intent)]
|
|
intent: PdfRenderingIntent,
|
|
}),
|
|
#[kw = b"s"]
|
|
CloseAndStrokePath(PdfOperatorCloseAndStrokePath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"S"]
|
|
StrokePath(PdfOperatorStrokePath {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"SC"]
|
|
SetStrokeColor(PdfOperatorSetStrokeColor {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_iter(...)]
|
|
color: Arc<[f32]>,
|
|
}),
|
|
#[kw = b"sc"]
|
|
SetNonStrokeColor(PdfOperatorSetNonStrokeColor {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_iter(...)]
|
|
color: Arc<[f32]>,
|
|
}),
|
|
#[kw = b"SCN"]
|
|
SetStrokeColorWithName(PdfOperatorSetStrokeColorWithName {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_iter(...)]
|
|
color_and_name: Arc<[NameOr<f32>]>,
|
|
}),
|
|
#[kw = b"scn"]
|
|
SetNonStrokeColorWithName(PdfOperatorSetNonStrokeColorWithName {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_iter(...)]
|
|
color_and_name: Arc<[NameOr<f32>]>,
|
|
}),
|
|
#[kw = b"sh"]
|
|
Shade(PdfOperatorShade {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"T*"]
|
|
TextNextLine(PdfOperatorTextNextLine {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"Tc"]
|
|
SetCharacterSpacing(PdfOperatorSetCharacterSpacing {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(char_space)]
|
|
char_space: f32,
|
|
}),
|
|
#[kw = b"Td"]
|
|
TextNextLineWithOffset(PdfOperatorTextNextLineWithOffset {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x, y)]
|
|
offset: PdfVec2D,
|
|
}),
|
|
#[kw = b"TD"]
|
|
TextNextLineWithOffsetAndLeading(PdfOperatorTextNextLineWithOffsetAndLeading {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(x, y)]
|
|
offset: PdfVec2D,
|
|
}),
|
|
#[kw = b"Tf"]
|
|
SetFontAndSize(PdfOperatorSetFontAndSize {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(font)]
|
|
font: PdfName,
|
|
#[parse(size)]
|
|
size: f32,
|
|
}),
|
|
#[kw = b"Tj"]
|
|
ShowText(PdfOperatorShowText {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(text)]
|
|
text: PdfString,
|
|
}),
|
|
#[kw = b"TJ"]
|
|
ShowTextWithGlyphPositioning(PdfOperatorShowTextWithGlyphPositioning {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(text_and_positioning)]
|
|
text_and_positioning: Arc<[PdfStringOrNumber]>,
|
|
}),
|
|
#[kw = b"TL"]
|
|
SetTextLeading(PdfOperatorSetTextLeading {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(leading)]
|
|
leading: f32,
|
|
}),
|
|
#[kw = b"Tm"]
|
|
SetTextMatrix(PdfOperatorSetTextMatrix {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse_flat(a, b, c, d, e, f)]
|
|
matrix: PdfMatrix,
|
|
}),
|
|
#[kw = b"Tr"]
|
|
SetTextRenderingMode(PdfOperatorSetTextRenderingMode {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(rendering_mode)]
|
|
rendering_mode: u8, // TODO: parse
|
|
}),
|
|
#[kw = b"Ts"]
|
|
SetTextRise(PdfOperatorSetTextRise {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(rise)]
|
|
rise: f32,
|
|
}),
|
|
#[kw = b"Tw"]
|
|
SetWordSpacing(PdfOperatorSetWordSpacing {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(word_space)]
|
|
word_space: f32,
|
|
}),
|
|
#[kw = b"Tz"]
|
|
SetTextHorizontalScaling(PdfOperatorSetTextHorizontalScaling {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(scale_percent)]
|
|
scale_percent: f32,
|
|
}),
|
|
#[kw = b"v"]
|
|
CurveTo23(PdfOperatorCurveTo23 {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"w"]
|
|
SetLineWidth(PdfOperatorSetLineWidth {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(line_width)]
|
|
line_width: f32,
|
|
}),
|
|
#[kw = b"W"]
|
|
Clip(PdfOperatorClip {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"W*"]
|
|
ClipEvenOdd(PdfOperatorClipEvenOdd {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"y"]
|
|
CurveTo13(PdfOperatorCurveTo13 {
|
|
pos: PdfInputPositionNoCompare,
|
|
}),
|
|
#[kw = b"'"]
|
|
TextNextLineAndShow(PdfOperatorTextNextLineAndShow {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(text)]
|
|
text: PdfString,
|
|
}),
|
|
#[kw = b"\""]
|
|
SetSpacingThenTextNextLineAndShow(PdfOperatorSetSpacingThenTextNextLineAndShow {
|
|
pos: PdfInputPositionNoCompare,
|
|
#[parse(word_space)]
|
|
word_space: f32,
|
|
#[parse(char_space)]
|
|
char_space: f32,
|
|
#[parse(text)]
|
|
text: PdfString,
|
|
}),
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfOperator {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos()
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfOperatorAndOperands {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PdfContentStreamData {
|
|
pub operators: Arc<[PdfOperatorAndOperands]>,
|
|
}
|
|
|
|
impl fmt::Debug for PdfContentStreamData {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("PdfContentStreamData")
|
|
.field("operators", &self.operators)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl PdfStreamContents for PdfContentStreamData {
|
|
fn parse(
|
|
data: &[u8],
|
|
stream_pos: PdfInputPosition,
|
|
objects: Arc<PdfObjects>,
|
|
) -> Result<Self, PdfParseError> {
|
|
let mut parser = PdfParser {
|
|
objects,
|
|
tokenizer: PdfTokenizer::new(
|
|
data,
|
|
PdfInputPositionKnown {
|
|
pos: 0,
|
|
containing_streams_pos: stream_pos.get().map(|v| v.pos),
|
|
},
|
|
),
|
|
};
|
|
let mut operands = Vec::new();
|
|
let mut operators = Vec::new();
|
|
loop {
|
|
parser.skip_comments_and_whitespace();
|
|
if parser.tokenizer.peek().is_none() {
|
|
break;
|
|
}
|
|
match parser.parse_object_or_operator()? {
|
|
PdfObjectOrStreamDictionaryOrOperator::StreamDictionary {
|
|
stream_kw_pos, ..
|
|
} => return Err(PdfParseError::StreamNotAllowedHere { pos: stream_kw_pos }),
|
|
PdfObjectOrStreamDictionaryOrOperator::Object(object) => operands.push(object),
|
|
PdfObjectOrStreamDictionaryOrOperator::Operator(operator) => {
|
|
operators.push(PdfOperator::from(operator).parse(operands.drain(..))?);
|
|
}
|
|
}
|
|
}
|
|
if operands.is_empty() {
|
|
Ok(Self {
|
|
operators: operators.into(),
|
|
})
|
|
} else {
|
|
Err(PdfParseError::MissingOperator {
|
|
pos: parser.tokenizer.pos(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type PdfContentStream = PdfStream<PdfDictionary, PdfContentStreamData>;
|