1#![deny(missing_docs)]
29
30#[macro_use]
31extern crate lazy_static;
32extern crate deflate;
33extern crate num;
34
35use num::NumCast;
36use std::fs::File;
37use std::io;
38
39mod graphicsstate;
40mod fonts;
41mod text;
42pub use fonts::Font;
43pub use graphicsstate::{Color, Matrix};
44pub use text::Alignment;
45
46struct PdfObject {
48 offset: usize,
49 id: usize,
50 is_page: bool,
51}
52
53pub struct Pdf {
55 buffer: Vec<u8>,
56 page_buffer: Vec<u8>,
57 objects: Vec<PdfObject>,
58 width: f64,
59 height: f64,
60 fonts: Vec<fonts::Font>,
61 font_size: f64,
62 compress: bool,
63}
64
65impl Pdf {
66 #[inline]
68 pub fn new() -> Self {
69 let mut buffer = Vec::new();
70 buffer.extend(b"%PDF-1.7\n%\xB5\xED\xAE\xFB\n");
71 Pdf {
72 buffer: buffer,
73 page_buffer: Vec::new(),
74 objects: vec![
75 PdfObject {
76 offset: 0,
77 id: 1,
78 is_page: false,
79 },
80 PdfObject {
81 offset: 0,
82 id: 2,
83 is_page: false,
84 },
85 ],
86 width: 400.0,
87 height: 400.0,
88 fonts: vec![Font::Helvetica],
89 font_size: 12.0,
90 compress: true,
91 }
92 }
93
94 #[inline]
96 pub fn new_uncompressed() -> Self {
97 let mut this = Pdf::new();
98 this.compress = false;
99 this
100 }
101
102 #[inline]
104 fn move_to(&mut self, x: f64, y: f64) -> &mut Self {
105 self.page_buffer.extend(
106 format!("{:.2} {:.2} m ", x, y).bytes(),
107 );
108 self
109 }
110
111 #[inline]
113 fn line_to(&mut self, x: f64, y: f64) -> &mut Self {
114 self.page_buffer.extend(
115 format!("{:.2} {:.2} l ", x, y).bytes(),
116 );
117 self
118 }
119
120 #[inline]
122 fn curve_to(
123 &mut self,
124 (x1, y1): (f64, f64),
125 (x2, y2): (f64, f64),
126 (x3, y3): (f64, f64),
127 ) -> &mut Self {
128 self.page_buffer.extend(
129 format!(
130 "{:.2} {:.2} {:.2} {:.2} {:.2} {:.2} c\n",
131 x1,
132 y1,
133 x2,
134 y2,
135 x3,
136 y3
137 ).bytes(),
138 );
139 self
140 }
141
142 #[inline]
144 pub fn set_line_width<N: NumCast>(&mut self, width: N) -> &mut Self {
145 self.page_buffer.extend(
146 format!(
147 "{:.2} w\n",
148 width.to_f64().unwrap()
149 ).bytes(),
150 );
151 self
152 }
153
154 #[inline]
156 pub fn set_color(&mut self, color: &Color) -> &mut Self {
157 let norm = |color| color as f64 / 255.0;
158 self.page_buffer.extend(
159 format!(
160 "{:.2} {:.2} {:.2} SC\n",
161 norm(color.red),
162 norm(color.green),
163 norm(color.blue),
164 ).bytes(),
165 );
166 self.page_buffer.extend(
167 format!(
168 "{:.2} {:.2} {:.2} rg\n",
169 norm(color.red),
170 norm(color.green),
171 norm(color.blue),
172 ).bytes(),
173 );
174
175 self
176 }
177
178 #[inline]
181 pub fn transform(&mut self, m: Matrix) -> &mut Self {
182 self.page_buffer.extend(format!("{} cm\n", m).bytes());
183 self
184 }
185
186 #[inline]
189 pub fn draw_circle<N1: NumCast, N2: NumCast, N3: NumCast>(
190 &mut self,
191 x: N1,
192 y: N2,
193 radius: N3,
194 ) -> &mut Self {
195 let x = x.to_f64().unwrap();
196 let y = y.to_f64().unwrap();
197 let radius = radius.to_f64().unwrap();
198 let top = y - radius;
199 let bottom = y + radius;
200 let left = x - radius;
201 let right = x + radius;
202 let c = 0.551915024494;
203 let leftp = x - (radius * c);
204 let rightp = x + (radius * c);
205 let topp = y - (radius * c);
206 let bottomp = y + (radius * c);
207 self.move_to(x, top);
208 self.curve_to((leftp, top), (left, topp), (left, y));
209 self.curve_to((left, bottomp), (leftp, bottom), (x, bottom));
210 self.curve_to((rightp, bottom), (right, bottomp), (right, y));
211 self.curve_to((right, topp), (rightp, top), (x, top));
212 self.page_buffer.extend_from_slice(b"S\n");
213 self
214 }
215
216 #[inline]
218 pub fn draw_line<I, N: NumCast>(&mut self, mut points: I) -> &mut Self
219 where
220 I: Iterator<Item = (N, N)>,
221 {
222 if let Some((x, y)) = points.next() {
223 let x = x.to_f64().unwrap();
224 let y = y.to_f64().unwrap();
225 self.move_to(x, y);
226 for (x, y) in points {
227 let x = x.to_f64().unwrap();
228 let y = y.to_f64().unwrap();
229 self.line_to(x, y);
230 }
231 }
232 self.page_buffer.extend_from_slice(b"S\n");
233 self
234 }
235
236 #[inline]
238 pub fn draw_rectangle_filled<N1: NumCast, N2: NumCast, N3: NumCast, N4: NumCast>(
239 &mut self,
240 x: N1,
241 y: N2,
242 width: N3,
243 height: N4,
244 ) -> &mut Self {
245 self.page_buffer.extend(
246 format!(
247 "{:.2} {:.2} {:.2} {:.2} re\n",
248 x.to_f64().unwrap(),
249 y.to_f64().unwrap(),
250 width.to_f64().unwrap(),
251 height.to_f64().unwrap()
252 ).bytes(),
253 );
254 self.page_buffer.extend_from_slice(b"f\n");
256 self
257 }
258
259 #[inline]
260 pub fn font<N: NumCast>(&mut self, font: Font, size: N) -> &mut Self {
262 self.fonts.push(font);
263 self.font_size = size.to_f64().unwrap();
264 self
265 }
266
267 #[inline]
269 pub fn draw_text<N1: NumCast, N2: NumCast>(
270 &mut self,
271 x: N1,
272 y: N2,
273 alignment: Alignment,
274 text: &str,
275 ) -> &mut Self {
276
277 let x = x.to_f64().unwrap();
278 let y = y.to_f64().unwrap();
279 let widths = &fonts::GLYPH_WIDTHS[self.fonts.iter().last().unwrap()];
280 let height = self.font_size;
281
282 self.page_buffer.extend(
283 format!("BT\n/F{} {} Tf\n", self.fonts.len() - 1, self.font_size).bytes(),
284 );
285
286 let num_lines = text.split('\n').count();
287 for (l, line) in text.split('\n').enumerate() {
288 let line_width = line.chars()
289 .filter(|c| *c != '\n')
290 .map(|c| *widths.get(&c).unwrap_or(&1.0))
291 .sum::<f64>() * self.font_size;
292
293 let (line_x, line_y) = match alignment {
294 Alignment::TopLeft => (x, y - height * (l as f64 + 1.0)),
295 Alignment::TopRight => (x - line_width, y - height * (l as f64 + 1.0)),
296 Alignment::TopCenter => (x - line_width / 2.0, y - height * (l as f64 + 1.0)),
297 Alignment::CenterLeft => (
298 x,
299 (y - height / 3.0) -
300 (l as f64 - (num_lines as f64 - 1.0) / 2.0) *
301 height * 1.25,
302 ),
303 Alignment::CenterRight => (
304 x - line_width,
305 (y - height / 3.0) -
306 (l as f64 - (num_lines as f64 - 1.0) / 2.0) *
307 height * 1.25,
308 ),
309 Alignment::CenterCenter => (
310 x - line_width / 2.0,
311 (y - height / 3.0) -
312 (l as f64 - (num_lines as f64 - 1.0) / 2.0) *
313 height * 1.25,
314 ),
315 Alignment::BottomLeft => (x, y + (num_lines - l - 1) as f64 * 1.25 * height),
316 Alignment::BottomRight => (
317 x - line_width,
318 y + (num_lines - l - 1) as f64 * 1.25 * height,
319 ),
320 Alignment::BottomCenter => (
321 x - line_width / 2.0,
322 y + (num_lines - l - 1) as f64 * 1.25 * height,
323 ),
324 };
325
326 self.page_buffer.extend(
327 format!(
328 "1 0 0 1 {} {} Tm (",
329 line_x,
330 line_y
331 ).bytes(),
332 );
333 for c in line.chars() {
334 let data = format!("\\{:o}", c as u32);
335 self.page_buffer.extend(data.bytes());
336 }
337 self.page_buffer.extend(b") Tj\n");
338 }
339 self.page_buffer.extend(b"ET\n");
340 self
341 }
342
343 #[inline]
346 pub fn add_page<N1: NumCast, N2: NumCast>(&mut self, width: N1, height: N2) -> &mut Self {
347 if !self.page_buffer.is_empty() {
349 self.flush_page();
350 }
351
352 self.page_buffer.extend(
353 "/DeviceRGB cs /DeviceRGB CS\n".bytes(),
354 );
355 self.width = width.to_f64().unwrap();
356 self.height = height.to_f64().unwrap();
357 self
358 }
359
360 fn flush_page(&mut self) {
362 let obj_id = self.objects.iter().map(|o| o.id).max().unwrap() + 1;
364 self.objects.push(PdfObject {
365 offset: self.buffer.len(),
366 id: obj_id,
367 is_page: false,
368 });
369
370 self.buffer.extend(format!("{} 0 obj\n", obj_id).bytes());
371 if self.compress {
372 let (compressed, rounds) = compress(self.page_buffer.clone());
373 self.buffer.extend(
374 format!(
375 "<</Length {} /Filter [{}]>>\nstream\n",
376 compressed.len(),
377 "/FlateDecode ".repeat(rounds)
378 ).bytes(),
379 );
380 self.buffer.extend(compressed.iter());
381 self.buffer.extend("endstream\nendobj\n".bytes())
382 } else {
383 self.buffer.extend(
384 format!("<</Length {}>>\nstream\n", self.page_buffer.len()).bytes(),
385 );
386 self.buffer.extend(self.page_buffer.iter());
387 self.buffer.extend("endstream\nendobj\n".bytes());
388 }
389 self.page_buffer.clear();
390
391 let obj_id = self.objects.iter().map(|o| o.id).max().unwrap() + 1;
393 self.objects.push(PdfObject {
394 offset: self.buffer.len(),
395 id: obj_id,
396 is_page: true,
397 });
398 self.buffer.extend(format!("{} 0 obj\n", obj_id).bytes());
399 self.buffer.extend_from_slice(b"<</Type /Page\n");
400 self.buffer.extend_from_slice(b"/Parent 2 0 R\n");
401 for (f, font) in self.fonts.iter().enumerate() {
403 self.buffer.extend(
404 format!("/Resources << /Font << /F{} << /Type /Font /Subtype /Type1 /BaseFont /{:?} /Encoding /WinAnsiEncoding>> >> >>\n", f, font).bytes(),
405 );
406 }
407
408 self.buffer.extend(
409 format!("/MediaBox [0 0 {} {}]\n", self.width, self.height).bytes(),
410 );
411 self.buffer.extend(
412 format!("/Contents {} 0 R >>\n", obj_id - 1)
413 .bytes(),
414 );
415
416 self.buffer.extend("endobj\n".bytes());
417 self.fonts.clear();
418 }
419
420 pub fn write_to(&mut self, filename: &str) -> io::Result<()> {
422 use std::io::Write;
423
424 if !self.page_buffer.is_empty() {
425 self.flush_page();
426 }
427
428 self.objects[1].offset = self.buffer.len();
430 self.buffer.extend_from_slice(b"2 0 obj\n");
431 self.buffer.extend_from_slice(b"<</Type /Pages\n");
432 self.buffer.extend(
433 format!(
434 "/Count {}\n",
435 self.objects.iter().filter(|o| o.is_page).count()
436 ).bytes(),
437 );
438 self.buffer.extend_from_slice(b"/Kids [");
439 for obj in self.objects.iter().filter(|obj| obj.is_page) {
440 self.buffer.extend(format!("{} 0 R ", obj.id).bytes());
441 }
442 self.buffer.pop();
443 self.buffer.extend_from_slice(b"]>>\nendobj\n");
444
445 self.objects[0].offset = self.buffer.len();
447 self.buffer.extend_from_slice(
448 b"1 0 obj\n<</Type /Catalog\n/Pages 2 0 R>>\nendobj\n",
449 );
450
451 let startxref = self.buffer.len();
453 self.buffer.extend_from_slice(b"xref\n");
454 self.buffer.extend(
455 format!("0 {}\n", self.objects.len() + 1).bytes(),
456 );
457 self.buffer.extend_from_slice(b"0000000000 65535 f \n");
458 self.objects.sort_by(|a, b| a.id.cmp(&b.id));
459
460 for obj in &self.objects {
461 self.buffer.extend(
462 format!("{:010} 00000 f \n", obj.offset).bytes(),
463 );
464 }
465
466 self.buffer.extend_from_slice(b"trailer\n");
468 self.buffer.extend(
469 format!("<</Size {}\n", self.objects.len())
470 .bytes(),
471 );
472 self.buffer.extend_from_slice(b"/Root 1 0 R>>\n");
473
474 self.buffer.extend(
476 format!("startxref\n{}\n", startxref).bytes(),
477 );
478
479 self.buffer.extend_from_slice(b"%%EOF");
481
482 File::create(filename)?.write_all(self.buffer.as_slice())
483 }
484}
485
486fn compress(input: Vec<u8>) -> (Vec<u8>, usize) {
487 let mut compressed = input;
488 let mut rounds = 0;
489 loop {
490 let another = deflate::deflate_bytes_zlib(compressed.as_slice());
491 if another.len() < compressed.len() {
492 compressed = another;
493 rounds += 1;
494 } else {
495 break;
496 }
497 }
498 (compressed, rounds)
499}