NetSurf
save_pdf.c
Go to the documentation of this file.
1/*
2 * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
3 * Copyright 2009 John Tytgat <joty@netsurf-browser.org>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /** \file
21 * Target independent PDF plotting using Haru Free PDF Library.
22 */
23
24/* TODO
25 * - finish all graphic primitives
26 * - allow adding raw bitmaps
27 * - make image-aware (embed the image in its native/original type if possible)
28 *
29 * - adjust content width to page width
30 * - divide output into multiple pages (not just the first one)
31 * - rearrange file structure
32 *
33 * - separate print-plotting as much as possible from window redrawing
34 * - add text-scaling (if not yet using the original font - make the default one
35 * have the same width)
36 * - add a save file.. dialogue
37 * - add utf support to Haru ( doable? )
38 * - wait for browser to end fetching?
39 * - analyze and deal with performance issues(huge file hangs some pdf viewers,
40 * for example kpdf when viewing plotted http://www.onet.pl)
41 * - deal with to wide pages - when window layouting adds a horizontal
42 * scrollbar, we should treat it otherwise - either print
43 * horizontal or scale or, better, find a new layout.
44 */
45
46#include "utils/config.h"
47#include "utils/errors.h"
48
49#include "desktop/save_pdf.h"
50
51#ifdef WITH_PDF_EXPORT
52
53#include <assert.h>
54#include <stdlib.h>
55#include <string.h>
56#include <hpdf.h>
57
58#include "utils/log.h"
59#include "utils/utils.h"
60#include "utils/useragent.h"
61#include "content/hlcache.h"
62#include "utils/nsoption.h"
63#include "netsurf/bitmap.h"
64
65#include "netsurf/plotters.h"
66#include "desktop/print.h"
67#include "desktop/printer.h"
68
69#include "font_haru.h"
70
71/* #define PDF_DEBUG */
72/* #define PDF_DEBUG_DUMPGRID */
73
74static bool pdf_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style);
75static bool pdf_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle);
76static bool pdf_plot_polygon(const int *p, unsigned int n, const plot_style_t *style);
77static bool pdf_plot_clip(const struct rect *clip);
78static bool pdf_plot_text(int x, int y, const char *text, size_t length,
79 const plot_font_style_t *fstyle);
80static bool pdf_plot_disc(int x, int y, int radius, const plot_style_t *style);
81static bool pdf_plot_arc(int x, int y, int radius, int angle1, int angle2,
82 const plot_style_t *style);
83static bool pdf_plot_bitmap_tile(int x, int y, int width, int height,
84 struct bitmap *bitmap, colour bg,
85 bitmap_flags_t flags);
86static bool pdf_plot_path(const float *p, unsigned int n, colour fill, float width,
87 colour c, const float transform[6]);
88
89static HPDF_Image pdf_extract_image(struct bitmap *bitmap);
90
91static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no,
92 void *user_data);
93
94#ifdef PDF_DEBUG_DUMPGRID
95static void pdf_plot_grid(int x_dist,int y_dist,unsigned int colour);
96#endif
97
98typedef enum {
99 DashPattern_eNone,
100 DashPattern_eDash,
101 DashPattern_eDotted
102} DashPattern_e;
103
104/* Wrapper routines to minimize gstate updates in the produced PDF file. */
105static void pdfw_gs_init(void);
106static void pdfw_gs_save(HPDF_Page page);
107static void pdfw_gs_restore(HPDF_Page page);
108static void pdfw_gs_fillcolour(HPDF_Page page, colour col);
109static void pdfw_gs_strokecolour(HPDF_Page page, colour col);
110static void pdfw_gs_linewidth(HPDF_Page page, float lineWidth);
111static void pdfw_gs_font(HPDF_Page page, HPDF_Font font, HPDF_REAL font_size);
112static void pdfw_gs_dash(HPDF_Page page, DashPattern_e dash);
113
114/**
115 * Our PDF gstate mirror which we use to minimize gstate updates
116 * in the PDF file.
117 */
118typedef struct {
119 colour fillColour; /**< Current fill colour. */
120 colour strokeColour; /**< Current stroke colour. */
121 float lineWidth; /**< Current line width. */
122 HPDF_Font font; /**< Current font. */
123 HPDF_REAL font_size; /**< Current font size. */
124 DashPattern_e dash; /**< Current dash state. */
125} PDFW_GState;
126
127static void apply_clip_and_mode(bool selectTextMode, colour fillCol,
128 colour strokeCol, float lineWidth, DashPattern_e dash);
129
130#define PDFW_MAX_GSTATES 4
131static PDFW_GState pdfw_gs[PDFW_MAX_GSTATES];
132static unsigned int pdfw_gs_level;
133
134static HPDF_Doc pdf_doc; /**< Current PDF document. */
135static HPDF_Page pdf_page; /**< Current page. */
136
137/*PDF Page size*/
138static HPDF_REAL page_height, page_width;
139
140static bool in_text_mode; /**< true if we're currently in text mode or not. */
141static bool clip_update_needed; /**< true if pdf_plot_clip was invoked for
142 current page and not yet synced with PDF output. */
143static int last_clip_x0, last_clip_y0, last_clip_x1, last_clip_y1;
144
145static const struct print_settings *settings;
146
147static const struct plotter_table pdf_plotters = {
148 .rectangle = pdf_plot_rectangle,
149 .line = pdf_plot_line,
150 .polygon = pdf_plot_polygon,
151 .clip = pdf_plot_clip,
152 .text = pdf_plot_text,
153 .disc = pdf_plot_disc,
154 .arc = pdf_plot_arc,
155 .bitmap = pdf_plot_bitmap_tile,
156 .path = pdf_plot_path,
157 .option_knockout = false,
158};
159
160const struct printer pdf_printer = {
161 &pdf_plotters,
162 pdf_begin,
164 pdf_end
165};
166
167static char *owner_pass;
168static char *user_pass;
169
170bool pdf_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
171{
172 DashPattern_e dash;
173#ifdef PDF_DEBUG
174 NSLOG(netsurf, INFO, "%d %d %d %d %f %X", x0, y0, x1, y1,
175 page_height - y0, pstyle->fill_colour);
176#endif
177
178 if (pstyle->fill_type != PLOT_OP_TYPE_NONE) {
179
180 apply_clip_and_mode(false, pstyle->fill_colour, NS_TRANSPARENT, 0., DashPattern_eNone);
181
182 /* Normalize boundaries of the area - to prevent
183 overflows. It is needed only in a few functions,
184 where integers are subtracted. When the whole
185 browser window is meant min and max int values are
186 used what must be handled in paged output.
187 */
188 x0 = min(max(x0, 0), page_width);
189 y0 = min(max(y0, 0), page_height);
190 x1 = min(max(x1, 0), page_width);
191 y1 = min(max(y1, 0), page_height);
192
193 HPDF_Page_Rectangle(pdf_page, x0, page_height - y1, x1 - x0, y1 - y0);
194 HPDF_Page_Fill(pdf_page);
195
196 }
197
198 if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) {
199
200 switch (pstyle->stroke_type) {
201 case PLOT_OP_TYPE_DOT:
202 dash = DashPattern_eDotted;
203 break;
204
206 dash = DashPattern_eDash;
207 break;
208
209 default:
210 dash = DashPattern_eNone;
211 break;
212
213 }
214
215 apply_clip_and_mode(false,
217 pstyle->stroke_colour,
219 dash);
220
221 HPDF_Page_Rectangle(pdf_page, x0, page_height - y0, x1 - x0, -(y1 - y0));
222 HPDF_Page_Stroke(pdf_page);
223 }
224
225 return true;
226}
227
228bool pdf_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
229{
230 DashPattern_e dash;
231
232 switch (pstyle->stroke_type) {
233 case PLOT_OP_TYPE_DOT:
234 dash = DashPattern_eDotted;
235 break;
236
238 dash = DashPattern_eDash;
239 break;
240
241 default:
242 dash = DashPattern_eNone;
243 break;
244
245 }
246
247 apply_clip_and_mode(false,
249 pstyle->stroke_colour,
251 dash);
252
253 HPDF_Page_MoveTo(pdf_page, x0, page_height - y0);
254 HPDF_Page_LineTo(pdf_page, x1, page_height - y1);
255 HPDF_Page_Stroke(pdf_page);
256
257 return true;
258}
259
260bool pdf_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
261{
262 unsigned int i;
263#ifdef PDF_DEBUG
264 int pmaxx = p[0], pmaxy = p[1];
265 int pminx = p[0], pminy = p[1];
266 NSLOG(netsurf, INFO, ".");
267#endif
268 if (n == 0)
269 return true;
270
271 apply_clip_and_mode(false, style->fill_colour, NS_TRANSPARENT, 0., DashPattern_eNone);
272
273 HPDF_Page_MoveTo(pdf_page, p[0], page_height - p[1]);
274 for (i = 1 ; i<n ; i++) {
275 HPDF_Page_LineTo(pdf_page, p[i*2], page_height - p[i*2+1]);
276#ifdef PDF_DEBUG
277 pmaxx = max(pmaxx, p[i*2]);
278 pmaxy = max(pmaxy, p[i*2+1]);
279 pminx = min(pminx, p[i*2]);
280 pminy = min(pminy, p[i*2+1]);
281#endif
282 }
283
284#ifdef PDF_DEBUG
285 NSLOG(netsurf, INFO, "%d %d %d %d %f", pminx, pminy, pmaxx, pmaxy,
286 page_height - pminy);
287#endif
288
289 HPDF_Page_Fill(pdf_page);
290
291 return true;
292}
293
294
295/**here the clip is only queried */
296bool pdf_plot_clip(const struct rect *clip)
297{
298#ifdef PDF_DEBUG
299 NSLOG(netsurf, INFO, "%d %d %d %d", clip->x0, clip->y0, clip->x1,
300 clip->y1);
301#endif
302
303 /*Normalize cllipping area - to prevent overflows.
304 See comment in pdf_plot_fill.
305 */
306 last_clip_x0 = min(max(clip->x0, 0), page_width);
307 last_clip_y0 = min(max(clip->y0, 0), page_height);
308 last_clip_x1 = min(max(clip->x1, 0), page_width);
309 last_clip_y1 = min(max(clip->y1, 0), page_height);
310
311 clip_update_needed = true;
312
313 return true;
314}
315
316bool pdf_plot_text(int x, int y, const char *text, size_t length,
317 const plot_font_style_t *fstyle)
318{
319#ifdef PDF_DEBUG
320 NSLOG(netsurf, INFO, ". %d %d %.*s", x, y, (int)length, text);
321#endif
322 char *word;
323 HPDF_Font pdf_font;
324 HPDF_REAL size;
325
326 if (length == 0)
327 return true;
328
329 apply_clip_and_mode(true, fstyle->foreground, NS_TRANSPARENT, 0.,
330 DashPattern_eNone);
331
332 haru_nsfont_apply_style(fstyle, pdf_doc, pdf_page, &pdf_font, &size);
333 pdfw_gs_font(pdf_page, pdf_font, size);
334
335 /* FIXME: UTF-8 to current font encoding needs to done. Or the font
336 * encoding needs to be UTF-8 or other Unicode encoding. */
337 word = (char *)malloc( sizeof(char) * (length+1) );
338 if (word == NULL)
339 return false;
340 memcpy(word, text, length);
341 word[length] = '\0';
342
343 HPDF_Page_TextOut (pdf_page, x, page_height - y, word);
344
345 free(word);
346
347 return true;
348}
349
350bool pdf_plot_disc(int x, int y, int radius, const plot_style_t *style)
351{
352#ifdef PDF_DEBUG
353 NSLOG(netsurf, INFO, ".");
354#endif
355 if (style->fill_type != PLOT_OP_TYPE_NONE) {
356 apply_clip_and_mode(false,
357 style->fill_colour,
359 1., DashPattern_eNone);
360
361 HPDF_Page_Circle(pdf_page, x, page_height - y, radius);
362
363 HPDF_Page_Fill(pdf_page);
364 }
365
366 if (style->stroke_type != PLOT_OP_TYPE_NONE) {
367 /* FIXME: line width 1 is ok ? */
368 apply_clip_and_mode(false,
370 style->stroke_colour,
371 1., DashPattern_eNone);
372
373 HPDF_Page_Circle(pdf_page, x, page_height - y, radius);
374
375 HPDF_Page_Stroke(pdf_page);
376 }
377
378 return true;
379}
380
381bool pdf_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
382{
383#ifdef PDF_DEBUG
384 NSLOG(netsurf, INFO, "%d %d %d %d %d %X", x, y, radius, angle1,
385 angle2, style->stroke_colour);
386#endif
387
388 /* FIXME: line width 1 is ok ? */
389 apply_clip_and_mode(false, NS_TRANSPARENT, style->fill_colour, 1., DashPattern_eNone);
390
391 /* Normalize angles */
392 angle1 %= 360;
393 angle2 %= 360;
394 if (angle1 > angle2)
395 angle1 -= 360;
396
397 HPDF_Page_Arc(pdf_page, x, page_height - y, radius, angle1, angle2);
398
399 HPDF_Page_Stroke(pdf_page);
400 return true;
401}
402
403
404bool pdf_plot_bitmap_tile(int x, int y, int width, int height,
405 struct bitmap *bitmap, colour bg,
406 bitmap_flags_t flags)
407{
408 HPDF_Image image;
409 HPDF_REAL current_x, current_y ;
410 HPDF_REAL max_width, max_height;
411
412#ifdef PDF_DEBUG
413 NSLOG(netsurf, INFO, "%d %d %d %d %p 0x%x", x, y, width, height,
414 bitmap, bg);
415#endif
416 if (width == 0 || height == 0)
417 return true;
418
419 apply_clip_and_mode(false, NS_TRANSPARENT, NS_TRANSPARENT, 0., DashPattern_eNone);
420
421 image = pdf_extract_image(bitmap);
422 if (!image)
423 return false;
424
425 /*The position of the next tile*/
426 max_width = (flags & BITMAPF_REPEAT_X) ? page_width : width;
427 max_height = (flags & BITMAPF_REPEAT_Y) ? page_height : height;
428
429 for (current_y = 0; current_y < max_height; current_y += height)
430 for (current_x = 0; current_x < max_width; current_x += width)
431 HPDF_Page_DrawImage(pdf_page, image,
432 current_x + x,
433 page_height - current_y - y - height,
434 width, height);
435
436 return true;
437}
438
439HPDF_Image pdf_extract_image(struct bitmap *bitmap)
440{
441 HPDF_Image image = NULL;
442 hlcache_handle *content = NULL;
443
444 /* TODO - get content from bitmap pointer */
445
446 if (content) {
447 const char *source_data;
448 unsigned long source_size;
449
450 /*Not sure if I don't have to check if downloading has been
451 finished.
452 Other way - lock pdf plotting while fetching a website
453 */
454 source_data = content_get_source_data(content, &source_size);
455
456 switch(content_get_type(content)){
457 /*Handle "embeddable" types of images*/
458 case CONTENT_JPEG:
459 image = HPDF_LoadJpegImageFromMem(pdf_doc,
460 (const HPDF_BYTE *) source_data,
461 source_size);
462 break;
463
464 /*Disabled until HARU PNG support will be more stable.
465
466 case CONTENT_PNG:
467 image = HPDF_LoadPngImageFromMem(pdf_doc,
468 (const HPDF_BYTE *)content->source_data,
469 content->total_size);
470 break;*/
471 default:
472 break;
473 }
474 }
475
476 if (!image) {
477 HPDF_Image smask;
478 unsigned char *img_buffer, *rgb_buffer, *alpha_buffer;
479 int img_width, img_height, img_rowstride;
480 int i, j;
481
482 /*Handle pixmaps*/
483 img_buffer = bitmap_get_buffer(bitmap);
484 img_width = bitmap_get_width(bitmap);
485 img_height = bitmap_get_height(bitmap);
486 img_rowstride = bitmap_get_rowstride(bitmap);
487
488 rgb_buffer = (unsigned char *)malloc(3 * img_width * img_height);
489 alpha_buffer = (unsigned char *)malloc(img_width * img_height);
490 if (rgb_buffer == NULL || alpha_buffer == NULL) {
491 NSLOG(netsurf, INFO,
492 "Not enough memory to create RGB buffer");
493 free(rgb_buffer);
494 free(alpha_buffer);
495 return NULL;
496 }
497
498 for (i = 0; i < img_height; i++)
499 for (j = 0; j < img_width; j++) {
500 rgb_buffer[((i * img_width) + j) * 3] =
501 img_buffer[(i * img_rowstride) + (j * 4)];
502
503 rgb_buffer[(((i * img_width) + j) * 3) + 1] =
504 img_buffer[(i * img_rowstride) + (j * 4) + 1];
505
506 rgb_buffer[(((i * img_width) + j) * 3) + 2] =
507 img_buffer[(i * img_rowstride) + (j * 4) + 2];
508
509 alpha_buffer[(i * img_width)+j] =
510 img_buffer[(i * img_rowstride) + (j * 4) + 3];
511 }
512
513 smask = HPDF_LoadRawImageFromMem(pdf_doc, alpha_buffer,
514 img_width, img_height,
515 HPDF_CS_DEVICE_GRAY, 8);
516
517 image = HPDF_LoadRawImageFromMem(pdf_doc, rgb_buffer,
518 img_width, img_height,
519 HPDF_CS_DEVICE_RGB, 8);
520
521 if (HPDF_Image_AddSMask(image, smask) != HPDF_OK)
522 image = NULL;
523
524 free(rgb_buffer);
525 free(alpha_buffer);
526 }
527
528 return image;
529}
530
531/**
532 * Enter/leave text mode and update PDF gstate for its clip, fill & stroke
533 * colour, line width and dash pattern parameters.
534 * \param selectTextMode true if text mode needs to be entered if required;
535 * false otherwise.
536 * \param fillCol Desired fill colour, use NS_TRANSPARENT if no update is
537 * required.
538 * \param strokeCol Desired stroke colour, use NS_TRANSPARENT if no update is
539 * required.
540 * \param lineWidth Desired line width. Only taken into account when strokeCol
541 * is different from NS_TRANSPARENT.
542 * \param dash Desired dash pattern. Only taken into account when strokeCol
543 * is different from NS_TRANSPARENT.
544 */
545static void apply_clip_and_mode(bool selectTextMode, colour fillCol,
546 colour strokeCol, float lineWidth, DashPattern_e dash)
547{
548 /* Leave text mode when
549 * 1) we're not setting text anymore
550 * 2) or we need to update the current clippath
551 * 3) or we need to update any fill/stroke colour, linewidth or dash.
552 * Note: the test on stroke parameters (stroke colour, line width and
553 * dash) is commented out as if these need updating we want to be
554 * outside the text mode anyway (i.e. selectTextMode is false).
555 */
556 if (in_text_mode && (!selectTextMode || clip_update_needed
557 || (fillCol != NS_TRANSPARENT
558 && fillCol != pdfw_gs[pdfw_gs_level].fillColour)
559 /* || (strokeCol != NS_TRANSPARENT
560 && (strokeCol != pdfw_gs[pdfw_gs_level].strokeColour
561 || lineWidth != pdfw_gs[pdfw_gs_level].lineWidth
562 || dash != pdfw_gs[pdfw_gs_level].dash)) */)) {
563 HPDF_Page_EndText(pdf_page);
564 in_text_mode = false;
565 }
566
567 if (clip_update_needed)
568 pdfw_gs_restore(pdf_page);
569
570 /* Update fill/stroke colour, linewidth and dash when needed. */
571 if (fillCol != NS_TRANSPARENT)
572 pdfw_gs_fillcolour(pdf_page, fillCol);
573 if (strokeCol != NS_TRANSPARENT) {
574 pdfw_gs_strokecolour(pdf_page, strokeCol);
575 pdfw_gs_linewidth(pdf_page, lineWidth);
576 pdfw_gs_dash(pdf_page, dash);
577 }
578
579 if (clip_update_needed) {
580 pdfw_gs_save(pdf_page);
581
582 HPDF_Page_Rectangle(pdf_page, last_clip_x0,
583 page_height - last_clip_y1,
584 last_clip_x1 - last_clip_x0,
585 last_clip_y1 - last_clip_y0);
586 HPDF_Page_Clip(pdf_page);
587 HPDF_Page_EndPath(pdf_page);
588
589 clip_update_needed = false;
590 }
591
592 if (selectTextMode && !in_text_mode) {
593 HPDF_Page_BeginText(pdf_page);
594 in_text_mode = true;
595 }
596}
597
598static inline float transform_x(const float transform[6], float x, float y)
599{
600 return transform[0] * x + transform[2] * y + transform[4];
601}
602
603static inline float transform_y(const float transform[6], float x, float y)
604{
605 return page_height
606 - (transform[1] * x + transform[3] * y + transform[5]);
607}
608
609bool pdf_plot_path(const float *p, unsigned int n, colour fill, float width,
610 colour c, const float transform[6])
611{
612 unsigned int i;
613 bool empty_path;
614
615#ifdef PDF_DEBUG
616 NSLOG(netsurf, INFO, ".");
617#endif
618
619 if (n == 0)
620 return true;
621
622 if (c == NS_TRANSPARENT && fill == NS_TRANSPARENT)
623 return true;
624
625 if (p[0] != PLOTTER_PATH_MOVE)
626 return false;
627
628 apply_clip_and_mode(false, fill, c, width, DashPattern_eNone);
629
630 empty_path = true;
631 for (i = 0 ; i < n ; ) {
632 if (p[i] == PLOTTER_PATH_MOVE) {
633 HPDF_Page_MoveTo(pdf_page,
634 transform_x(transform, p[i+1], p[i+2]),
635 transform_y(transform, p[i+1], p[i+2]));
636 i+= 3;
637 } else if (p[i] == PLOTTER_PATH_CLOSE) {
638 if (!empty_path)
639 HPDF_Page_ClosePath(pdf_page);
640 i++;
641 } else if (p[i] == PLOTTER_PATH_LINE) {
642 HPDF_Page_LineTo(pdf_page,
643 transform_x(transform, p[i+1], p[i+2]),
644 transform_y(transform, p[i+1], p[i+2]));
645 i+=3;
646 empty_path = false;
647 } else if (p[i] == PLOTTER_PATH_BEZIER) {
648 HPDF_Page_CurveTo(pdf_page,
649 transform_x(transform, p[i+1], p[i+2]),
650 transform_y(transform, p[i+1], p[i+2]),
651 transform_x(transform, p[i+3], p[i+4]),
652 transform_y(transform, p[i+3], p[i+4]),
653 transform_x(transform, p[i+5], p[i+6]),
654 transform_y(transform, p[i+5], p[i+6]));
655 i += 7;
656 empty_path = false;
657 } else {
658 NSLOG(netsurf, INFO, "bad path command %f", p[i]);
659 return false;
660 }
661 }
662
663 if (empty_path) {
664 HPDF_Page_EndPath(pdf_page);
665 return true;
666 }
667
668 if (fill != NS_TRANSPARENT) {
669 if (c != NS_TRANSPARENT)
670 HPDF_Page_FillStroke(pdf_page);
671 else
672 HPDF_Page_Fill(pdf_page);
673 }
674 else
675 HPDF_Page_Stroke(pdf_page);
676
677 return true;
678}
679
680/**
681 * Begin pdf plotting - initialize a new document
682 * \param path Output file path
683 * \param pg_width page width
684 * \param pg_height page height
685 */
687{
688 pdfw_gs_init();
689
690 if (pdf_doc != NULL)
691 HPDF_Free(pdf_doc);
692 pdf_doc = HPDF_New(error_handler, NULL);
693 if (!pdf_doc) {
694 NSLOG(netsurf, INFO, "Error creating pdf_doc");
695 return false;
696 }
697
699
700 page_width = settings->page_width -
701 FIXTOFLT(FSUB(settings->margins[MARGINLEFT],
703
704 page_height = settings->page_height -
705 FIXTOFLT(settings->margins[MARGINTOP]);
706
707
708#ifndef PDF_DEBUG
709 if (option_enable_PDF_compression)
710 HPDF_SetCompressionMode(pdf_doc, HPDF_COMP_ALL); /*Compression on*/
711#endif
712 HPDF_SetInfoAttr(pdf_doc, HPDF_INFO_CREATOR, user_agent_string());
713
714 pdf_page = NULL;
715
716#ifdef PDF_DEBUG
717 NSLOG(netsurf, INFO, "pdf_begin finishes");
718#endif
719 return true;
720}
721
722
723bool pdf_next_page(void)
724{
725#ifdef PDF_DEBUG
726 NSLOG(netsurf, INFO, "pdf_next_page begins");
727#endif
728 clip_update_needed = false;
729 if (pdf_page != NULL) {
730 apply_clip_and_mode(false, NS_TRANSPARENT, NS_TRANSPARENT, 0.,
731 DashPattern_eNone);
732 pdfw_gs_restore(pdf_page);
733 }
734
735#ifdef PDF_DEBUG_DUMPGRID
736 if (pdf_page != NULL) {
737 pdf_plot_grid(10, 10, 0xCCCCCC);
738 pdf_plot_grid(100, 100, 0xCCCCFF);
739 }
740#endif
741 pdf_page = HPDF_AddPage(pdf_doc);
742 if (pdf_page == NULL)
743 return false;
744
745 HPDF_Page_SetWidth (pdf_page, settings->page_width);
746 HPDF_Page_SetHeight(pdf_page, settings->page_height);
747
748 HPDF_Page_Concat(pdf_page, 1, 0, 0, 1,
749 FIXTOFLT(settings->margins[MARGINLEFT]), 0);
750
751 pdfw_gs_save(pdf_page);
752
753#ifdef PDF_DEBUG
754 NSLOG(netsurf, INFO, "%f %f", page_width, page_height);
755#endif
756
757 return true;
758}
759
760
761void pdf_end(void)
762{
763#ifdef PDF_DEBUG
764 NSLOG(netsurf, INFO, "pdf_end begins");
765#endif
766 clip_update_needed = false;
767 if (pdf_page != NULL) {
768 apply_clip_and_mode(false, NS_TRANSPARENT, NS_TRANSPARENT, 0.,
769 DashPattern_eNone);
770 pdfw_gs_restore(pdf_page);
771 }
772
773#ifdef PDF_DEBUG_DUMPGRID
774 if (pdf_page != NULL) {
775 pdf_plot_grid(10, 10, 0xCCCCCC);
776 pdf_plot_grid(100, 100, 0xCCCCFF);
777 }
778#endif
779
780 assert(settings->output != NULL);
781
782 /*Encryption on*/
783 if (option_enable_PDF_password)
784 guit->misc->pdf_password(&owner_pass, &user_pass,
785 (void *)settings->output);
786 else
788#ifdef PDF_DEBUG
789 NSLOG(netsurf, INFO, "pdf_end finishes");
790#endif
791}
792
793/** saves the pdf with optional encryption */
794nserror save_pdf(const char *path)
795{
796 nserror res = NSERROR_OK;
797
798 if (option_enable_PDF_password && owner_pass != NULL ) {
799 HPDF_SetPassword(pdf_doc, owner_pass, user_pass);
800 HPDF_SetEncryptionMode(pdf_doc, HPDF_ENCRYPT_R3, 16);
801 free(owner_pass);
802 free(user_pass);
803 }
804
805 if (path != NULL) {
806 if (HPDF_SaveToFile(pdf_doc, path) != HPDF_OK) {
807 remove(path);
809 }
810 }
811
812 HPDF_Free(pdf_doc);
813 pdf_doc = NULL;
814
815 return res;
816}
817
818
819/**
820 * Haru error handler
821 * for debugging purposes - it immediately exits the program on the first error,
822 * as it would otherwise flood the user with all resulting complications,
823 * covering the most important error source.
824*/
825static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no,
826 void *user_data)
827{
828 NSLOG(netsurf, INFO, "ERROR:\n\terror_no=%x\n\tdetail_no=%d\n",
829 (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
830#ifdef PDF_DEBUG
831 exit(1);
832#endif
833}
834
835/**
836 * This function plots a grid - used for debug purposes to check if all
837 * elements' final coordinates are correct.
838*/
839#ifdef PDF_DEBUG_DUMPGRID
840void pdf_plot_grid(int x_dist, int y_dist, unsigned int colour)
841{
842 for (int i = x_dist ; i < page_width ; i += x_dist)
843 pdf_plot_line(i, 0, i, page_height, 1, colour, false, false);
844
845 for (int i = y_dist ; i < page_height ; i += x_dist)
846 pdf_plot_line(0, i, page_width, i, 1, colour, false, false);
847}
848#endif
849
850/**
851 * Initialize the gstate wrapper code.
852 */
853void pdfw_gs_init()
854{
855 pdfw_gs_level = 0;
856 pdfw_gs[0].fillColour = 0x00000000; /* Default PDF fill colour is black. */
857 pdfw_gs[0].strokeColour = 0x00000000; /* Default PDF stroke colour is black. */
858 pdfw_gs[0].lineWidth = 1.0; /* Default PDF line width is 1. */
859 pdfw_gs[0].font = NULL;
860 pdfw_gs[0].font_size = 0.;
861 pdfw_gs[0].dash = DashPattern_eNone; /* Default dash state is a solid line. */
862}
863
864/**
865 * Increase gstate level.
866 * \param page PDF page where the update needs to happen.
867 */
868void pdfw_gs_save(HPDF_Page page)
869{
870 if (pdfw_gs_level == PDFW_MAX_GSTATES)
871 abort();
872 pdfw_gs[pdfw_gs_level + 1] = pdfw_gs[pdfw_gs_level];
873 ++pdfw_gs_level;
874 HPDF_Page_GSave(page);
875}
876
877/**
878 * Decrease gstate level and restore the gstate to its value at last save
879 * operation.
880 * \param page PDF page where the update needs to happen.
881 */
882void pdfw_gs_restore(HPDF_Page page)
883{
884 if (pdfw_gs_level == 0)
885 abort();
886 --pdfw_gs_level;
887 HPDF_Page_GRestore(page);
888}
889
890#define RBYTE(x) (((x) & 0x0000FF) >> 0)
891#define GBYTE(x) (((x) & 0x00FF00) >> 8)
892#define BBYTE(x) (((x) & 0xFF0000) >> 16)
893#define R(x) (RBYTE(x) / 255.)
894#define G(x) (GBYTE(x) / 255.)
895#define B(x) (BBYTE(x) / 255.)
896
897/**
898 * Checks if given fill colour is already set in PDF gstate and if not,
899 * update the gstate accordingly.
900 * \param page PDF page where the update needs to happen.
901 * \param col Wanted fill colour.
902 */
903void pdfw_gs_fillcolour(HPDF_Page page, colour col)
904{
905 if (col == pdfw_gs[pdfw_gs_level].fillColour)
906 return;
907 pdfw_gs[pdfw_gs_level].fillColour = col;
908 if (RBYTE(col) == GBYTE(col) && GBYTE(col) == BBYTE(col))
909 HPDF_Page_SetGrayFill(pdf_page, R(col));
910 else
911 HPDF_Page_SetRGBFill(pdf_page, R(col), G(col), B(col));
912}
913
914/**
915 * Checks if given stroke colour is already set in PDF gstate and if not,
916 * update the gstate accordingly.
917 * \param page PDF page where the update needs to happen.
918 * \param col Wanted stroke colour.
919 */
920void pdfw_gs_strokecolour(HPDF_Page page, colour col)
921{
922 if (col == pdfw_gs[pdfw_gs_level].strokeColour)
923 return;
924 pdfw_gs[pdfw_gs_level].strokeColour = col;
925 if (RBYTE(col) == GBYTE(col) && GBYTE(col) == BBYTE(col))
926 HPDF_Page_SetGrayStroke(pdf_page, R(col));
927 else
928 HPDF_Page_SetRGBStroke(pdf_page, R(col), G(col), B(col));
929}
930
931/**
932 * Checks if given line width is already set in PDF gstate and if not, update
933 * the gstate accordingly.
934 * \param page PDF page where the update needs to happen.
935 * \param lineWidth Wanted line width.
936 */
937void pdfw_gs_linewidth(HPDF_Page page, float lineWidth)
938{
939 if (lineWidth == pdfw_gs[pdfw_gs_level].lineWidth)
940 return;
941 pdfw_gs[pdfw_gs_level].lineWidth = lineWidth;
942 HPDF_Page_SetLineWidth(page, lineWidth);
943}
944
945/**
946 * Checks if given font and font size is already set in PDF gstate and if not,
947 * update the gstate accordingly.
948 * \param page PDF page where the update needs to happen.
949 * \param font Wanted PDF font.
950 * \param font_size Wanted PDF font size.
951 */
952void pdfw_gs_font(HPDF_Page page, HPDF_Font font, HPDF_REAL font_size)
953{
954 if (font == pdfw_gs[pdfw_gs_level].font
955 && font_size == pdfw_gs[pdfw_gs_level].font_size)
956 return;
957 pdfw_gs[pdfw_gs_level].font = font;
958 pdfw_gs[pdfw_gs_level].font_size = font_size;
959 HPDF_Page_SetFontAndSize(page, font, font_size);
960}
961
962/**
963 * Checks if given dash pattern is already set in PDF gstate and if not,
964 * update the gstate accordingly.
965 * \param page PDF page where the update needs to happen.
966 * \param dash Wanted dash pattern.
967 */
968void pdfw_gs_dash(HPDF_Page page, DashPattern_e dash)
969{
970 if (dash == pdfw_gs[pdfw_gs_level].dash)
971 return;
972 pdfw_gs[pdfw_gs_level].dash = dash;
973 switch (dash) {
974 case DashPattern_eNone: {
975 HPDF_Page_SetDash(page, NULL, 0, 0);
976 break;
977 }
978 case DashPattern_eDash: {
979 const HPDF_UINT16 dash_ptn[] = {3};
980 HPDF_Page_SetDash(page, dash_ptn, 1, 1);
981 break;
982 }
983 case DashPattern_eDotted: {
984 const HPDF_UINT16 dash_ptn[] = {1};
985 HPDF_Page_SetDash(page, dash_ptn, 1, 1);
986 break;
987 }
988 }
989}
990
991#else
993{
995}
996#endif /* WITH_PDF_EXPORT */
static size_t bitmap_get_rowstride(void *vbitmap)
Find the width of a pixel row in bytes.
Definition: bitmap.cpp:195
Conception: Generalized output-in-pages.
@ MARGINLEFT
Definition: print.h:42
@ MARGINTOP
Definition: print.h:42
@ MARGINRIGHT
Definition: print.h:42
nserror save_pdf(const char *path)
Definition: save_pdf.c:992
PDF Plotting.
bool pdf_begin(struct print_settings *settings)
Start plotting a pdf file.
bool pdf_next_page(void)
Finish the current page and start a new one.
const struct printer pdf_printer
void pdf_end(void)
Close pdf document and save changes to file.
Error codes.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_SAVE_FAILED
Failed to save data.
Definition: errors.h:36
@ NSERROR_NOT_IMPLEMENTED
Functionality is not implemented.
Definition: errors.h:61
@ NSERROR_OK
No error.
Definition: errors.h:30
Font handling in Haru pdf documents (interface).
bool haru_nsfont_apply_style(const plot_font_style_t *fstyle, HPDF_Doc doc, HPDF_Page page, HPDF_Font *font, HPDF_REAL *font_size)
int bitmap_get_width(void *bitmap)
get width of a bitmap.
Definition: bitmap.c:319
int bitmap_get_height(void *bitmap)
get height of a bitmap.
Definition: bitmap.c:336
static unsigned char * bitmap_get_buffer(void *bitmap)
Return a pointer to the pixel data in a bitmap.
Definition: bitmap.c:194
static struct print_settings * settings
Definition: print.c:50
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
High-level resource cache interface.
Generic bitmap handling interface.
const uint8_t * content_get_source_data(struct hlcache_handle *h, size_t *size)
Retrieve source of content.
Definition: content.c:1209
content_type content_get_type(struct hlcache_handle *h)
Retrieve computed type of content.
Definition: content.c:1061
Target independent plotting interface.
#define BITMAPF_REPEAT_X
Definition: plotters.h:38
#define BITMAPF_REPEAT_Y
Definition: plotters.h:39
unsigned long bitmap_flags_t
Definition: plotters.h:36
@ PLOTTER_PATH_MOVE
Definition: plotters.h:42
@ PLOTTER_PATH_CLOSE
Definition: plotters.h:43
@ PLOTTER_PATH_LINE
Definition: plotters.h:44
@ PLOTTER_PATH_BEZIER
Definition: plotters.h:45
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
#define plot_style_int_to_fixed(v)
Definition: plot_style.h:51
@ PLOT_OP_TYPE_NONE
No operation.
Definition: plot_style.h:66
@ PLOT_OP_TYPE_DASH
Dashed plot.
Definition: plot_style.h:69
@ PLOT_OP_TYPE_DOT
Dotted plot.
Definition: plot_style.h:68
#define NS_TRANSPARENT
Transparent colour value.
Definition: plot_style.h:39
Printer interface.
int width
Definition: gui.c:160
int height
Definition: gui.c:161
Interface to utility string handling.
RISC OS wimp toolkit bitmap.
Definition: bitmap.c:68
Content which corresponds to a single URL.
void(* pdf_password)(char **owner_pass, char **user_pass, char *path)
Prompt the user for a password for a PDF.
Definition: misc.h:117
High-level cache handle.
Definition: hlcache.c:66
struct gui_misc_table * misc
Browser table.
Definition: gui_table.h:57
Font style for plotting.
Definition: plot_style.h:111
colour foreground
Colour of text.
Definition: plot_style.h:123
Plot style for stroke/fill plotters.
Definition: plot_style.h:76
colour fill_colour
Colour of fill.
Definition: plot_style.h:81
plot_style_fixed stroke_width
Width of stroke, in pixels.
Definition: plot_style.h:78
plot_operation_type_t fill_type
Fill plot type.
Definition: plot_style.h:80
colour stroke_colour
Colour of stroke.
Definition: plot_style.h:79
plot_operation_type_t stroke_type
Stroke plot type.
Definition: plot_style.h:77
Plotter operations table.
Definition: plotters.h:102
nserror(* rectangle)(const struct redraw_context *ctx, const plot_style_t *pstyle, const struct rect *rectangle)
Plots a rectangle.
Definition: plotters.h:188
Settings for a print - filled in by print_make_settings or 'manually' by the caller.
Definition: print.h:50
float page_width
Definition: print.h:52
float page_height
Definition: print.h:52
css_fixed margins[4]
Definition: print.h:53
const char * output
Definition: print.h:60
Printer interface.
Definition: printer.h:34
Rectangle coordinates.
Definition: types.h:40
uint32_t colour
Colour type: XBGR.
Definition: types.h:35
const char * user_agent_string(void)
Retrieve the core user agent for this release.
Definition: useragent.c:79
Option reading and saving interface.
Interface to a number of general purpose functionality.
#define min(x, y)
Definition: utils.h:46
#define max(x, y)
Definition: utils.h:50
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821
static nserror text(const struct redraw_context *ctx, const struct plot_font_style *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plot.c:978
static nserror clip(const struct redraw_context *ctx, const struct rect *clip)
Sets a clip rectangle for subsequent plot operations.
Definition: plot.c:357