use lopdf::{
content::{Content, Operation},
dictionary, Dictionary, Document, Object, ObjectId, Stream,
};
use std::collections::HashMap;
use std::f32::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct Transform {
pub scale_x: f32,
pub scale_y: f32,
pub rotation: f32,
pub translate_x: f32,
pub translate_y: f32,
}
impl Default for Transform {
fn default() -> Self {
Transform {
scale_x: 1.0,
scale_y: 1.0,
rotation: 0.0,
translate_x: 0.0,
translate_y: 0.0,
}
}
}
impl Transform {
pub fn translate(x: f32, y: f32) -> Self {
Transform {
translate_x: x,
translate_y: y,
..Default::default()
}
}
pub fn translate_scale(x: f32, y: f32, scale: f32) -> Self {
Transform {
translate_x: x,
translate_y: y,
scale_x: scale,
scale_y: scale,
..Default::default()
}
}
pub fn translate_scale_xy(x: f32, y: f32, scale_x: f32, scale_y: f32) -> Self {
Transform {
translate_x: x,
translate_y: y,
scale_x,
scale_y,
..Default::default()
}
}
pub fn full(x: f32, y: f32, scale_x: f32, scale_y: f32, rotation: f32) -> Self {
Transform {
translate_x: x,
translate_y: y,
scale_x,
scale_y,
rotation,
}
}
pub fn to_matrix(&self) -> [f32; 6] {
let angle_rad = self.rotation * PI / 180.0;
let cos_angle = angle_rad.cos();
let sin_angle = angle_rad.sin();
let a = self.scale_x * cos_angle;
let b = self.scale_x * sin_angle;
let c = -self.scale_y * sin_angle;
let d = self.scale_y * cos_angle;
let e = self.translate_x;
let f = self.translate_y;
[a, b, c, d, e, f]
}
pub fn to_operation(&self) -> Operation {
let matrix = self.to_matrix();
Operation::new(
"cm",
vec![
matrix[0].into(),
matrix[1].into(),
matrix[2].into(),
matrix[3].into(),
matrix[4].into(),
matrix[5].into(),
],
)
}
}
#[derive(Debug, Clone)]
pub struct Block {
pub id: String,
pub operations: Vec<Operation>,
pub bbox: Option<(f32, f32, f32, f32)>,
pub resources: Option<Dictionary>,
}
impl Block {
pub fn new(id: impl Into<String>, operations: Vec<Operation>) -> Self {
Block {
id: id.into(),
operations,
bbox: None,
resources: None,
}
}
pub fn with_bbox(mut self, x: f32, y: f32, width: f32, height: f32) -> Self {
self.bbox = Some((x, y, width, height));
self
}
pub fn with_resources(mut self, resources: Dictionary) -> Self {
self.resources = Some(resources);
self
}
pub fn add_operation(&mut self, op: Operation) {
self.operations.push(op);
}
pub fn add_operations(&mut self, ops: Vec<Operation>) {
self.operations.extend(ops);
}
}
#[derive(Debug, Clone)]
pub struct BlockInstance {
pub block_id: String,
pub transform: Transform,
}
impl BlockInstance {
pub fn new(block_id: impl Into<String>, transform: Transform) -> Self {
BlockInstance {
block_id: block_id.into(),
transform,
}
}
pub fn at(block_id: impl Into<String>, x: f32, y: f32) -> Self {
BlockInstance {
block_id: block_id.into(),
transform: Transform::translate(x, y),
}
}
pub fn at_scaled(block_id: impl Into<String>, x: f32, y: f32, scale: f32) -> Self {
BlockInstance {
block_id: block_id.into(),
transform: Transform::translate_scale(x, y, scale),
}
}
}
pub struct BlockManager {
blocks: HashMap<String, Block>,
xobjects: HashMap<String, ObjectId>,
xobject_counter: usize,
}
impl Default for BlockManager {
fn default() -> Self {
Self::new()
}
}
impl BlockManager {
pub fn new() -> Self {
BlockManager {
blocks: HashMap::new(),
xobjects: HashMap::new(),
xobject_counter: 0,
}
}
pub fn register(&mut self, block: Block) {
self.blocks.insert(block.id.clone(), block);
}
pub fn register_blocks(&mut self, blocks: Vec<Block>) {
for block in blocks {
self.register(block);
}
}
pub fn get(&self, id: &str) -> Option<&Block> {
self.blocks.get(id)
}
pub fn get_mut(&mut self, id: &str) -> Option<&mut Block> {
self.blocks.get_mut(id)
}
pub fn remove(&mut self, id: &str) -> Option<Block> {
self.xobjects.remove(id);
self.blocks.remove(id)
}
pub fn has(&self, id: &str) -> bool {
self.blocks.contains_key(id)
}
pub fn count(&self) -> usize {
self.blocks.len()
}
pub fn render_instance(&self, instance: &BlockInstance) -> Vec<Operation> {
if let Some(block) = self.blocks.get(&instance.block_id) {
let mut ops = Vec::new();
ops.push(Operation::new("q", vec![]));
ops.push(instance.transform.to_operation());
ops.extend(block.operations.clone());
ops.push(Operation::new("Q", vec![]));
ops
} else {
Vec::new()
}
}
pub fn render_instances(&self, instances: &[BlockInstance]) -> Vec<Operation> {
let mut operations = Vec::new();
for instance in instances {
operations.extend(self.render_instance(instance));
}
operations
}
pub fn create_xobjects(&mut self, doc: &mut Document) {
for (id, block) in &self.blocks {
if !self.xobjects.contains_key(id) {
let xobject_id = self.create_xobject_for_block(doc, block);
self.xobjects.insert(id.clone(), xobject_id);
}
}
}
fn create_xobject_for_block(&self, doc: &mut Document, block: &Block) -> ObjectId {
let mut dict = dictionary! {
"Type" => "XObject",
"Subtype" => "Form",
};
if let Some((x, y, w, h)) = block.bbox {
dict.set(
"BBox",
vec![x.into(), y.into(), (x + w).into(), (y + h).into()],
);
} else {
dict.set("BBox", vec![0.into(), 0.into(), 100.into(), 100.into()]);
}
if let Some(ref resources) = block.resources {
dict.set("Resources", resources.clone());
}
let content = Content {
operations: block.operations.clone(),
};
let stream = Stream::new(dict, content.encode().unwrap());
doc.add_object(stream)
}
pub fn render_instances_as_xobjects(
&mut self,
instances: &[BlockInstance],
resources: &mut Dictionary,
) -> Vec<Operation> {
let mut operations = Vec::new();
let mut xobject_dict = Dictionary::new();
for instance in instances {
if let Some(&xobject_id) = self.xobjects.get(&instance.block_id) {
let name = format!("Blk{}", self.xobject_counter);
self.xobject_counter += 1;
xobject_dict.set(name.clone(), Object::Reference(xobject_id));
operations.push(Operation::new("q", vec![]));
operations.push(instance.transform.to_operation());
operations.push(Operation::new(
"Do",
vec![Object::Name(name.as_bytes().to_vec())],
));
operations.push(Operation::new("Q", vec![]));
}
}
if !xobject_dict.is_empty() {
resources.set("XObject", xobject_dict);
}
operations
}
pub fn clear(&mut self) {
self.blocks.clear();
self.xobjects.clear();
self.xobject_counter = 0;
}
}
pub fn merge_blocks(blocks: &[&Block]) -> Vec<Operation> {
let mut operations = Vec::new();
for block in blocks {
operations.extend(block.operations.clone());
}
operations
}
#[macro_export]
macro_rules! block {
($id:expr, $ops:expr) => {
Block::new($id, $ops)
};
($id:expr, $ops:expr, bbox: ($x:expr, $y:expr, $w:expr, $h:expr)) => {
Block::new($id, $ops).with_bbox($x, $y, $w, $h)
};
}
#[macro_export]
macro_rules! instance {
($block_id:expr, at: ($x:expr, $y:expr)) => {
BlockInstance::at($block_id, $x, $y)
};
($block_id:expr, at: ($x:expr, $y:expr), scale: $scale:expr) => {
BlockInstance::at_scaled($block_id, $x, $y, $scale)
};
($block_id:expr, transform: $transform:expr) => {
BlockInstance::new($block_id, $transform)
};
}