Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
pdfgen.hpp
Go to the documentation of this file.
1#pragma once
2
15
16#include <algorithm>
17#include <array>
18#include <cerrno>
19#include <cmath>
20#include <cstdarg>
21#include <cstdint>
22#include <cstdio>
23#include <cstring>
24#include <ctime>
25#include <fstream>
26#include <memory>
27#include <sstream>
28#include <string>
29#include <string_view>
30#include <utility>
31#include <vector>
32
33namespace pgl::pdfgen {
34
35struct pdf_doc;
36struct pdf_object;
37
38struct pdf_info {
39 char creator[64]{};
40 char producer[64]{};
41 char title[64]{};
42 char author[64]{};
43 char subject[64]{};
44 char date[64]{};
45};
46
48 char op{};
49 float x1{};
50 float y1{};
51 float x2{};
52 float y2{};
53 float x3{};
54 float y3{};
55};
56
57inline constexpr float PDF_INCH_TO_POINT(float inch) {
58 return inch * 72.0f;
59}
60
61inline constexpr float PDF_MM_TO_POINT(float mm) {
62 return mm * 72.0f / 25.4f;
63}
64
65inline constexpr float PDF_LETTER_WIDTH = PDF_INCH_TO_POINT(8.5f);
66inline constexpr float PDF_LETTER_HEIGHT = PDF_INCH_TO_POINT(11.0f);
67inline constexpr float PDF_A4_WIDTH = PDF_MM_TO_POINT(210.0f);
68inline constexpr float PDF_A4_HEIGHT = PDF_MM_TO_POINT(297.0f);
69inline constexpr float PDF_A3_WIDTH = PDF_MM_TO_POINT(297.0f);
70inline constexpr float PDF_A3_HEIGHT = PDF_MM_TO_POINT(420.0f);
71
72inline constexpr std::uint32_t PDF_RGB(unsigned int r, unsigned int g, unsigned int b) {
73 return (((r & 0xffu) << 16) | ((g & 0xffu) << 8) | (b & 0xffu));
74}
75
76inline constexpr std::uint32_t PDF_ARGB(unsigned int a, unsigned int r, unsigned int g, unsigned int b) {
77 return (((a & 0xffu) << 24) | ((r & 0xffu) << 16) | ((g & 0xffu) << 8) | (b & 0xffu));
78}
79
80inline constexpr std::uint32_t PDF_RED = PDF_RGB(0xffu, 0u, 0u);
81inline constexpr std::uint32_t PDF_GREEN = PDF_RGB(0u, 0xffu, 0u);
82inline constexpr std::uint32_t PDF_BLUE = PDF_RGB(0u, 0u, 0xffu);
83inline constexpr std::uint32_t PDF_BLACK = PDF_RGB(0u, 0u, 0u);
84inline constexpr std::uint32_t PDF_WHITE = PDF_RGB(0xffu, 0xffu, 0xffu);
85inline constexpr std::uint32_t PDF_TRANSPARENT = (0xffu << 24);
86
87inline constexpr float PDF_RGB_R(std::uint32_t colour) {
88 return static_cast<float>((colour >> 16) & 0xffu) / 255.0f;
89}
90
91inline constexpr float PDF_RGB_G(std::uint32_t colour) {
92 return static_cast<float>((colour >> 8) & 0xffu) / 255.0f;
93}
94
95inline constexpr float PDF_RGB_B(std::uint32_t colour) {
96 return static_cast<float>(colour & 0xffu) / 255.0f;
97}
98
99inline constexpr bool PDF_IS_TRANSPARENT(std::uint32_t colour) {
100 return (colour >> 24) == 0xffu;
101}
102
103namespace detail {
104
105enum {
115};
116
117inline constexpr std::array valid_fonts = {
118 "Times-Roman",
119 "Times-Bold",
120 "Times-Italic",
121 "Times-BoldItalic",
122 "Helvetica",
123 "Helvetica-Bold",
124 "Helvetica-Oblique",
125 "Helvetica-BoldOblique",
126 "Courier",
127 "Courier-Bold",
128 "Courier-Oblique",
129 "Courier-BoldOblique",
130 "Symbol",
131 "ZapfDingbats",
132};
133
134struct stream_data {
135 pdf_object* page = nullptr;
136 std::string stream;
137};
138
139struct page_data {
140 float width = 0.0f;
141 float height = 0.0f;
142 std::vector<pdf_object*> children;
143 std::vector<pdf_object*> ext_gstates;
144};
145
146struct font_data {
147 char name[64]{};
148 int index = 0;
149 bool is_ttf = false;
150};
151
152struct ext_gstate_data {
153 float fill_alpha = 1.0f;
154 float stroke_alpha = 1.0f;
155};
156
157inline int ascii_casecmp(std::string_view left, std::string_view right) {
158 const std::size_t size = std::min(left.size(), right.size());
159 for (std::size_t i = 0; i < size; ++i) {
160 const unsigned char a = static_cast<unsigned char>(left[i]);
161 const unsigned char b = static_cast<unsigned char>(right[i]);
162 const int lower_a = (a >= 'A' && a <= 'Z') ? (a - 'A' + 'a') : a;
163 const int lower_b = (b >= 'A' && b <= 'Z') ? (b - 'A' + 'a') : b;
164 if (lower_a != lower_b) {
165 return lower_a - lower_b;
166 }
167 }
168 if (left.size() == right.size()) {
169 return 0;
170 }
171 return left.size() < right.size() ? -1 : 1;
172}
173
174inline void append_format(std::string& out, const char* format, ...) {
175 va_list args;
176 va_start(args, format);
177 va_list copy;
178 va_copy(copy, args);
179 const int required = std::vsnprintf(nullptr, 0, format, copy);
180 va_end(copy);
181 if (required < 0) {
182 va_end(args);
183 return;
184 }
185 const std::size_t old_size = out.size();
186 out.resize(old_size + static_cast<std::size_t>(required) + 1);
187 std::vsnprintf(out.data() + old_size, static_cast<std::size_t>(required) + 1, format, args);
188 out.resize(old_size + static_cast<std::size_t>(required));
189 va_end(args);
190}
191
192inline void append_escaped_string(std::string& out, std::string_view value) {
193 out.push_back('(');
194 for (const char character : value) {
195 if (character == '(' || character == ')' || character == '\\') {
196 out.push_back('\\');
197 }
198 out.push_back(character);
199 }
200 out.push_back(')');
201}
202
203inline std::string now_as_pdf_date() {
204 std::time_t now = std::time(nullptr);
205 std::tm tm{};
206#if defined(_WIN32)
207 const std::tm* tmp = std::localtime(&now);
208 if (tmp != nullptr) {
209 tm = *tmp;
210 }
211#else
212 localtime_r(&now, &tm);
213#endif
214 char buffer[64]{};
215 std::strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%SZ", &tm);
216 return buffer;
217}
218
219} // namespace detail
220
223 int index = 0;
224 std::size_t offset = 0;
225 pdf_object* prev = nullptr;
226 pdf_object* next = nullptr;
228 detail::stream_data stream{};
229 detail::page_data page{};
230 detail::font_data font{};
231 detail::ext_gstate_data ext_gstate{};
232};
233
234struct pdf_doc {
235 char errstr[128]{};
236 int errval = 0;
237 std::vector<std::unique_ptr<pdf_object>> objects;
238 float width = 0.0f;
239 float height = 0.0f;
241 std::array<pdf_object*, detail::OBJ_count> last_objects{};
242 std::array<pdf_object*, detail::OBJ_count> first_objects{};
243};
244
245inline int pdf_set_err(pdf_doc* doc, int errval, const char* buffer, ...) {
246 if (doc == nullptr) {
247 return errval;
248 }
249 va_list args;
250 va_start(args, buffer);
251 const int len = std::vsnprintf(doc->errstr, sizeof(doc->errstr), buffer, args);
252 va_end(args);
253 if (len < 0) {
254 doc->errstr[0] = '\0';
255 } else {
256 doc->errstr[sizeof(doc->errstr) - 1] = '\0';
257 }
258 doc->errval = errval;
259 return errval;
260}
261
262inline const char* pdf_get_err(const pdf_doc* pdf, int* errval) {
263 if (pdf == nullptr || pdf->errstr[0] == '\0') {
264 return nullptr;
265 }
266 if (errval != nullptr) {
267 *errval = pdf->errval;
268 }
269 return pdf->errstr;
270}
271
272inline void pdf_clear_err(pdf_doc* pdf) {
273 if (pdf == nullptr) {
274 return;
275 }
276 pdf->errstr[0] = '\0';
277 pdf->errval = 0;
278}
279
280inline pdf_object* pdf_get_object(const pdf_doc* pdf, int index) {
281 if (pdf == nullptr || index < 0 || static_cast<std::size_t>(index) >= pdf->objects.size()) {
282 return nullptr;
283 }
284 return pdf->objects[static_cast<std::size_t>(index)].get();
285}
286
287inline pdf_object* pdf_find_first_object(const pdf_doc* pdf, int type) {
288 return pdf == nullptr ? nullptr : pdf->first_objects[static_cast<std::size_t>(type)];
289}
290
291inline pdf_object* pdf_find_last_object(const pdf_doc* pdf, int type) {
292 return pdf == nullptr ? nullptr : pdf->last_objects[static_cast<std::size_t>(type)];
293}
294
295inline int pdf_append_object(pdf_doc* pdf, pdf_object* object) {
296 if (pdf == nullptr || object == nullptr) {
297 return -EINVAL;
298 }
299 object->index = static_cast<int>(pdf->objects.size());
300 if (pdf->last_objects[static_cast<std::size_t>(object->type)] != nullptr) {
301 object->prev = pdf->last_objects[static_cast<std::size_t>(object->type)];
302 object->prev->next = object;
303 }
304 pdf->last_objects[static_cast<std::size_t>(object->type)] = object;
305 if (pdf->first_objects[static_cast<std::size_t>(object->type)] == nullptr) {
306 pdf->first_objects[static_cast<std::size_t>(object->type)] = object;
307 }
308 return 0;
309}
310
311inline pdf_object* pdf_add_object(pdf_doc* pdf, int type) {
312 if (pdf == nullptr) {
313 return nullptr;
314 }
315 std::unique_ptr<pdf_object> object = std::make_unique<pdf_object>();
316 object->type = type;
317 pdf_object* raw = object.get();
318 if (pdf_append_object(pdf, raw) < 0) {
319 return nullptr;
320 }
321 pdf->objects.push_back(std::move(object));
322 return raw;
323}
324
325inline float pdf_width(const pdf_doc* pdf) {
326 return pdf == nullptr ? 0.0f : pdf->width;
327}
328
329inline float pdf_height(const pdf_doc* pdf) {
330 return pdf == nullptr ? 0.0f : pdf->height;
331}
332
333inline float pdf_page_width(const pdf_object* page) {
334 return page == nullptr ? 0.0f : page->page.width;
335}
336
337inline float pdf_page_height(const pdf_object* page) {
338 return page == nullptr ? 0.0f : page->page.height;
339}
340
341inline int pdf_set_font(pdf_doc* pdf, const char* font) {
342 if (pdf == nullptr || font == nullptr) {
343 return pdf_set_err(pdf, -EINVAL, "Invalid font");
344 }
345
346 const char* canonical = nullptr;
347 for (const char* candidate : detail::valid_fonts) {
348 if (detail::ascii_casecmp(font, candidate) == 0) {
349 canonical = candidate;
350 break;
351 }
352 }
353 if (canonical == nullptr) {
354 return pdf_set_err(pdf, -EINVAL, "Invalid font name '%s'", font);
355 }
356
357 int last_index = 0;
358 for (pdf_object* object = pdf_find_first_object(pdf, detail::OBJ_font); object != nullptr; object = object->next) {
359 last_index = std::max(last_index, object->font.index);
360 if (std::strcmp(object->font.name, canonical) == 0) {
361 pdf->current_font = object;
362 return 0;
363 }
364 }
365
367 if (object == nullptr) {
368 return pdf_set_err(pdf, -ENOMEM, "Unable to allocate PDF font");
369 }
370
371 std::snprintf(object->font.name, sizeof(object->font.name), "%s", canonical);
372 object->font.name[sizeof(object->font.name) - 1] = '\0';
373 object->font.index = last_index + 1;
374 pdf->current_font = object;
375 return 0;
376}
377
378inline pdf_doc* pdf_create(float width, float height, const pdf_info* info) {
379 std::unique_ptr<pdf_doc> pdf = std::make_unique<pdf_doc>();
380 pdf->width = width;
381 pdf->height = height;
382
383 (void)pdf_add_object(pdf.get(), detail::OBJ_none);
384
385 pdf_object* info_object = pdf_add_object(pdf.get(), detail::OBJ_info);
386 if (info_object == nullptr) {
387 return nullptr;
388 }
389 if (info != nullptr) {
390 info_object->info = *info;
391 info_object->info.creator[sizeof(info_object->info.creator) - 1] = '\0';
392 info_object->info.producer[sizeof(info_object->info.producer) - 1] = '\0';
393 info_object->info.title[sizeof(info_object->info.title) - 1] = '\0';
394 info_object->info.author[sizeof(info_object->info.author) - 1] = '\0';
395 info_object->info.subject[sizeof(info_object->info.subject) - 1] = '\0';
396 info_object->info.date[sizeof(info_object->info.date) - 1] = '\0';
397 }
398 if (info_object->info.date[0] == '\0') {
399 const std::string date = detail::now_as_pdf_date();
400 std::snprintf(info_object->info.date, sizeof(info_object->info.date), "%s", date.c_str());
401 }
402
403 if (pdf_add_object(pdf.get(), detail::OBJ_pages) == nullptr) {
404 return nullptr;
405 }
406 if (pdf_add_object(pdf.get(), detail::OBJ_catalog) == nullptr) {
407 return nullptr;
408 }
409 if (pdf_set_font(pdf.get(), "Times-Roman") < 0) {
410 return nullptr;
411 }
412
413 return pdf.release();
414}
415
416inline void pdf_destroy(pdf_doc* pdf) {
417 delete pdf;
418}
419
422 if (page == nullptr) {
423 return nullptr;
424 }
425 page->page.width = pdf->width;
426 page->page.height = pdf->height;
427 return page;
428}
429
430inline pdf_object* pdf_get_page(pdf_doc* pdf, int page_number) {
431 if (page_number <= 0) {
432 pdf_set_err(pdf, -EINVAL, "page number must be >= 1");
433 return nullptr;
434 }
435 for (pdf_object* object = pdf_find_first_object(pdf, detail::OBJ_page); object != nullptr; object = object->next, --page_number) {
436 if (page_number == 1) {
437 return object;
438 }
439 }
440 pdf_set_err(pdf, -EINVAL, "no such page");
441 return nullptr;
442}
443
444inline int pdf_page_set_size(pdf_doc* pdf, pdf_object* page, float width, float height) {
445 if (page == nullptr) {
447 }
448 if (page == nullptr || page->type != detail::OBJ_page) {
449 return pdf_set_err(pdf, -EINVAL, "Invalid PDF page");
450 }
451 page->page.width = width;
452 page->page.height = height;
453 return 0;
454}
455
456inline int pdf_add_stream(pdf_doc* pdf, pdf_object* page, const std::string& buffer) {
457 if (page == nullptr) {
459 }
460 if (page == nullptr || page->type != detail::OBJ_page) {
461 return pdf_set_err(pdf, -EINVAL, "Invalid pdf page");
462 }
463
464 std::string trimmed = buffer;
465 while (!trimmed.empty() && (trimmed.back() == '\r' || trimmed.back() == '\n')) {
466 trimmed.pop_back();
467 }
468
470 if (object == nullptr) {
471 return pdf_set_err(pdf, -ENOMEM, "Unable to allocate PDF stream");
472 }
473 object->stream.page = page;
474 object->stream.stream = std::move(trimmed);
475 page->page.children.push_back(object);
476 return 0;
477}
478
479inline std::string pdf_escape_content_string(std::string_view value) {
480 std::string escaped;
481 escaped.reserve(value.size() + 4);
482 for (const char character : value) {
483 if (character == '(' || character == ')' || character == '\\') {
484 escaped.push_back('\\');
485 }
486 if (character == '\n' || character == '\r' || character == '\t' || character == '\b' || character == '\f') {
487 continue;
488 }
489 escaped.push_back(character);
490 }
491 return escaped;
492}
493
494inline int pdf_add_text(pdf_doc* pdf, pdf_object* page, const char* text, float size, float xoff, float yoff, std::uint32_t colour) {
495 if (text == nullptr || *text == '\0') {
496 return 0;
497 }
498 if (pdf == nullptr || pdf->current_font == nullptr) {
499 return pdf_set_err(pdf, -EINVAL, "No active font");
500 }
501 std::string stream;
502 detail::append_format(stream, "BT %f %f TD /F%d %f Tf %f %f %f rg (%s) Tj ET",
503 xoff,
504 yoff,
505 pdf->current_font->font.index,
506 size,
507 PDF_RGB_R(colour),
508 PDF_RGB_G(colour),
509 PDF_RGB_B(colour),
510 pdf_escape_content_string(text).c_str());
511 return pdf_add_stream(pdf, page, stream);
512}
513
514inline float pdf_clamp_alpha(float alpha) {
515 if (!std::isfinite(alpha)) {
516 return 1.0f;
517 }
518 return std::clamp(alpha, 0.0f, 1.0f);
519}
520
521inline bool pdf_alpha_equal(float left, float right) {
522 return std::fabs(left - right) <= 0.000001f;
523}
524
525inline int pdf_find_or_create_ext_gstate(pdf_doc* pdf, pdf_object* page, float fill_alpha, float stroke_alpha) {
526 if (page == nullptr) {
528 }
529 if (page == nullptr || page->type != detail::OBJ_page) {
530 return pdf_set_err(pdf, -EINVAL, "Invalid pdf page");
531 }
532
533 const float clamped_fill_alpha = pdf_clamp_alpha(fill_alpha);
534 const float clamped_stroke_alpha = pdf_clamp_alpha(stroke_alpha);
535 for (std::size_t index = 0; index < page->page.ext_gstates.size(); ++index) {
536 const pdf_object* ext_gstate = page->page.ext_gstates[index];
537 if (ext_gstate != nullptr &&
538 ext_gstate->type == detail::OBJ_ext_gstate &&
539 pdf_alpha_equal(ext_gstate->ext_gstate.fill_alpha, clamped_fill_alpha) &&
540 pdf_alpha_equal(ext_gstate->ext_gstate.stroke_alpha, clamped_stroke_alpha)) {
541 return static_cast<int>(index);
542 }
543 }
544
546 if (ext_gstate == nullptr) {
547 return pdf_set_err(pdf, -ENOMEM, "Unable to allocate PDF ExtGState");
548 }
549 ext_gstate->ext_gstate.fill_alpha = clamped_fill_alpha;
550 ext_gstate->ext_gstate.stroke_alpha = clamped_stroke_alpha;
551 page->page.ext_gstates.push_back(ext_gstate);
552 return static_cast<int>(page->page.ext_gstates.size() - 1);
553}
554
555inline int pdf_add_line_pattern(pdf_doc* pdf, pdf_object* page, float x1, float y1, float x2, float y2, float width,
556 std::uint32_t colour, const float pattern[], int pattern_len, float phase, float stroke_alpha = 1.0f) {
557 if (pattern_len < 0 || (pattern_len > 0 && pattern == nullptr)) {
558 return pdf_set_err(pdf, -EINVAL, "Invalid line pattern");
559 }
560 if (width <= 0.0f || PDF_IS_TRANSPARENT(colour)) {
561 return 0;
562 }
563
564 int ext_gstate_index = -1;
565 const float effective_stroke_alpha = pdf_clamp_alpha(stroke_alpha);
566 if (effective_stroke_alpha < 1.0f) {
567 ext_gstate_index = pdf_find_or_create_ext_gstate(pdf, page, 1.0f, effective_stroke_alpha);
568 if (ext_gstate_index < 0) {
569 return ext_gstate_index;
570 }
571 }
572
573 std::string stream;
574 if (ext_gstate_index >= 0) {
575 stream += "q\r\n";
576 }
577 if (pattern_len > 0) {
578 stream.push_back('[');
579 for (int i = 0; i < pattern_len; ++i) {
580 detail::append_format(stream, "%s%f", i == 0 ? "" : " ", pattern[i]);
581 }
582 detail::append_format(stream, "] %f d\r\n", phase);
583 }
584 detail::append_format(stream, "%f w\r\n", width);
585 detail::append_format(stream, "%f %f m\r\n", x1, y1);
586 detail::append_format(stream, "/DeviceRGB CS\r\n%f %f %f RG\r\n", PDF_RGB_R(colour), PDF_RGB_G(colour), PDF_RGB_B(colour));
587 detail::append_format(stream, "%f %f l\r\n", x2, y2);
588 if (ext_gstate_index >= 0) {
589 detail::append_format(stream, "/GS%d gs\r\n", ext_gstate_index);
590 }
591 stream += "S\r\n";
592 if (pattern_len > 0) {
593 stream += "[] 0 d\r\n";
594 }
595 if (ext_gstate_index >= 0) {
596 stream += "Q\r\n";
597 }
598 return pdf_add_stream(pdf, page, stream);
599}
600
601inline int pdf_add_line(pdf_doc* pdf, pdf_object* page, float x1, float y1, float x2, float y2, float width, std::uint32_t colour,
602 float stroke_alpha = 1.0f) {
603 return pdf_add_line_pattern(pdf, page, x1, y1, x2, y2, width, colour, nullptr, 0, 0.0f, stroke_alpha);
604}
605
606inline int pdf_add_cubic_bezier(pdf_doc* pdf, pdf_object* page, float x1, float y1, float x2, float y2, float xq1,
607 float yq1, float xq2, float yq2, float width, std::uint32_t colour, float stroke_alpha = 1.0f) {
608 if (width <= 0.0f || PDF_IS_TRANSPARENT(colour)) {
609 return 0;
610 }
611
612 int ext_gstate_index = -1;
613 const float effective_stroke_alpha = pdf_clamp_alpha(stroke_alpha);
614 if (effective_stroke_alpha < 1.0f) {
615 ext_gstate_index = pdf_find_or_create_ext_gstate(pdf, page, 1.0f, effective_stroke_alpha);
616 if (ext_gstate_index < 0) {
617 return ext_gstate_index;
618 }
619 }
620
621 std::string stream;
622 if (ext_gstate_index >= 0) {
623 stream += "q\r\n";
624 }
625 detail::append_format(stream, "%f w\r\n", width);
626 detail::append_format(stream, "%f %f m\r\n", x1, y1);
627 detail::append_format(stream, "/DeviceRGB CS\r\n%f %f %f RG\r\n", PDF_RGB_R(colour), PDF_RGB_G(colour), PDF_RGB_B(colour));
628 detail::append_format(stream, "%f %f %f %f %f %f c\r\n", xq1, yq1, xq2, yq2, x2, y2);
629 if (ext_gstate_index >= 0) {
630 detail::append_format(stream, "/GS%d gs\r\n", ext_gstate_index);
631 }
632 stream += "S\r\n";
633 if (ext_gstate_index >= 0) {
634 stream += "Q\r\n";
635 }
636 return pdf_add_stream(pdf, page, stream);
637}
638
639inline int pdf_add_quadratic_bezier(pdf_doc* pdf, pdf_object* page, float x1, float y1, float x2, float y2,
640 float xq1, float yq1, float width, std::uint32_t colour, float stroke_alpha = 1.0f) {
641 const float xc1 = x1 + (xq1 - x1) * (2.0f / 3.0f);
642 const float yc1 = y1 + (yq1 - y1) * (2.0f / 3.0f);
643 const float xc2 = x2 + (xq1 - x2) * (2.0f / 3.0f);
644 const float yc2 = y2 + (yq1 - y2) * (2.0f / 3.0f);
645 return pdf_add_cubic_bezier(pdf, page, x1, y1, x2, y2, xc1, yc1, xc2, yc2, width, colour, stroke_alpha);
646}
647
648inline int pdf_add_custom_path(pdf_doc* pdf, pdf_object* page, const pdf_path_operation* operations,
649 int operation_count, float stroke_width, std::uint32_t stroke_colour, std::uint32_t fill_colour,
650 float fill_alpha = 1.0f, float stroke_alpha = 1.0f) {
651 if (operation_count < 0 || (operation_count > 0 && operations == nullptr)) {
652 return pdf_set_err(pdf, -EINVAL, "Invalid path operations");
653 }
654
655 const bool has_fill = !PDF_IS_TRANSPARENT(fill_colour);
656 const bool has_stroke = !PDF_IS_TRANSPARENT(stroke_colour) && stroke_width > 0.0f;
657 if (!has_fill && !has_stroke) {
658 return 0;
659 }
660
661 int ext_gstate_index = -1;
662 const float effective_fill_alpha = has_fill ? pdf_clamp_alpha(fill_alpha) : 1.0f;
663 const float effective_stroke_alpha = has_stroke ? pdf_clamp_alpha(stroke_alpha) : 1.0f;
664 if (effective_fill_alpha < 1.0f || effective_stroke_alpha < 1.0f) {
665 ext_gstate_index = pdf_find_or_create_ext_gstate(pdf, page, effective_fill_alpha, effective_stroke_alpha);
666 if (ext_gstate_index < 0) {
667 return ext_gstate_index;
668 }
669 }
670
671 std::string stream;
672 if (ext_gstate_index >= 0) {
673 stream += "q\r\n";
674 }
675 if (has_fill) {
676 detail::append_format(stream, "/DeviceRGB cs\r\n%f %f %f rg\r\n", PDF_RGB_R(fill_colour), PDF_RGB_G(fill_colour), PDF_RGB_B(fill_colour));
677 }
678 if (has_stroke) {
679 detail::append_format(stream, "%f w\r\n/DeviceRGB CS\r\n%f %f %f RG\r\n", stroke_width, PDF_RGB_R(stroke_colour), PDF_RGB_G(stroke_colour), PDF_RGB_B(stroke_colour));
680 }
681
682 for (int i = 0; i < operation_count; ++i) {
683 const pdf_path_operation& operation = operations[i];
684 switch (operation.op) {
685 case 'm':
686 detail::append_format(stream, "%f %f m\r\n", operation.x1, operation.y1);
687 break;
688 case 'l':
689 detail::append_format(stream, "%f %f l\r\n", operation.x1, operation.y1);
690 break;
691 case 'c':
692 detail::append_format(stream, "%f %f %f %f %f %f c\r\n", operation.x1, operation.y1, operation.x2, operation.y2, operation.x3, operation.y3);
693 break;
694 case 'v':
695 detail::append_format(stream, "%f %f %f %f v\r\n", operation.x1, operation.y1, operation.x2, operation.y2);
696 break;
697 case 'y':
698 detail::append_format(stream, "%f %f %f %f y\r\n", operation.x1, operation.y1, operation.x2, operation.y2);
699 break;
700 case 'h':
701 stream += "h\r\n";
702 break;
703 default:
704 return pdf_set_err(pdf, -EINVAL, "Invalid operation");
705 }
706 }
707
708 if (ext_gstate_index >= 0) {
709 detail::append_format(stream, "/GS%d gs\r\n", ext_gstate_index);
710 }
711 if (has_fill && has_stroke) {
712 stream += "B\r\n";
713 } else if (has_fill) {
714 stream += "f\r\n";
715 } else {
716 stream += "S\r\n";
717 }
718 if (ext_gstate_index >= 0) {
719 stream += "Q\r\n";
720 }
721
722 return pdf_add_stream(pdf, page, stream);
723}
724
725inline int pdf_add_ellipse(pdf_doc* pdf, pdf_object* page, float x, float y, float xradius, float yradius, float width,
726 std::uint32_t colour, std::uint32_t fill_colour, float fill_alpha = 1.0f, float stroke_alpha = 1.0f) {
727 const float lx = (4.0f / 3.0f) * static_cast<float>(std::sqrt(2.0) - 1.0) * xradius;
728 const float ly = (4.0f / 3.0f) * static_cast<float>(std::sqrt(2.0) - 1.0) * yradius;
729
730 std::array<pdf_path_operation, 5> operations{{
731 {'m', x + xradius, y, 0.0f, 0.0f, 0.0f, 0.0f},
732 {'c', x + xradius, y - ly, x + lx, y - yradius, x, y - yradius},
733 {'c', x - lx, y - yradius, x - xradius, y - ly, x - xradius, y},
734 {'c', x - xradius, y + ly, x - lx, y + yradius, x, y + yradius},
735 {'c', x + lx, y + yradius, x + xradius, y + ly, x + xradius, y},
736 }};
737 return pdf_add_custom_path(
738 pdf,
739 page,
740 operations.data(),
741 static_cast<int>(operations.size()),
742 width,
743 colour,
744 fill_colour,
745 fill_alpha,
746 stroke_alpha
747 );
748}
749
750inline int pdf_add_circle(pdf_doc* pdf, pdf_object* page, float x, float y, float radius, float width,
751 std::uint32_t colour, std::uint32_t fill_colour, float fill_alpha = 1.0f, float stroke_alpha = 1.0f) {
752 return pdf_add_ellipse(pdf, page, x, y, radius, radius, width, colour, fill_colour, fill_alpha, stroke_alpha);
753}
754
755inline int pdf_add_rectangle(pdf_doc* pdf, pdf_object* page, float x, float y, float width, float height,
756 float border_width, std::uint32_t colour, float stroke_alpha = 1.0f) {
757 if (border_width <= 0.0f || PDF_IS_TRANSPARENT(colour)) {
758 return 0;
759 }
760
761 int ext_gstate_index = -1;
762 const float effective_stroke_alpha = pdf_clamp_alpha(stroke_alpha);
763 if (effective_stroke_alpha < 1.0f) {
764 ext_gstate_index = pdf_find_or_create_ext_gstate(pdf, page, 1.0f, effective_stroke_alpha);
765 if (ext_gstate_index < 0) {
766 return ext_gstate_index;
767 }
768 }
769
770 std::string stream;
771 if (ext_gstate_index >= 0) {
772 stream += "q\r\n";
773 }
774 detail::append_format(stream, "/DeviceRGB CS\r\n%f %f %f RG\r\n", PDF_RGB_R(colour), PDF_RGB_G(colour), PDF_RGB_B(colour));
775 detail::append_format(stream, "%f w\r\n%f %f %f %f re\r\n", border_width, x, y, width, height);
776 if (ext_gstate_index >= 0) {
777 detail::append_format(stream, "/GS%d gs\r\n", ext_gstate_index);
778 }
779 stream += "S\r\n";
780 if (ext_gstate_index >= 0) {
781 stream += "Q\r\n";
782 }
783 return pdf_add_stream(pdf, page, stream);
784}
785
786inline int pdf_add_filled_rectangle(pdf_doc* pdf, pdf_object* page, float x, float y, float width, float height,
787 float border_width, std::uint32_t colour_fill, std::uint32_t colour_border, float fill_alpha = 1.0f, float stroke_alpha = 1.0f) {
788 const bool has_fill = !PDF_IS_TRANSPARENT(colour_fill);
789 const bool has_stroke = !PDF_IS_TRANSPARENT(colour_border) && border_width > 0.0f;
790 if (!has_fill && !has_stroke) {
791 return 0;
792 }
793
794 int ext_gstate_index = -1;
795 const float effective_fill_alpha = has_fill ? pdf_clamp_alpha(fill_alpha) : 1.0f;
796 const float effective_stroke_alpha = has_stroke ? pdf_clamp_alpha(stroke_alpha) : 1.0f;
797 if (effective_fill_alpha < 1.0f || effective_stroke_alpha < 1.0f) {
798 ext_gstate_index = pdf_find_or_create_ext_gstate(pdf, page, effective_fill_alpha, effective_stroke_alpha);
799 if (ext_gstate_index < 0) {
800 return ext_gstate_index;
801 }
802 }
803
804 std::string stream;
805 if (ext_gstate_index >= 0) {
806 stream += "q\r\n";
807 }
808 if (has_fill) {
809 detail::append_format(stream, "/DeviceRGB cs\r\n%f %f %f rg\r\n", PDF_RGB_R(colour_fill), PDF_RGB_G(colour_fill), PDF_RGB_B(colour_fill));
810 }
811 if (has_stroke) {
812 detail::append_format(stream, "/DeviceRGB CS\r\n%f %f %f RG\r\n%f w\r\n", PDF_RGB_R(colour_border), PDF_RGB_G(colour_border), PDF_RGB_B(colour_border), border_width);
813 }
814 detail::append_format(stream, "%f %f %f %f re\r\n", x, y, width, height);
815 if (ext_gstate_index >= 0) {
816 detail::append_format(stream, "/GS%d gs\r\n", ext_gstate_index);
817 }
818 stream += has_fill && has_stroke ? "B\r\n" : (has_fill ? "f\r\n" : "S\r\n");
819 if (ext_gstate_index >= 0) {
820 stream += "Q\r\n";
821 }
822 return pdf_add_stream(pdf, page, stream);
823}
824
825inline int pdf_add_polygon(pdf_doc* pdf, pdf_object* page, const float x[], const float y[], int count,
826 float border_width, std::uint32_t colour, float stroke_alpha = 1.0f) {
827 if (count <= 0) {
828 return 0;
829 }
830 std::vector<pdf_path_operation> operations;
831 operations.reserve(static_cast<std::size_t>(count) + 1);
832 operations.push_back({'m', x[0], y[0], 0.0f, 0.0f, 0.0f, 0.0f});
833 for (int i = 1; i < count; ++i) {
834 operations.push_back({'l', x[i], y[i], 0.0f, 0.0f, 0.0f, 0.0f});
835 }
836 operations.push_back({'h', 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
837 return pdf_add_custom_path(
838 pdf,
839 page,
840 operations.data(),
841 static_cast<int>(operations.size()),
842 border_width,
843 colour,
845 1.0f,
846 stroke_alpha
847 );
848}
849
850inline int pdf_add_filled_polygon(pdf_doc* pdf, pdf_object* page, const float x[], const float y[], int count,
851 float border_width, std::uint32_t colour, float fill_alpha = 1.0f, float stroke_alpha = 1.0f) {
852 if (count <= 0) {
853 return 0;
854 }
855 std::vector<pdf_path_operation> operations;
856 operations.reserve(static_cast<std::size_t>(count) + 1);
857 operations.push_back({'m', x[0], y[0], 0.0f, 0.0f, 0.0f, 0.0f});
858 for (int i = 1; i < count; ++i) {
859 operations.push_back({'l', x[i], y[i], 0.0f, 0.0f, 0.0f, 0.0f});
860 }
861 operations.push_back({'h', 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
862 return pdf_add_custom_path(
863 pdf,
864 page,
865 operations.data(),
866 static_cast<int>(operations.size()),
867 border_width,
868 colour,
869 colour,
870 fill_alpha,
871 stroke_alpha
872 );
873}
874
875inline int pdf_save_buffer(pdf_doc* pdf, std::string& out) {
876 if (pdf == nullptr) {
877 return -EINVAL;
878 }
879 out.clear();
880 out += (pdf_find_first_object(pdf, detail::OBJ_ext_gstate) != nullptr ? "%PDF-1.4\r\n%\xFF\xFF\xFF\xFF\r\n" : "%PDF-1.3\r\n%\xFF\xFF\xFF\xFF\r\n");
881
882 const auto save_object = [&](pdf_object& object) {
883 object.offset = out.size();
884 detail::append_format(out, "%d 0 obj\r\n", object.index);
885 switch (object.type) {
887 detail::append_format(out, "<< /Length %zu >>\r\nstream\r\n", object.stream.stream.size());
888 out += object.stream.stream;
889 out += "\r\nendstream\r\n";
890 break;
891 case detail::OBJ_info:
892 out += "<<\r\n";
893 if (object.info.creator[0] != '\0') {
894 out += " /Creator ";
895 detail::append_escaped_string(out, object.info.creator);
896 out += "\r\n";
897 }
898 if (object.info.producer[0] != '\0') {
899 out += " /Producer ";
900 detail::append_escaped_string(out, object.info.producer);
901 out += "\r\n";
902 }
903 if (object.info.title[0] != '\0') {
904 out += " /Title ";
905 detail::append_escaped_string(out, object.info.title);
906 out += "\r\n";
907 }
908 if (object.info.author[0] != '\0') {
909 out += " /Author ";
910 detail::append_escaped_string(out, object.info.author);
911 out += "\r\n";
912 }
913 if (object.info.subject[0] != '\0') {
914 out += " /Subject ";
915 detail::append_escaped_string(out, object.info.subject);
916 out += "\r\n";
917 }
918 if (object.info.date[0] != '\0') {
919 out += " /CreationDate (D:";
920 out += object.info.date;
921 out += ")\r\n";
922 }
923 out += ">>\r\n";
924 break;
925 case detail::OBJ_page: {
927 out += "<<\r\n /Type /Page\r\n";
928 detail::append_format(out, " /Parent %d 0 R\r\n", pages != nullptr ? pages->index : 0);
929 detail::append_format(out, " /MediaBox [0 0 %f %f]\r\n", object.page.width, object.page.height);
930 out += " /Resources <<\r\n /Font <<\r\n";
931 for (pdf_object* font = pdf_find_first_object(pdf, detail::OBJ_font); font != nullptr; font = font->next) {
932 detail::append_format(out, " /F%d %d 0 R\r\n", font->font.index, font->index);
933 }
934 out += " >>\r\n";
935 if (!object.page.ext_gstates.empty()) {
936 out += " /ExtGState <<\r\n";
937 for (std::size_t index = 0; index < object.page.ext_gstates.size(); ++index) {
938 const pdf_object* ext_gstate = object.page.ext_gstates[index];
939 detail::append_format(out, " /GS%zu %d 0 R\r\n", index, ext_gstate != nullptr ? ext_gstate->index : 0);
940 }
941 out += " >>\r\n";
942 }
943 out += " >>\r\n /Contents [\r\n";
944 for (const pdf_object* child : object.page.children) {
945 detail::append_format(out, "%d 0 R\r\n", child->index);
946 }
947 out += "]\r\n>>\r\n";
948 break;
949 }
952 "<<\r\n /Type /ExtGState\r\n /ca %f\r\n /CA %f\r\n>>\r\n",
953 object.ext_gstate.fill_alpha,
954 object.ext_gstate.stroke_alpha);
955 break;
956 case detail::OBJ_font:
958 "<<\r\n /Type /Font\r\n /Subtype /Type1\r\n /BaseFont /%s\r\n /Encoding /WinAnsiEncoding\r\n>>\r\n",
959 object.font.name);
960 break;
961 case detail::OBJ_pages: {
962 out += "<<\r\n /Type /Pages\r\n /Kids [ ";
963 int pages_count = 0;
964 for (pdf_object* page = pdf_find_first_object(pdf, detail::OBJ_page); page != nullptr; page = page->next) {
965 ++pages_count;
966 detail::append_format(out, "%d 0 R ", page->index);
967 }
968 out += "]\r\n";
969 detail::append_format(out, " /Count %d\r\n>>\r\n", pages_count);
970 break;
971 }
972 case detail::OBJ_catalog: {
974 out += "<<\r\n /Type /Catalog\r\n";
975 detail::append_format(out, " /Pages %d 0 R\r\n>>\r\n", pages != nullptr ? pages->index : 0);
976 break;
977 }
978 case detail::OBJ_none:
979 out += "<<>>\r\n";
980 break;
981 default:
982 break;
983 }
984 out += "endobj\r\n";
985 };
986
987 for (std::size_t index = 1; index < pdf->objects.size(); ++index) {
988 pdf_object* object = pdf->objects[index].get();
989 if (object != nullptr) {
990 save_object(*object);
991 }
992 }
993
994 const std::size_t xref_offset = out.size();
995 detail::append_format(out, "xref\r\n0 %zu\r\n", pdf->objects.size());
996 out += "0000000000 65535 f\r\n";
997 for (std::size_t index = 1; index < pdf->objects.size(); ++index) {
998 const pdf_object* object = pdf->objects[index].get();
999 detail::append_format(out, "%010zu 00000 n\r\n", object != nullptr ? object->offset : static_cast<std::size_t>(0));
1000 }
1001
1004 out += "trailer\r\n<<\r\n";
1005 detail::append_format(out, " /Size %zu\r\n", pdf->objects.size());
1006 if (catalog != nullptr) {
1007 detail::append_format(out, " /Root %d 0 R\r\n", catalog->index);
1008 }
1009 if (info != nullptr) {
1010 detail::append_format(out, " /Info %d 0 R\r\n", info->index);
1011 }
1012 out += ">>\r\nstartxref\r\n";
1013 detail::append_format(out, "%zu\r\n%%%%EOF\r\n", xref_offset);
1014 return 0;
1015}
1016
1017inline int pdf_save_file(pdf_doc* pdf, FILE* fp) {
1018 if (fp == nullptr) {
1019 return pdf_set_err(pdf, -EINVAL, "Invalid FILE pointer");
1020 }
1021 std::string buffer;
1022 const int result = pdf_save_buffer(pdf, buffer);
1023 if (result < 0) {
1024 return result;
1025 }
1026 if (!buffer.empty()) {
1027 const std::size_t written = std::fwrite(buffer.data(), 1, buffer.size(), fp);
1028 if (written != buffer.size()) {
1029 return pdf_set_err(pdf, -errno, "Unable to write PDF bytes");
1030 }
1031 }
1032 return 0;
1033}
1034
1035inline int pdf_save(pdf_doc* pdf, const char* filename) {
1036 if (filename == nullptr) {
1037 return pdf_set_err(pdf, -EINVAL, "Invalid filename");
1038 }
1039 std::ofstream output(filename, std::ios::binary);
1040 if (!output) {
1041 return pdf_set_err(pdf, -errno, "Unable to open '%s'", filename);
1042 }
1043 std::string buffer;
1044 const int result = pdf_save_buffer(pdf, buffer);
1045 if (result < 0) {
1046 return result;
1047 }
1048 output.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
1049 if (!output) {
1050 return pdf_set_err(pdf, -errno, "Unable to write '%s'", filename);
1051 }
1052 return 0;
1053}
1054
1055inline std::string pdf_to_string(pdf_doc* pdf) {
1056 std::string buffer;
1057 if (pdf_save_buffer(pdf, buffer) < 0) {
1058 return {};
1059 }
1060 return buffer;
1061}
1062
1063} // namespace pgl::pdfgen
Definition pdfgen.hpp:103
std::string now_as_pdf_date()
Definition pdfgen.hpp:203
int ascii_casecmp(std::string_view left, std::string_view right)
Definition pdfgen.hpp:157
void append_escaped_string(std::string &out, std::string_view value)
Definition pdfgen.hpp:192
@ OBJ_pages
Definition pdfgen.hpp:113
@ OBJ_stream
Definition pdfgen.hpp:108
@ OBJ_none
Definition pdfgen.hpp:106
@ OBJ_ext_gstate
Definition pdfgen.hpp:111
@ OBJ_count
Definition pdfgen.hpp:114
@ OBJ_page
Definition pdfgen.hpp:110
@ OBJ_font
Definition pdfgen.hpp:109
@ OBJ_catalog
Definition pdfgen.hpp:112
@ OBJ_info
Definition pdfgen.hpp:107
constexpr std::array valid_fonts
Definition pdfgen.hpp:117
void append_format(std::string &out, const char *format,...)
Definition pdfgen.hpp:174
Definition pdfgen.hpp:33
int pdf_add_rectangle(pdf_doc *pdf, pdf_object *page, float x, float y, float width, float height, float border_width, std::uint32_t colour, float stroke_alpha=1.0f)
Definition pdfgen.hpp:755
constexpr float PDF_A3_HEIGHT
Definition pdfgen.hpp:70
constexpr std::uint32_t PDF_TRANSPARENT
Definition pdfgen.hpp:85
constexpr std::uint32_t PDF_WHITE
Definition pdfgen.hpp:84
int pdf_save_buffer(pdf_doc *pdf, std::string &out)
Definition pdfgen.hpp:875
std::string pdf_escape_content_string(std::string_view value)
Definition pdfgen.hpp:479
constexpr std::uint32_t PDF_ARGB(unsigned int a, unsigned int r, unsigned int g, unsigned int b)
Definition pdfgen.hpp:76
int pdf_find_or_create_ext_gstate(pdf_doc *pdf, pdf_object *page, float fill_alpha, float stroke_alpha)
Definition pdfgen.hpp:525
int pdf_save(pdf_doc *pdf, const char *filename)
Definition pdfgen.hpp:1035
constexpr float PDF_MM_TO_POINT(float mm)
Definition pdfgen.hpp:61
pdf_doc * pdf_create(float width, float height, const pdf_info *info)
Definition pdfgen.hpp:378
constexpr float PDF_INCH_TO_POINT(float inch)
Definition pdfgen.hpp:57
int pdf_set_font(pdf_doc *pdf, const char *font)
Definition pdfgen.hpp:341
int pdf_add_text(pdf_doc *pdf, pdf_object *page, const char *text, float size, float xoff, float yoff, std::uint32_t colour)
Definition pdfgen.hpp:494
int pdf_page_set_size(pdf_doc *pdf, pdf_object *page, float width, float height)
Definition pdfgen.hpp:444
constexpr std::uint32_t PDF_RED
Definition pdfgen.hpp:80
constexpr std::uint32_t PDF_GREEN
Definition pdfgen.hpp:81
int pdf_append_object(pdf_doc *pdf, pdf_object *object)
Definition pdfgen.hpp:295
pdf_object * pdf_add_object(pdf_doc *pdf, int type)
Definition pdfgen.hpp:311
constexpr float PDF_RGB_R(std::uint32_t colour)
Definition pdfgen.hpp:87
float pdf_height(const pdf_doc *pdf)
Definition pdfgen.hpp:329
int pdf_add_ellipse(pdf_doc *pdf, pdf_object *page, float x, float y, float xradius, float yradius, float width, std::uint32_t colour, std::uint32_t fill_colour, float fill_alpha=1.0f, float stroke_alpha=1.0f)
Definition pdfgen.hpp:725
bool pdf_alpha_equal(float left, float right)
Definition pdfgen.hpp:521
constexpr std::uint32_t PDF_BLUE
Definition pdfgen.hpp:82
int pdf_add_filled_polygon(pdf_doc *pdf, pdf_object *page, const float x[], const float y[], int count, float border_width, std::uint32_t colour, float fill_alpha=1.0f, float stroke_alpha=1.0f)
Definition pdfgen.hpp:850
constexpr std::uint32_t PDF_BLACK
Definition pdfgen.hpp:83
int pdf_add_quadratic_bezier(pdf_doc *pdf, pdf_object *page, float x1, float y1, float x2, float y2, float xq1, float yq1, float width, std::uint32_t colour, float stroke_alpha=1.0f)
Definition pdfgen.hpp:639
pdf_object * pdf_get_page(pdf_doc *pdf, int page_number)
Definition pdfgen.hpp:430
pdf_object * pdf_get_object(const pdf_doc *pdf, int index)
Definition pdfgen.hpp:280
float pdf_width(const pdf_doc *pdf)
Definition pdfgen.hpp:325
constexpr std::uint32_t PDF_RGB(unsigned int r, unsigned int g, unsigned int b)
Definition pdfgen.hpp:72
std::string pdf_to_string(pdf_doc *pdf)
Definition pdfgen.hpp:1055
int pdf_add_polygon(pdf_doc *pdf, pdf_object *page, const float x[], const float y[], int count, float border_width, std::uint32_t colour, float stroke_alpha=1.0f)
Definition pdfgen.hpp:825
void pdf_clear_err(pdf_doc *pdf)
Definition pdfgen.hpp:272
constexpr float PDF_LETTER_WIDTH
Definition pdfgen.hpp:65
void pdf_destroy(pdf_doc *pdf)
Definition pdfgen.hpp:416
int pdf_add_circle(pdf_doc *pdf, pdf_object *page, float x, float y, float radius, float width, std::uint32_t colour, std::uint32_t fill_colour, float fill_alpha=1.0f, float stroke_alpha=1.0f)
Definition pdfgen.hpp:750
int pdf_add_cubic_bezier(pdf_doc *pdf, pdf_object *page, float x1, float y1, float x2, float y2, float xq1, float yq1, float xq2, float yq2, float width, std::uint32_t colour, float stroke_alpha=1.0f)
Definition pdfgen.hpp:606
int pdf_set_err(pdf_doc *doc, int errval, const char *buffer,...)
Definition pdfgen.hpp:245
pdf_object * pdf_find_first_object(const pdf_doc *pdf, int type)
Definition pdfgen.hpp:287
pdf_object * pdf_find_last_object(const pdf_doc *pdf, int type)
Definition pdfgen.hpp:291
int pdf_add_stream(pdf_doc *pdf, pdf_object *page, const std::string &buffer)
Definition pdfgen.hpp:456
const char * pdf_get_err(const pdf_doc *pdf, int *errval)
Definition pdfgen.hpp:262
float pdf_clamp_alpha(float alpha)
Definition pdfgen.hpp:514
float pdf_page_height(const pdf_object *page)
Definition pdfgen.hpp:337
constexpr bool PDF_IS_TRANSPARENT(std::uint32_t colour)
Definition pdfgen.hpp:99
int pdf_add_custom_path(pdf_doc *pdf, pdf_object *page, const pdf_path_operation *operations, int operation_count, float stroke_width, std::uint32_t stroke_colour, std::uint32_t fill_colour, float fill_alpha=1.0f, float stroke_alpha=1.0f)
Definition pdfgen.hpp:648
int pdf_add_filled_rectangle(pdf_doc *pdf, pdf_object *page, float x, float y, float width, float height, float border_width, std::uint32_t colour_fill, std::uint32_t colour_border, float fill_alpha=1.0f, float stroke_alpha=1.0f)
Definition pdfgen.hpp:786
pdf_object * pdf_append_page(pdf_doc *pdf)
Definition pdfgen.hpp:420
int pdf_add_line(pdf_doc *pdf, pdf_object *page, float x1, float y1, float x2, float y2, float width, std::uint32_t colour, float stroke_alpha=1.0f)
Definition pdfgen.hpp:601
constexpr float PDF_RGB_G(std::uint32_t colour)
Definition pdfgen.hpp:91
float pdf_page_width(const pdf_object *page)
Definition pdfgen.hpp:333
constexpr float PDF_A4_HEIGHT
Definition pdfgen.hpp:68
constexpr float PDF_A4_WIDTH
Definition pdfgen.hpp:67
int pdf_add_line_pattern(pdf_doc *pdf, pdf_object *page, float x1, float y1, float x2, float y2, float width, std::uint32_t colour, const float pattern[], int pattern_len, float phase, float stroke_alpha=1.0f)
Definition pdfgen.hpp:555
constexpr float PDF_A3_WIDTH
Definition pdfgen.hpp:69
int pdf_save_file(pdf_doc *pdf, FILE *fp)
Definition pdfgen.hpp:1017
constexpr float PDF_RGB_B(std::uint32_t colour)
Definition pdfgen.hpp:95
constexpr float PDF_LETTER_HEIGHT
Definition pdfgen.hpp:66
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
Definition pdfgen.hpp:234
std::array< pdf_object *, detail::OBJ_count > first_objects
Definition pdfgen.hpp:242
std::vector< std::unique_ptr< pdf_object > > objects
Definition pdfgen.hpp:237
char errstr[128]
Definition pdfgen.hpp:235
float height
Definition pdfgen.hpp:239
std::array< pdf_object *, detail::OBJ_count > last_objects
Definition pdfgen.hpp:241
int errval
Definition pdfgen.hpp:236
pdf_object * current_font
Definition pdfgen.hpp:240
float width
Definition pdfgen.hpp:238
Definition pdfgen.hpp:38
char author[64]
Definition pdfgen.hpp:42
char subject[64]
Definition pdfgen.hpp:43
char creator[64]
Definition pdfgen.hpp:39
char title[64]
Definition pdfgen.hpp:41
char producer[64]
Definition pdfgen.hpp:40
char date[64]
Definition pdfgen.hpp:44
Definition pdfgen.hpp:221
detail::ext_gstate_data ext_gstate
Definition pdfgen.hpp:231
pdf_object * next
Definition pdfgen.hpp:226
std::size_t offset
Definition pdfgen.hpp:224
detail::page_data page
Definition pdfgen.hpp:229
pdf_object * prev
Definition pdfgen.hpp:225
pdf_info info
Definition pdfgen.hpp:227
detail::stream_data stream
Definition pdfgen.hpp:228
int index
Definition pdfgen.hpp:223
int type
Definition pdfgen.hpp:222
detail::font_data font
Definition pdfgen.hpp:230
Definition pdfgen.hpp:47
float x2
Definition pdfgen.hpp:51
float y1
Definition pdfgen.hpp:50
char op
Definition pdfgen.hpp:48
float y3
Definition pdfgen.hpp:54
float x3
Definition pdfgen.hpp:53
float y2
Definition pdfgen.hpp:52
float x1
Definition pdfgen.hpp:49