use super::*;
use crate::object::TextStrLike;
pub struct Content {
buf: Buf,
q_depth: usize,
}
impl Content {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self::with_capacity(1024)
}
pub fn with_capacity(capacity: usize) -> Self {
Self { buf: Buf::with_capacity(capacity), q_depth: 0 }
}
#[inline]
pub fn op<'a>(&'a mut self, operator: &'a str) -> Operation<'a> {
Operation::start(&mut self.buf, operator)
}
pub fn finish(mut self) -> Buf {
if self.buf.last() == Some(&b'\n') {
self.buf.inner.pop();
}
self.buf
}
}
pub struct Operation<'a> {
buf: &'a mut Buf,
op: &'a str,
first: bool,
}
impl<'a> Operation<'a> {
#[inline]
pub(crate) fn start(buf: &'a mut Buf, op: &'a str) -> Self {
Self { buf, op, first: true }
}
#[inline]
pub fn operand<T: Primitive>(&mut self, value: T) -> &mut Self {
self.obj().primitive(value);
self
}
#[inline]
pub fn operands<T, I>(&mut self, values: I) -> &mut Self
where
T: Primitive,
I: IntoIterator<Item = T>,
{
for value in values {
self.operand(value);
}
self
}
#[inline]
pub fn obj(&mut self) -> Obj<'_> {
if !self.first {
self.buf.push(b' ');
}
self.first = false;
Obj::direct(self.buf, 0)
}
}
impl Drop for Operation<'_> {
#[inline]
fn drop(&mut self) {
if !self.first {
self.buf.push(b' ');
}
self.buf.extend(self.op.as_bytes());
self.buf.push(b'\n');
}
}
impl Content {
#[inline]
pub fn set_line_width(&mut self, width: f32) -> &mut Self {
assert!(width >= 0.0, "line width must be positive");
self.op("w").operand(width);
self
}
#[inline]
pub fn set_line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
self.op("J").operand(cap.to_int());
self
}
#[inline]
pub fn set_line_join(&mut self, join: LineJoinStyle) -> &mut Self {
self.op("j").operand(join.to_int());
self
}
#[inline]
pub fn set_miter_limit(&mut self, limit: f32) -> &mut Self {
self.op("M").operand(limit);
self
}
#[inline]
pub fn set_dash_pattern(
&mut self,
array: impl IntoIterator<Item = f32>,
phase: f32,
) -> &mut Self {
let mut op = self.op("d");
op.obj().array().items(array);
op.operand(phase);
op.finish();
self
}
#[inline]
pub fn set_rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
self.op("ri").operand(intent.to_name());
self
}
#[inline]
pub fn set_flatness(&mut self, tolerance: i32) -> &mut Self {
assert!(
matches!(tolerance, 0..=100),
"flatness tolerance must be between 0 and 100",
);
self.op("i").operand(tolerance);
self
}
#[inline]
pub fn set_parameters(&mut self, dict: Name) -> &mut Self {
self.op("gs").operand(dict);
self
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum LineCapStyle {
ButtCap,
RoundCap,
ProjectingSquareCap,
}
impl LineCapStyle {
#[inline]
pub(crate) fn to_int(self) -> i32 {
match self {
Self::ButtCap => 0,
Self::RoundCap => 1,
Self::ProjectingSquareCap => 2,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum LineJoinStyle {
MiterJoin,
RoundJoin,
BevelJoin,
}
impl LineJoinStyle {
#[inline]
pub(crate) fn to_int(self) -> i32 {
match self {
Self::MiterJoin => 0,
Self::RoundJoin => 1,
Self::BevelJoin => 2,
}
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum RenderingIntent {
AbsoluteColorimetric,
#[default]
RelativeColorimetric,
Saturation,
Perceptual,
}
impl RenderingIntent {
#[inline]
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::AbsoluteColorimetric => Name(b"AbsoluteColorimetric"),
Self::RelativeColorimetric => Name(b"RelativeColorimetric"),
Self::Saturation => Name(b"Saturation"),
Self::Perceptual => Name(b"Perceptual"),
}
}
}
impl Content {
#[inline]
pub fn save_state(&mut self) -> &mut Self {
self.op("q");
self.q_depth = self.q_depth.saturating_add(1);
self
}
#[inline]
pub fn restore_state(&mut self) -> &mut Self {
self.op("Q");
self.q_depth = self.q_depth.saturating_sub(1);
self
}
#[inline]
pub fn state_nesting_depth(&self) -> usize {
self.q_depth
}
#[inline]
pub fn transform(&mut self, matrix: [f32; 6]) -> &mut Self {
self.op("cm").operands(matrix);
self
}
}
impl Content {
#[inline]
pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
self.op("m").operands([x, y]);
self
}
#[inline]
pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
self.op("l").operands([x, y]);
self
}
#[inline]
pub fn cubic_to(
&mut self,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
x3: f32,
y3: f32,
) -> &mut Self {
self.op("c").operands([x1, y1, x2, y2, x3, y3]);
self
}
#[inline]
pub fn cubic_to_initial(&mut self, x2: f32, y2: f32, x3: f32, y3: f32) -> &mut Self {
self.op("v").operands([x2, y2, x3, y3]);
self
}
#[inline]
pub fn cubic_to_final(&mut self, x1: f32, y1: f32, x3: f32, y3: f32) -> &mut Self {
self.op("y").operands([x1, y1, x3, y3]);
self
}
#[inline]
pub fn close_path(&mut self) -> &mut Self {
self.op("h");
self
}
#[inline]
pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32) -> &mut Self {
self.op("re").operands([x, y, width, height]);
self
}
}
impl Content {
#[inline]
pub fn stroke(&mut self) -> &mut Self {
self.op("S");
self
}
#[inline]
pub fn close_and_stroke(&mut self) -> &mut Self {
self.op("s");
self
}
#[inline]
pub fn fill_nonzero(&mut self) -> &mut Self {
self.op("f");
self
}
#[inline]
pub fn fill_even_odd(&mut self) -> &mut Self {
self.op("f*");
self
}
#[inline]
pub fn fill_nonzero_and_stroke(&mut self) -> &mut Self {
self.op("B");
self
}
#[inline]
pub fn fill_even_odd_and_stroke(&mut self) -> &mut Self {
self.op("B*");
self
}
#[inline]
pub fn close_fill_nonzero_and_stroke(&mut self) -> &mut Self {
self.op("b");
self
}
#[inline]
pub fn close_fill_even_odd_and_stroke(&mut self) -> &mut Self {
self.op("b*");
self
}
#[inline]
pub fn end_path(&mut self) -> &mut Self {
self.op("n");
self
}
}
impl Content {
#[inline]
pub fn clip_nonzero(&mut self) -> &mut Self {
self.op("W");
self
}
#[inline]
pub fn clip_even_odd(&mut self) -> &mut Self {
self.op("W*");
self
}
}
impl Content {
#[inline]
pub fn begin_text(&mut self) -> &mut Self {
self.op("BT");
self
}
#[inline]
pub fn end_text(&mut self) -> &mut Self {
self.op("ET");
self
}
}
impl Content {
#[inline]
pub fn set_char_spacing(&mut self, spacing: f32) -> &mut Self {
self.op("Tc").operand(spacing);
self
}
#[inline]
pub fn set_word_spacing(&mut self, spacing: f32) -> &mut Self {
self.op("Tw").operand(spacing);
self
}
#[inline]
pub fn set_horizontal_scaling(&mut self, scaling: f32) -> &mut Self {
self.op("Tz").operand(scaling);
self
}
#[inline]
pub fn set_leading(&mut self, leading: f32) -> &mut Self {
self.op("TL").operand(leading);
self
}
#[inline]
pub fn set_font(&mut self, font: Name, size: f32) -> &mut Self {
self.op("Tf").operand(font).operand(size);
self
}
#[inline]
pub fn set_text_rendering_mode(&mut self, mode: TextRenderingMode) -> &mut Self {
self.op("Tr").operand(mode.to_int());
self
}
#[inline]
pub fn set_rise(&mut self, rise: f32) -> &mut Self {
self.op("Ts").operand(rise);
self
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum TextRenderingMode {
#[default]
Fill,
Stroke,
FillStroke,
Invisible,
FillClip,
StrokeClip,
FillStrokeClip,
Clip,
}
impl TextRenderingMode {
#[inline]
pub(crate) fn to_int(self) -> i32 {
match self {
Self::Fill => 0,
Self::Stroke => 1,
Self::FillStroke => 2,
Self::Invisible => 3,
Self::FillClip => 4,
Self::StrokeClip => 5,
Self::FillStrokeClip => 6,
Self::Clip => 7,
}
}
}
impl Content {
#[inline]
pub fn next_line(&mut self, x: f32, y: f32) -> &mut Self {
self.op("Td").operands([x, y]);
self
}
#[inline]
pub fn next_line_and_set_leading(&mut self, x: f32, y: f32) -> &mut Self {
self.op("TD").operands([x, y]);
self
}
#[inline]
pub fn set_text_matrix(&mut self, matrix: [f32; 6]) -> &mut Self {
self.op("Tm").operands(matrix);
self
}
#[inline]
pub fn next_line_using_leading(&mut self) -> &mut Self {
self.op("T*");
self
}
}
impl Content {
#[inline]
pub fn show(&mut self, text: Str) -> &mut Self {
self.op("Tj").operand(text);
self
}
#[inline]
pub fn next_line_show(&mut self, text: Str) -> &mut Self {
self.op("'").operand(text);
self
}
#[inline]
pub fn next_line_show_and_set_word_and_char_spacing(
&mut self,
word_spacing: f32,
char_spacing: f32,
text: Str,
) -> &mut Self {
self.op("\"").operands([word_spacing, char_spacing]).operand(text);
self
}
#[inline]
pub fn show_positioned(&mut self) -> ShowPositioned<'_> {
ShowPositioned::start(self.op("TJ"))
}
}
pub struct ShowPositioned<'a> {
op: Operation<'a>,
}
impl<'a> ShowPositioned<'a> {
#[inline]
pub(crate) fn start(op: Operation<'a>) -> Self {
Self { op }
}
#[inline]
pub fn items(&mut self) -> PositionedItems<'_> {
PositionedItems::start(self.op.obj())
}
}
deref!('a, ShowPositioned<'a> => Operation<'a>, op);
pub struct PositionedItems<'a> {
array: Array<'a>,
}
impl<'a> PositionedItems<'a> {
#[inline]
pub(crate) fn start(obj: Obj<'a>) -> Self {
Self { array: obj.array() }
}
#[inline]
pub fn show(&mut self, text: Str) -> &mut Self {
self.array.item(text);
self
}
#[inline]
pub fn adjust(&mut self, amount: f32) -> &mut Self {
self.array.item(amount);
self
}
}
deref!('a, PositionedItems<'a> => Array<'a>, array);
impl Content {
#[inline]
pub fn start_color_glyph(&mut self, wx: f32) -> &mut Self {
self.op("d0").operands([wx, 0.0]);
self
}
#[inline]
pub fn start_shape_glyph(
&mut self,
wx: f32,
ll_x: f32,
ll_y: f32,
ur_x: f32,
ur_y: f32,
) -> &mut Self {
self.op("d1").operands([wx, 0.0, ll_x, ll_y, ur_x, ur_y]);
self
}
}
impl Content {
#[inline]
pub fn set_stroke_color_space<'a>(
&mut self,
space: impl Into<ColorSpaceOperand<'a>>,
) -> &mut Self {
self.op("CS").operand(space.into().to_name());
self
}
#[inline]
pub fn set_fill_color_space<'a>(
&mut self,
space: impl Into<ColorSpaceOperand<'a>>,
) -> &mut Self {
self.op("cs").operand(space.into().to_name());
self
}
#[inline]
pub fn set_stroke_color(
&mut self,
color: impl IntoIterator<Item = f32>,
) -> &mut Self {
self.op("SCN").operands(color);
self
}
#[inline]
pub fn set_stroke_pattern(
&mut self,
tint: impl IntoIterator<Item = f32>,
name: Name,
) -> &mut Self {
self.op("SCN").operands(tint).operand(name);
self
}
#[inline]
pub fn set_fill_color(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
self.op("scn").operands(color);
self
}
#[inline]
pub fn set_fill_pattern(
&mut self,
tint: impl IntoIterator<Item = f32>,
name: Name,
) -> &mut Self {
self.op("scn").operands(tint).operand(name);
self
}
#[inline]
pub fn set_stroke_gray(&mut self, gray: f32) -> &mut Self {
self.op("G").operand(gray);
self
}
#[inline]
pub fn set_fill_gray(&mut self, gray: f32) -> &mut Self {
self.op("g").operand(gray);
self
}
#[inline]
pub fn set_stroke_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
self.op("RG").operands([r, g, b]);
self
}
#[inline]
pub fn set_fill_rgb(&mut self, r: f32, g: f32, b: f32) -> &mut Self {
self.op("rg").operands([r, g, b]);
self
}
#[inline]
pub fn set_stroke_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
self.op("K").operands([c, m, y, k]);
self
}
#[inline]
pub fn set_fill_cmyk(&mut self, c: f32, m: f32, y: f32, k: f32) -> &mut Self {
self.op("k").operands([c, m, y, k]);
self
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ColorSpaceOperand<'a> {
DeviceGray,
DeviceRgb,
DeviceCmyk,
Pattern,
Named(Name<'a>),
}
impl<'a> ColorSpaceOperand<'a> {
#[inline]
pub(crate) fn to_name(self) -> Name<'a> {
match self {
Self::DeviceGray => Name(b"DeviceGray"),
Self::DeviceRgb => Name(b"DeviceRGB"),
Self::DeviceCmyk => Name(b"DeviceCMYK"),
Self::Pattern => Name(b"Pattern"),
Self::Named(name) => name,
}
}
}
impl<'a> From<Name<'a>> for ColorSpaceOperand<'a> {
fn from(name: Name<'a>) -> Self {
Self::Named(name)
}
}
impl Content {
#[inline]
pub fn shading(&mut self, shading: Name) -> &mut Self {
self.op("sh").operand(shading);
self
}
}
impl Content {
#[inline]
pub fn x_object(&mut self, name: Name) -> &mut Self {
self.op("Do").operand(name);
self
}
}
impl Content {
#[inline]
pub fn marked_content_point(&mut self, tag: Name) -> &mut Self {
self.op("MP").operand(tag);
self
}
#[inline]
pub fn marked_content_point_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
let mut op = self.op("DP");
op.operand(tag);
MarkContent::start(op)
}
#[inline]
pub fn begin_marked_content(&mut self, tag: Name) -> &mut Self {
self.op("BMC").operand(tag);
self
}
#[inline]
pub fn begin_marked_content_with_properties(&mut self, tag: Name) -> MarkContent<'_> {
let mut op = self.op("BDC");
op.operand(tag);
MarkContent::start(op)
}
#[inline]
pub fn end_marked_content(&mut self) -> &mut Self {
self.op("EMC");
self
}
}
pub struct MarkContent<'a> {
op: Operation<'a>,
}
impl<'a> MarkContent<'a> {
#[inline]
pub(crate) fn start(op: Operation<'a>) -> Self {
Self { op }
}
#[inline]
pub fn properties(&mut self) -> PropertyList<'_> {
self.op.obj().start()
}
#[inline]
pub fn properties_named(mut self, name: Name) {
self.op.operand(name);
}
}
deref!('a, MarkContent<'a> => Operation<'a>, op);
pub struct PropertyList<'a> {
dict: Dict<'a>,
}
writer!(PropertyList: |obj| Self { dict: obj.dict() });
impl<'a> PropertyList<'a> {
#[inline]
pub fn identify(&mut self, identifier: i32) -> &mut Self {
self.pair(Name(b"MCID"), identifier);
self
}
#[inline]
pub fn actual_text(&mut self, text: impl TextStrLike) -> &mut Self {
self.pair(Name(b"ActualText"), text);
self
}
#[inline]
pub fn artifact(self) -> Artifact<'a> {
Artifact::start_with_dict(self.dict)
}
}
deref!('a, PropertyList<'a> => Dict<'a>, dict);
pub struct Artifact<'a> {
dict: Dict<'a>,
}
writer!(Artifact: |obj| Self::start_with_dict(obj.dict()));
impl<'a> Artifact<'a> {
#[inline]
pub(crate) fn start_with_dict(dict: Dict<'a>) -> Self {
Self { dict }
}
#[inline]
pub fn kind(&mut self, kind: ArtifactType) -> &mut Self {
self.pair(Name(b"Type"), kind.to_name());
self
}
#[inline]
pub fn subtype(&mut self, subtype: ArtifactSubtype) -> &mut Self {
self.pair(Name(b"Subtype"), subtype.to_name());
self
}
#[inline]
pub fn bounding_box(&mut self, bbox: Rect) -> &mut Self {
self.pair(Name(b"BBox"), bbox);
self
}
#[inline]
pub fn attached(
&mut self,
attachment: impl IntoIterator<Item = ArtifactAttachment>,
) -> &mut Self {
self.insert(Name(b"Attached"))
.array()
.typed()
.items(attachment.into_iter().map(ArtifactAttachment::to_name));
self
}
}
deref!('a, Artifact<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ArtifactType {
Pagination,
Layout,
Page,
Background,
Inline,
}
impl ArtifactType {
#[inline]
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::Pagination => Name(b"Pagination"),
Self::Layout => Name(b"Layout"),
Self::Page => Name(b"Page"),
Self::Background => Name(b"Background"),
Self::Inline => Name(b"Inline"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ArtifactSubtype<'a> {
Header,
Footer,
Watermark,
PageNumber,
Bates,
LineNumber,
Redaction,
Custom(Name<'a>),
}
impl<'a> ArtifactSubtype<'a> {
#[inline]
pub(crate) fn to_name(self) -> Name<'a> {
match self {
Self::Header => Name(b"Header"),
Self::Footer => Name(b"Footer"),
Self::Watermark => Name(b"Watermark"),
Self::PageNumber => Name(b"PageNum"),
Self::Bates => Name(b"Bates"),
Self::LineNumber => Name(b"LineNum"),
Self::Redaction => Name(b"Redaction"),
Self::Custom(name) => name,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum ArtifactAttachment {
Left,
Top,
Right,
Bottom,
}
impl ArtifactAttachment {
#[inline]
pub(crate) fn to_name(self) -> Name<'static> {
match self {
Self::Left => Name(b"Left"),
Self::Top => Name(b"Top"),
Self::Right => Name(b"Right"),
Self::Bottom => Name(b"Bottom"),
}
}
}
impl Content {
#[inline]
pub fn begin_compat(&mut self) -> &mut Self {
self.op("BX");
self
}
#[inline]
pub fn end_compat(&mut self) -> &mut Self {
self.op("EX");
self
}
}
pub struct Resources<'a> {
dict: Dict<'a>,
}
writer!(Resources: |obj| Self { dict: obj.dict() });
impl Resources<'_> {
pub fn x_objects(&mut self) -> Dict<'_> {
self.insert(Name(b"XObject")).dict()
}
pub fn fonts(&mut self) -> Dict<'_> {
self.insert(Name(b"Font")).dict()
}
pub fn color_spaces(&mut self) -> Dict<'_> {
self.insert(Name(b"ColorSpace")).dict()
}
pub fn patterns(&mut self) -> Dict<'_> {
self.insert(Name(b"Pattern")).dict()
}
pub fn shadings(&mut self) -> Dict<'_> {
self.insert(Name(b"Shading")).dict()
}
pub fn ext_g_states(&mut self) -> Dict<'_> {
self.insert(Name(b"ExtGState")).dict()
}
pub fn proc_sets(&mut self, sets: impl IntoIterator<Item = ProcSet>) -> &mut Self {
self.insert(Name(b"ProcSet"))
.array()
.items(sets.into_iter().map(ProcSet::to_name));
self
}
pub fn proc_sets_all(&mut self) -> &mut Self {
self.proc_sets([
ProcSet::Pdf,
ProcSet::Text,
ProcSet::ImageGrayscale,
ProcSet::ImageColor,
ProcSet::ImageIndexed,
])
}
pub fn properties(&mut self) -> TypedDict<'_, PropertyList<'_>> {
self.insert(Name(b"Properties")).dict().typed()
}
}
deref!('a, Resources<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ProcSet {
Pdf,
Text,
ImageGrayscale,
ImageColor,
ImageIndexed,
}
impl ProcSet {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
ProcSet::Pdf => Name(b"PDF"),
ProcSet::Text => Name(b"Text"),
ProcSet::ImageGrayscale => Name(b"ImageB"),
ProcSet::ImageColor => Name(b"ImageC"),
ProcSet::ImageIndexed => Name(b"ImageI"),
}
}
}
pub struct ExtGraphicsState<'a> {
dict: Dict<'a>,
}
writer!(ExtGraphicsState: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"ExtGState"));
Self { dict }
});
impl ExtGraphicsState<'_> {
pub fn line_width(&mut self, width: f32) -> &mut Self {
self.pair(Name(b"LW"), width);
self
}
pub fn line_cap(&mut self, cap: LineCapStyle) -> &mut Self {
self.pair(Name(b"LC"), cap.to_int());
self
}
pub fn line_join(&mut self, join: LineJoinStyle) -> &mut Self {
self.pair(Name(b"LJ"), join.to_int());
self
}
pub fn miter_limit(&mut self, limit: f32) -> &mut Self {
self.pair(Name(b"ML"), limit);
self
}
pub fn dash_pattern(
&mut self,
pattern: impl IntoIterator<Item = f32>,
phase: f32,
) -> &mut Self {
let mut array = self.insert(Name(b"D")).array();
array.push().array().items(pattern);
array.item(phase);
array.finish();
self
}
pub fn rendering_intent(&mut self, intent: RenderingIntent) -> &mut Self {
self.pair(Name(b"RI"), intent.to_name());
self
}
pub fn overprint(&mut self, overprint: bool) -> &mut Self {
self.pair(Name(b"OP"), overprint);
self
}
pub fn overprint_fill(&mut self, overprint: bool) -> &mut Self {
self.pair(Name(b"op"), overprint);
self
}
pub fn overprint_mode(&mut self, mode: OverprintMode) -> &mut Self {
self.pair(Name(b"OPM"), mode.to_int());
self
}
pub fn font(&mut self, font: Name, size: f32) -> &mut Self {
let mut array = self.insert(Name(b"Font")).array();
array.item(font);
array.item(size);
array.finish();
self
}
pub fn black_generation(&mut self, func: Ref) -> &mut Self {
self.pair(Name(b"BG"), func);
self
}
pub fn black_generation_default(&mut self) -> &mut Self {
self.pair(Name(b"BG2"), Name(b"Default"));
self
}
pub fn undercolor_removal(&mut self, func: Ref) -> &mut Self {
self.pair(Name(b"UCR"), func);
self
}
pub fn undercolor_removal_default(&mut self) -> &mut Self {
self.pair(Name(b"UCR2"), Name(b"Default"));
self
}
pub fn transfer(&mut self, func: Ref) -> &mut Self {
self.pair(Name(b"TR"), func);
self
}
pub fn transfer_default(&mut self) -> &mut Self {
self.pair(Name(b"TR2"), Name(b"Default"));
self
}
pub fn halftone(&mut self, ht: Ref) -> &mut Self {
self.pair(Name(b"HT"), ht);
self
}
pub fn halftone_default(&mut self) -> &mut Self {
self.pair(Name(b"HT"), Name(b"Default"));
self
}
pub fn flatness(&mut self, tolerance: f32) -> &mut Self {
self.pair(Name(b"FL"), tolerance);
self
}
pub fn smoothness(&mut self, tolerance: f32) -> &mut Self {
self.pair(Name(b"SM"), tolerance);
self
}
pub fn stroke_adjustment(&mut self, adjust: bool) -> &mut Self {
self.pair(Name(b"SA"), adjust);
self
}
pub fn blend_mode(&mut self, mode: BlendMode) -> &mut Self {
self.pair(Name(b"BM"), mode.to_name());
self
}
pub fn soft_mask(&mut self) -> SoftMask<'_> {
self.insert(Name(b"SMask")).start()
}
pub fn soft_mask_name(&mut self, mask: Name) -> &mut Self {
self.pair(Name(b"SMask"), mask);
self
}
pub fn stroking_alpha(&mut self, alpha: f32) -> &mut Self {
self.pair(Name(b"CA"), alpha);
self
}
pub fn non_stroking_alpha(&mut self, alpha: f32) -> &mut Self {
self.pair(Name(b"ca"), alpha);
self
}
pub fn alpha_source(&mut self, source: bool) -> &mut Self {
self.pair(Name(b"AIS"), source);
self
}
pub fn text_knockout(&mut self, knockout: bool) -> &mut Self {
self.pair(Name(b"TK"), knockout);
self
}
}
deref!('a, ExtGraphicsState<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
#[allow(missing_docs)]
pub enum BlendMode {
#[default]
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
}
impl BlendMode {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
BlendMode::Normal => Name(b"Normal"),
BlendMode::Multiply => Name(b"Multiply"),
BlendMode::Screen => Name(b"Screen"),
BlendMode::Overlay => Name(b"Overlay"),
BlendMode::Darken => Name(b"Darken"),
BlendMode::Lighten => Name(b"Lighten"),
BlendMode::ColorDodge => Name(b"ColorDodge"),
BlendMode::ColorBurn => Name(b"ColorBurn"),
BlendMode::HardLight => Name(b"HardLight"),
BlendMode::SoftLight => Name(b"SoftLight"),
BlendMode::Difference => Name(b"Difference"),
BlendMode::Exclusion => Name(b"Exclusion"),
BlendMode::Hue => Name(b"Hue"),
BlendMode::Saturation => Name(b"Saturation"),
BlendMode::Color => Name(b"Color"),
BlendMode::Luminosity => Name(b"Luminosity"),
}
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
pub enum OverprintMode {
#[default]
OverrideAllColorants,
IgnoreZeroChannel,
}
impl OverprintMode {
pub(crate) fn to_int(self) -> i32 {
match self {
OverprintMode::OverrideAllColorants => 0,
OverprintMode::IgnoreZeroChannel => 1,
}
}
}
pub struct SoftMask<'a> {
dict: Dict<'a>,
}
writer!(SoftMask: |obj| {
let mut dict = obj.dict();
dict.pair(Name(b"Type"), Name(b"Mask"));
Self { dict }
});
impl SoftMask<'_> {
pub fn subtype(&mut self, subtype: MaskType) -> &mut Self {
self.pair(Name(b"S"), subtype.to_name());
self
}
pub fn group(&mut self, group: Ref) -> &mut Self {
self.pair(Name(b"G"), group);
self
}
pub fn backdrop(&mut self, color: impl IntoIterator<Item = f32>) -> &mut Self {
self.insert(Name(b"BC")).array().items(color);
self
}
pub fn transfer_function(&mut self, function: Ref) -> &mut Self {
self.pair(Name(b"TR"), function);
self
}
}
deref!('a, SoftMask<'a> => Dict<'a>, dict);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum MaskType {
Alpha,
Luminosity,
}
impl MaskType {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
MaskType::Alpha => Name(b"Alpha"),
MaskType::Luminosity => Name(b"Luminosity"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_content_encoding() {
let mut content = Content::new();
content
.save_state()
.rect(1.0, 2.0, 3.0, 4.0)
.fill_nonzero()
.set_dash_pattern([7.0, 2.0], 4.0)
.x_object(Name(b"MyImage"))
.set_fill_pattern([2.0, 3.5], Name(b"MyPattern"))
.restore_state();
assert_eq!(
content.finish().into_vec(),
b"q\n1 2 3 4 re\nf\n[7 2] 4 d\n/MyImage Do\n2 3.5 /MyPattern scn\nQ"
);
}
#[test]
fn test_content_text() {
let mut content = Content::new();
content.set_font(Name(b"F1"), 12.0);
content.begin_text();
content.show_positioned().items();
content
.show_positioned()
.items()
.show(Str(b"AB"))
.adjust(2.0)
.show(Str(b"CD"));
content.end_text();
assert_eq!(
content.finish().into_vec(),
b"/F1 12 Tf\nBT\n[] TJ\n[(AB) 2 (CD)] TJ\nET"
);
}
}