From e063a2a59d698dd96123b68a44b09f0623f80cab Mon Sep 17 00:00:00 2001 From: John Tytgat Date: Thu, 14 Aug 2008 20:32:10 +0000 Subject: Second merge of Adam Blokus' GSoC work from his branch 'branches/adamblokus/netsurf'. Merged revisions 4195-4211,4216,4219-4220,4222-4234,4236-4250,4252-4262,4264-4266,4268-4326,4329-4335,4338-4342,4344-4411,4413-4420,4422-4436,4438-4491,4494-4506,4508-4514,4516,4518-4552,4554,4556-4564,4567-4568,4570-4574,4576-4686,4689-4692,4694,4698-4709,4715-4723,4725-4755,4757-4769,4771-4919,4921-4996,4998-5110,5112-5117 via svnmerge from svn://svn.netsurf-browser.org/branches/adamblokus/netsurf ........ r4736 | adamblokus | 2008-07-26 13:46:54 +0200 (Sat, 26 Jul 2008) | 2 lines Sorting out some problems with svn. ........ r4737 | adamblokus | 2008-07-26 13:54:36 +0200 (Sat, 26 Jul 2008) | 4 lines Added export tab to the options dialog. Added the possibility of changing some print options. ........ r4897 | adamblokus | 2008-08-04 17:59:05 +0200 (Mon, 04 Aug 2008) | 5 lines Added checking of horizontal clipping. Added better table loosening. Changed some minor bugs. Applied changes in the Export options tab according to the review from tlsa. ........ r4905 | adamblokus | 2008-08-05 01:53:34 +0200 (Tue, 05 Aug 2008) | 2 lines Fixed bug which made it impossible to export pdf's. ........ r4919 | adamblokus | 2008-08-05 16:39:33 +0200 (Tue, 05 Aug 2008) | 2 lines Fixed some memory leaks which caused Netsurf to break. ........ r4927 | adamblokus | 2008-08-06 02:26:30 +0200 (Wed, 06 Aug 2008) | 4 lines Fixed bug with filenames which crashed Netsurf. Turned anti aliasing off for printing. Fixed some scaling issues. ........ r4928 | adamblokus | 2008-08-06 17:52:44 +0200 (Wed, 06 Aug 2008) | 5 lines Added new export/print options: - suppressing images - turning off backgrounds - toggled loosening ........ r4950 | adamblokus | 2008-08-07 21:15:21 +0200 (Thu, 07 Aug 2008) | 5 lines Added new options to PDF export: - document compression - document encryption Added PDF password dialog ........ r4954 | adamblokus | 2008-08-07 22:11:31 +0200 (Thu, 07 Aug 2008) | 2 lines Added saving print settings. ........ r4956 | adamblokus | 2008-08-07 22:44:48 +0200 (Thu, 07 Aug 2008) | 2 lines Fixes to PDF encryption ........ r4970 | adamblokus | 2008-08-09 15:26:24 +0200 (Sat, 09 Aug 2008) | 3 lines Fixed bug in plotting tiled bitmaps. Fixed bug with too long text decorations. ........ r4977 | adamblokus | 2008-08-09 19:18:56 +0200 (Sat, 09 Aug 2008) | 2 lines Fixed JPG embedding bug. ........ r4988 | adamblokus | 2008-08-10 16:59:51 +0200 (Sun, 10 Aug 2008) | 3 lines Added clip checking to pdf plotters. No more "blank" clips. Made PDF compression a default setting. ........ r4995 | adamblokus | 2008-08-10 20:03:00 +0200 (Sun, 10 Aug 2008) | 2 lines Fixed Haru crash on font-size==0. ........ r4996 | adamblokus | 2008-08-10 21:04:43 +0200 (Sun, 10 Aug 2008) | 2 lines Added changing text mode only if necessary. ........ r5045 | adamblokus | 2008-08-11 21:26:26 +0200 (Mon, 11 Aug 2008) | 3 lines Removing gtk stuff from core code. Little fix in options. ........ r5048 | adamblokus | 2008-08-11 21:57:45 +0200 (Mon, 11 Aug 2008) | 2 lines Better font size checking in PDF export. ........ r5050 | adamblokus | 2008-08-11 22:19:56 +0200 (Mon, 11 Aug 2008) | 2 lines Fixed riscos text scale bug. ........ r5073 | adamblokus | 2008-08-12 17:40:57 +0200 (Tue, 12 Aug 2008) | 2 lines Added missing tooltips ........ r5092 | adamblokus | 2008-08-13 17:09:25 +0200 (Wed, 13 Aug 2008) | 2 lines Moved /pdf folder to desktop/save_pdf ........ r5110 | adamblokus | 2008-08-13 22:44:50 +0200 (Wed, 13 Aug 2008) | 2 lines Added comments. ........ r5113 | adamblokus | 2008-08-13 23:07:35 +0200 (Wed, 13 Aug 2008) | 2 lines Cosmetic changes ........ r5116 | adamblokus | 2008-08-14 16:10:18 +0200 (Thu, 14 Aug 2008) | 2 lines Fixed bug with BOX_INLINE_END in tree duplication. ........ r5117 | joty | 2008-08-14 21:47:46 +0200 (Thu, 14 Aug 2008) | 1 line Improvement for r5116: use local vars when possible; rename global last to box_duplicate_last; check on box_duplicate_main_tree failure. ........ svn path=/trunk/netsurf/; revision=5118 --- desktop/options.c | 30 ++ desktop/options.h | 19 + desktop/print.c | 141 +++++-- desktop/print.h | 23 +- desktop/save_pdf/TODO | 19 + desktop/save_pdf/font_haru.c | 372 ++++++++++++++++++ desktop/save_pdf/font_haru.h | 36 ++ desktop/save_pdf/pdf_plotters.c | 831 ++++++++++++++++++++++++++++++++++++++++ desktop/save_pdf/pdf_plotters.h | 44 +++ 9 files changed, 1468 insertions(+), 47 deletions(-) create mode 100644 desktop/save_pdf/TODO create mode 100644 desktop/save_pdf/font_haru.c create mode 100644 desktop/save_pdf/font_haru.h create mode 100644 desktop/save_pdf/pdf_plotters.c create mode 100644 desktop/save_pdf/pdf_plotters.h (limited to 'desktop') diff --git a/desktop/options.c b/desktop/options.c index c289cb0b6..197399aa1 100644 --- a/desktop/options.c +++ b/desktop/options.c @@ -139,6 +139,26 @@ unsigned int option_min_reflow_period = 100; /* time in cs */ #else unsigned int option_min_reflow_period = 25; /* time in cs */ #endif +/** top margin of exported page*/ +int option_margin_top = DEFAULT_MARGIN_TOP_MM; +/** bottom margin of exported page*/ +int option_margin_bottom = DEFAULT_MARGIN_BOTTOM_MM; +/** left margin of exported page*/ +int option_margin_left = DEFAULT_MARGIN_LEFT_MM; +/** right margin of exported page*/ +int option_margin_right = DEFAULT_MARGIN_RIGHT_MM; +/** scale of exported content*/ +int option_export_scale = DEFAULT_EXPORT_SCALE * 100; +/**suppressing images in printed content*/ +bool option_suppress_images = false; +/**turning off all backgrounds for printed content*/ +bool option_remove_backgrounds = false; +/**turning on content loosening for printed content*/ +bool option_enable_loosening = true; +/**compression of PDF documents*/ +bool option_enable_PDF_compression = true; +/**setting a password and encoding PDF documents*/ +bool option_enable_PDF_password = false; /* Fetcher configuration */ /** Maximum simultaneous active fetchers */ @@ -218,6 +238,16 @@ struct { { "suppress_curl_debug", OPTION_BOOL, &option_suppress_curl_debug }, { "target_blank", OPTION_BOOL, &option_target_blank }, + { "margin_top", OPTION_INTEGER, &option_margin_top}, + { "margin_bottom", OPTION_INTEGER, &option_margin_bottom}, + { "margin_left", OPTION_INTEGER, &option_margin_left}, + { "margin_right", OPTION_INTEGER, &option_margin_right}, + { "export_scale", OPTION_INTEGER, &option_export_scale}, + { "suppress_images", OPTION_BOOL, &option_suppress_images}, + { "remove_backgrounds", OPTION_BOOL, &option_remove_backgrounds}, + { "enable_loosening", OPTION_BOOL, &option_enable_loosening}, + { "enable_PDF_compression", OPTION_BOOL, &option_enable_PDF_compression}, + { "enable_PDF_password", OPTION_BOOL, &option_enable_PDF_password}, EXTRA_OPTION_TABLE }; diff --git a/desktop/options.h b/desktop/options.h index 89e99969b..cb238b93a 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -83,6 +83,25 @@ extern int option_scale; extern bool option_incremental_reflow; extern unsigned int option_min_reflow_period; +extern int option_margin_top; +extern int option_margin_bottom; +extern int option_margin_left; +extern int option_margin_right; +extern int option_export_scale; +extern bool option_suppress_images; +extern bool option_remove_backgrounds; +extern bool option_enable_loosening; +extern bool option_enable_PDF_compression; +extern bool option_enable_PDF_password; +#define DEFAULT_PAGE_WIDTH 595 +#define DEFAULT_PAGE_HEIGHT 840 +#define DEFAULT_MARGIN_TOP_MM 10 +#define DEFAULT_MARGIN_BOTTOM_MM 10 +#define DEFAULT_MARGIN_LEFT_MM 10 +#define DEFAULT_MARGIN_RIGHT_MM 10 +#define DEFAULT_EXPORT_SCALE 0.7 +#define DEFAULT_COPIES 1 + /* Fetcher configuration. */ extern int option_max_fetchers; extern int option_max_fetchers_per_host; diff --git a/desktop/print.c b/desktop/print.c index 931dbe087..433a0bd89 100644 --- a/desktop/print.c +++ b/desktop/print.c @@ -23,31 +23,34 @@ #include "utils/config.h" #ifdef WITH_PDF_EXPORT +#include + +#include "desktop/options.h" #include "desktop/print.h" #include "desktop/printer.h" +#include "desktop/save_pdf/font_haru.h" #include "content/content.h" +#include "gtk/options.h" + #include "utils/log.h" #include "utils/talloc.h" #include "render/loosen.h" #include "render/box.h" -#include "pdf/font_haru.h" - static struct content *print_init(struct content *, struct print_settings *); static bool print_apply_settings(struct content *, struct print_settings *); - -/*TODO: should these be passed as parameters in order to allow simultaneous - printings? -*/ static float page_content_width, page_content_height; -static float text_margin_height; static struct content *printed_content; static float done_height; +bool html_redraw_printing = false; +int html_redraw_printing_border = 0; +int html_redraw_printing_top_cropped = 0; + /** * This function calls print setup, prints page after page until the whole * content is printed calls cleaning up afterwise. @@ -61,9 +64,9 @@ bool print_basic_run(struct content *content, struct print_settings *settings) { bool ret = true; - + if (settings == NULL) - settings = print_make_settings(DEFAULT); + settings = print_make_settings(DEFAULT, NULL); if (!print_set_up(content, printer, settings, NULL)) ret = false; @@ -71,7 +74,7 @@ bool print_basic_run(struct content *content, while (ret && (done_height < printed_content->height) ) ret = print_draw_next_page(printer, settings); - print_cleanup(content, printer); + print_cleanup(content, printer, settings); return ret; } @@ -115,11 +118,17 @@ bool print_set_up(struct content *content, bool print_draw_next_page(const struct printer *printer, struct print_settings *settings) { - /*TODO:Plotter will have to be duplicated and passed - as an argument - to allow simultaneous screen and - page plotting*/ + int clip_x1, clip_y1; + plot = *(printer->plotter); - + html_redraw_printing_top_cropped = INT_MAX; + + clip_x1 = page_content_width * settings->scale; + clip_y1 = page_content_height * settings->scale; + + html_redraw_printing = true; + html_redraw_printing_border = clip_y1; + printer->print_next_page(); if( !content_redraw(printed_content, 0, @@ -127,11 +136,13 @@ bool print_draw_next_page(const struct printer *printer, 0,0, 0, 0, - page_content_width * settings->scale, - page_content_height * settings->scale, + clip_x1, + clip_y1, settings->scale, 0xffffff)) return false; - done_height += page_content_height - text_margin_height; + done_height += page_content_height - + (html_redraw_printing_top_cropped != INT_MAX ? + clip_y1 - html_redraw_printing_top_cropped : 0) / settings->scale; return true; } @@ -194,9 +205,7 @@ bool print_apply_settings(struct content *content, return false; /*Apply settings - adjust page size etc*/ - - text_margin_height = settings->margins[MARGINTEXT]; - + page_content_width = (settings->page_width - settings->margins[MARGINLEFT] - settings->margins[MARGINRIGHT]) / settings->scale; @@ -207,9 +216,10 @@ bool print_apply_settings(struct content *content, LOG(("New layout applied.New height = %d ; New width = %d ", content->height, content->width)); - if (content->width > page_content_width) - return loosen_document_layout(content, content->data.html.layout, - page_content_width, page_content_height); + /*check if loosening is necessary and requested*/ + if (option_enable_loosening && content->width > page_content_width) + return loosen_document_layout(content, content->data.html.layout, + page_content_width, page_content_height); return true; } @@ -221,10 +231,13 @@ bool print_apply_settings(struct content *content, * \return true if successful, false otherwise */ bool print_cleanup(struct content *content, - const struct printer *printer) + const struct printer *printer, + struct print_settings *settings) { printer->print_end(); + html_redraw_printing = false; + if (printed_content) { content_remove_user(printed_content, NULL, (intptr_t)print_init, 0); talloc_free(printed_content); @@ -232,50 +245,98 @@ bool print_cleanup(struct content *content, content_remove_user(content, NULL, (intptr_t)print_init, 0); + free((void *)settings->output); + free(settings); + return true; } /** * Generates one of the predefined print settings sets. + * \param configuration the requested configuration + * \param filename the filename or NULL * \return print_settings in case if successful, NULL if unknown configuration \ * or lack of memory. */ -struct print_settings *print_make_settings(print_configuration configuration) +struct print_settings *print_make_settings(print_configuration configuration, + const char *filename) { struct print_settings *settings; + char *path; + struct css_length length; + + path = malloc(PATH_MAX * sizeof(char)); + if (path == NULL) + return NULL; + + length.unit = CSS_UNIT_MM; switch (configuration){ case DEFAULT: settings = (struct print_settings*) - malloc(sizeof (struct print_settings) ); + malloc(sizeof(struct print_settings) ); if (settings == NULL) return NULL; - settings->page_width = 595; - settings->page_height = 840; - settings->copies = 1; - /*with 0.7 the pages look the best, the value in - haru_nsfont_apply_style should be kept the same as this - */ - settings->scale = 0.7; + settings->page_width = DEFAULT_PAGE_WIDTH; + settings->page_height = DEFAULT_PAGE_HEIGHT; + settings->copies = DEFAULT_COPIES; + + settings->scale = DEFAULT_EXPORT_SCALE; + + length.value = DEFAULT_MARGIN_LEFT_MM; + settings->margins[MARGINLEFT] = css_len2px(&length, 0); + length.value = DEFAULT_MARGIN_RIGHT_MM; + settings->margins[MARGINRIGHT] = css_len2px(&length, 0); + length.value = DEFAULT_MARGIN_TOP_MM; + settings->margins[MARGINTOP] = css_len2px(&length, 0); + length.value = DEFAULT_MARGIN_BOTTOM_MM; + settings->margins[MARGINBOTTOM] = css_len2px(&length, 0); - settings->margins[MARGINLEFT] = 30; - settings->margins[MARGINRIGHT] = 30; - settings->margins[MARGINTOP] = 30; - settings->margins[MARGINBOTTOM] = 30; + settings->font_func = &haru_nsfont; - settings->margins[MARGINTEXT] = 10; + break; + /*use settings from the Export options tab*/ + case OPTIONS: + settings = (struct print_settings*) + malloc(sizeof(struct print_settings) ); + + if (settings == NULL) + return NULL; + + settings->page_width = DEFAULT_PAGE_WIDTH; + settings->page_height = DEFAULT_PAGE_HEIGHT; + settings->copies = DEFAULT_COPIES; + + settings->scale = (float)option_export_scale / 100; + + length.value = option_margin_left; + settings->margins[MARGINLEFT] = css_len2px(&length, 0); + length.value = option_margin_right; + settings->margins[MARGINRIGHT] = css_len2px(&length, 0); + length.value = option_margin_top; + settings->margins[MARGINTOP] = css_len2px(&length, 0); + length.value = option_margin_bottom; + settings->margins[MARGINBOTTOM] = css_len2px(&length, 0); - settings->output = "out.pdf"; settings->font_func = &haru_nsfont; break; - default: return NULL; } + /*if no filename is specified use one without an extension*/ + if (filename == NULL) { + /*TODO: the "/" is not platform independent*/ + strcpy(path, "/out"); + } + else + strcpy(path, filename); + + settings->output = path; + return settings; } diff --git a/desktop/print.h b/desktop/print.h index dc4cde642..98f382eca 100644 --- a/desktop/print.h +++ b/desktop/print.h @@ -37,11 +37,10 @@ struct content; struct printer; -enum { MARGINLEFT = 0, MARGINRIGHT = 1, MARGINTOP = 2, MARGINBOTTOM = 3, - MARGINTEXT = 4}; +enum { MARGINLEFT = 0, MARGINRIGHT = 1, MARGINTOP = 2, MARGINBOTTOM = 3}; /** Predefined printing configuration names*/ -typedef enum {DEFAULT} print_configuration; +typedef enum {DEFAULT, OPTIONS} print_configuration; /** Settings for a print - filled in by print_make_settings or * 'manually' by the caller @@ -49,7 +48,7 @@ typedef enum {DEFAULT} print_configuration; struct print_settings{ /*Standard parameters*/ float page_width, page_height; - float margins[5]; + int margins[4]; float scale; @@ -58,18 +57,28 @@ struct print_settings{ /*Output destinations - file/printer name*/ const char *output; - /*TODO: more options?*/ + /*the functions used to measure fonts*/ const struct font_functions *font_func; }; + bool print_basic_run(struct content *, const struct printer *, struct print_settings *); bool print_set_up(struct content *content, const struct printer *printer, struct print_settings *settings, double *height); bool print_draw_next_page(const struct printer *printer, struct print_settings *settings); -bool print_cleanup(struct content *, const struct printer *); +bool print_cleanup(struct content *, const struct printer *, + struct print_settings *settings); + +struct print_settings *print_make_settings(print_configuration configuration, + const char *url); -struct print_settings *print_make_settings(print_configuration configuration); +/*is the content currently redrawn fo printing?*/ +extern bool html_redraw_printing; +/*if something is partially under this Y coordinate it won't be drawn...*/ +extern int html_redraw_printing_border; +/*...and the highest of the tops of all cropped elements will be remembered*/ +extern int html_redraw_printing_top_cropped; #endif diff --git a/desktop/save_pdf/TODO b/desktop/save_pdf/TODO new file mode 100644 index 000000000..1b3a896b9 --- /dev/null +++ b/desktop/save_pdf/TODO @@ -0,0 +1,19 @@ +- finish all graphic primitives +- allow adding raw bitmaps +- make image-aware (embed the image in its native/original type if possible) + +- adjust content width to page width +- divide output into multiple pages (not just the first one) +- rearrange file structure + +- separate print-plotting as much as possible from window redrawing +- add text-scaling (if not yet using the original font - make the default one + have the same width) +- add a save file.. dialogue +- add utf support to Haru ( doable? ) +- wait for browser to end fetching? +- analyze and deal with performance issues(huge file hangs some pdf viewers, + for example kpdf when viewing plotted http://www.onet.pl) +- deal with to wide pages - when window layouting adds a horizontal scrollbar, + we should treat it otherwise - either print horizontal or scale or, + better, find a new layout. \ No newline at end of file diff --git a/desktop/save_pdf/font_haru.c b/desktop/save_pdf/font_haru.c new file mode 100644 index 000000000..2e5f90adb --- /dev/null +++ b/desktop/save_pdf/font_haru.c @@ -0,0 +1,372 @@ +/* + * Copyright 2008 Adam Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * Font handling in Haru pdf documents (implementation). + * + * The functions were written to implement the same interface as the Pango ones + * so that the usage of the latter wouldn't have to be modified. + */ + +#include "utils/config.h" +#ifdef WITH_PDF_EXPORT + +/*#define FONT_HARU_DEBUG */ + +#include +#include +#include +#include +#include "hpdf.h" +#include "css/css.h" +#include "desktop/save_pdf/font_haru.h" +#include "render/font.h" +#include "utils/log.h" + + +static bool haru_nsfont_init(HPDF_Doc *pdf, HPDF_Page *page, + const char *string, char **string_nt, int length); + +static bool haru_nsfont_width(const struct css_style *style, + const char *string, size_t length, + int *width); + +static bool haru_nsfont_position_in_string(const struct css_style *style, + const char *string, size_t length, + int x, size_t *char_offset, int *actual_x); + +static bool haru_nsfont_split(const struct css_style *style, + const char *string, size_t length, + int x, size_t *char_offset, int *actual_x); + + +const struct font_functions haru_nsfont = { + haru_nsfont_width, + haru_nsfont_position_in_string, + haru_nsfont_split +}; + +/** + * Haru error handler + * for debugging purposes - it immediately exits the program on the first error, + * as it would otherwise flood the user with all resulting complications, + * covering the most important error source. + */ +static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, + void *user_data) +{ + LOG(("ERROR: in font_haru \n\terror_no=%x\n\tdetail_no=%d\n", + (HPDF_UINT)error_no, (HPDF_UINT)detail_no)); +#ifdef FONT_HARU_DEBUG + exit(1); +#endif +} + +static bool haru_nsfont_init(HPDF_Doc *pdf, HPDF_Page *page, + const char *string, char **string_nt, int length) +{ + + *pdf = HPDF_New(error_handler, NULL); + + if (*pdf == NULL) + return false; + + *page = HPDF_AddPage(*pdf); + + if (*page == NULL) { + HPDF_Free(*pdf); + return false; + } + + *string_nt = malloc((length + 1) * sizeof(char)); + if (*string_nt == NULL) { + HPDF_Free(*pdf); + return false; + } + + memcpy(*string_nt, string, length); + (*string_nt)[length] = '\0'; + return true; +} + +/** + * Measure the width of a string. + * + * \param style css_style for this text, with style->font_size.size == + * CSS_FONT_SIZE_LENGTH + * \param string string to measure (no UTF-8 currently) + * \param length length of string + * \param width updated to width of string[0..length] + * \return true on success, false on error and error reported + */ +bool haru_nsfont_width(const struct css_style *style, + const char *string, size_t length, + int *width) +{ + HPDF_Doc pdf; + HPDF_Page page; + char *string_nt; + HPDF_REAL width_real; + + *width = 0; + + if (length == 0) { + return true; + } + + if (!haru_nsfont_init(&pdf, &page, string, &string_nt, length)) + return false; + + if (!haru_nsfont_apply_style(style, pdf, page, NULL)) { + free(string_nt); + HPDF_Free(pdf); + return false; + } + + width_real = HPDF_Page_TextWidth(page, string_nt); + *width = width_real; + +#ifdef FONT_HARU_DEBUG + LOG(("Measuring string: %s ; Calculated width: %f %i",string_nt, width_real, *width)); +#endif + free(string_nt); + HPDF_Free(pdf); + + + return true; +} + + +/** + * Find the position in a string where an x coordinate falls. + * + * \param style css_style for this text, with style->font_size.size == + * CSS_FONT_SIZE_LENGTH + * \param string string to measure (no UTF-8 currently) + * \param length length of string + * \param x x coordinate to search for + * \param char_offset updated to offset in string of actual_x, [0..length] + * \param actual_x updated to x coordinate of character closest to x + * \return true on success, false on error and error reported + */ + +bool haru_nsfont_position_in_string(const struct css_style *style, + const char *string, size_t length, + int x, size_t *char_offset, int *actual_x) +{ + HPDF_Doc pdf; + HPDF_Page page; + char *string_nt; + HPDF_UINT offset; + HPDF_REAL real_width; + + if (!haru_nsfont_init(&pdf, &page, string, &string_nt, length)) + return false; + + if (HPDF_Page_SetWidth(page, x) != HPDF_OK + || !haru_nsfont_apply_style(style, pdf, page, NULL)) { + free(string_nt); + HPDF_Free(pdf); + return false; + } + + + offset = HPDF_Page_MeasureText(page, string_nt, x, + HPDF_FALSE, &real_width); + + + if (real_width < x) + *char_offset = offset; + else { + assert(fabs(real_width - x) < FLT_EPSILON); + assert(offset > 0); + *char_offset = offset - 1; + } + + /*TODO: this is only the right edge of the character*/ + *actual_x = real_width; + +#ifdef FONT_HARU_DEBUG + LOG(("Position in string: %s at x: %i; Calculated position: %i", + string_nt, x, *char_offset)); +#endif + free(string_nt); + HPDF_Free(pdf); + + return true; +} + +/** + * Find where to split a string to make it fit a width. + * + * \param style css_style for this text, with style->font_size.size == + * CSS_FONT_SIZE_LENGTH + * \param string string to measure (no UTF-8 currently) + * \param length length of string + * \param x width available + * \param char_offset updated to offset in string of actual_x, [0..length] + * \param actual_x updated to x coordinate of character closest to x + * \return true on success, false on error and error reported + */ + +bool haru_nsfont_split(const struct css_style *style, + const char *string, size_t length, + int x, size_t *char_offset, int *actual_x) +{ + HPDF_Doc pdf; + HPDF_Page page; + char *string_nt; + HPDF_REAL real_width; + HPDF_UINT offset; + + + if (!haru_nsfont_init(&pdf, &page, string, &string_nt, length)) + return false; + + if (HPDF_Page_SetWidth(page, x) != HPDF_OK + || !haru_nsfont_apply_style(style, pdf, page, NULL)) { + free(string_nt); + HPDF_Free(pdf); + return false; + } + + offset = HPDF_Page_MeasureText(page, string_nt, x, + HPDF_TRUE, &real_width); + +#ifdef FONT_HARU_DEBUG + LOG(("Splitting string: %s for width: %i ; Calculated position: %i Calculated real_width: %f", + string_nt, x, *char_offset, real_width)); +#endif + *char_offset = offset - 1; + + /*TODO: this is only the right edge of the character*/ + *actual_x = real_width; + + free(string_nt); + HPDF_Free(pdf); + + return true; +} + +/** + * Apply css_style to a Haru HPDF_Page + * + * \param style css_style for this page, with style->font_size.size == + * CSS_FONT_SIZE_LENGTH + * \param doc document owning the page + * \param page the page to apply the style to + * \param font if this is not NULL it is updated to the font from the + * style and nothing with the page is done + * \return true on success, false on error and error reported + */ +bool haru_nsfont_apply_style(const struct css_style *style, + HPDF_Doc doc, HPDF_Page page, HPDF_Font *font) +{ + HPDF_Font pdf_font; + HPDF_REAL size; + char font_name[50]; + bool roman; + bool bold; + bool styled; + + roman = false; + bold = false; + styled = false; + + + /*TODO: style handling, we are mapping the + styles on the basic 14 fonts only + */ + switch (style->font_family) { + case CSS_FONT_FAMILY_SERIF: + strcpy(font_name, "Times"); + roman = true; + break; + case CSS_FONT_FAMILY_MONOSPACE: + strcpy(font_name, "Courier"); + break; + case CSS_FONT_FAMILY_SANS_SERIF: + strcpy(font_name, "Helvetica"); + break; + case CSS_FONT_FAMILY_CURSIVE: + case CSS_FONT_FAMILY_FANTASY: + default: + strcpy(font_name, "Times"); + roman=true; + break; + } + + if (style->font_weight == CSS_FONT_WEIGHT_BOLD){ + strcat(font_name, "-Bold"); + bold = true; + } + + switch (style->font_style) { + case CSS_FONT_STYLE_ITALIC: + case CSS_FONT_STYLE_OBLIQUE: + if (!bold) + strcat(font_name,"-"); + if (roman) + strcat(font_name,"Italic"); + else + strcat(font_name,"Oblique"); + + styled = true; + break; + default: + break; + } + + if (roman && !styled && !bold) + strcat(font_name, "-Roman"); + +#ifdef FONT_HARU_DEBUG + LOG(("Setting font: %s", font_name)); +#endif + + /*the functions was invoked only to get the proper font*/ + if (font != NULL) { + pdf_font = HPDF_GetFont(doc, font_name, "StandardEncoding"); + if (pdf_font == NULL) + return false; + *font = pdf_font; + } + /*the function was invoked to set the page parameters*/ + else { + if (style->font_size.value.length.unit == CSS_UNIT_PX) + size = style->font_size.value.length.value; + else + size = css_len2pt(&style->font_size.value.length, style); + + if (size <= 0) + return false; + + if (size > HPDF_MAX_FONTSIZE) + size = HPDF_MAX_FONTSIZE; + + pdf_font = HPDF_GetFont(doc, font_name, "StandardEncoding"); + if (pdf_font == NULL) + return false; + HPDF_Page_SetFontAndSize(page, pdf_font, size); + } + + return true; +} + +#endif /* WITH_PDF_EXPORT */ + diff --git a/desktop/save_pdf/font_haru.h b/desktop/save_pdf/font_haru.h new file mode 100644 index 000000000..eca9855e6 --- /dev/null +++ b/desktop/save_pdf/font_haru.h @@ -0,0 +1,36 @@ +/* + * Copyright 2008 Adam Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * Font handling in Haru pdf documents (interface). + */ + +#ifndef _NETSURF_RENDER_FONT_HARU_H_ +#define _NETSURF_RENDER_FONT_HARU_H_ + +#include "render/font.h" +#include "hpdf.h" + +bool haru_nsfont_apply_style(const struct css_style *style, + HPDF_Doc doc, HPDF_Page page, + HPDF_Font *font); + +extern const struct font_functions haru_nsfont; +extern float pdf_scale; + +#endif diff --git a/desktop/save_pdf/pdf_plotters.c b/desktop/save_pdf/pdf_plotters.c new file mode 100644 index 000000000..10ddc10bf --- /dev/null +++ b/desktop/save_pdf/pdf_plotters.c @@ -0,0 +1,831 @@ +/* + * Copyright 2008 Adam Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * Target independent PDF plotting using Haru Free PDF Library. + */ + +#include "utils/config.h" +#ifdef WITH_PDF_EXPORT + +#include +#include +#include "hpdf.h" + +#include "desktop/options.h" +#include "desktop/plotters.h" +#include "desktop/print.h" +#include "desktop/printer.h" +#include "desktop/save_pdf/pdf_plotters.h" +#include "utils/log.h" +#include "utils/utils.h" +#include "image/bitmap.h" + +#include "font_haru.h" + +#define R(x) (( (x) & 0x0000ff )/256.0) +#define G(x) ((( (x) & 0x00ff00)>>8 )/256.0) +#define B(x) ((( (x) & 0xff0000)>>16)/256.0) + +/*#define PDF_DEBUG*/ + +static bool pdf_plot_clg(colour c); +static bool pdf_plot_rectangle(int x0, int y0, int width, int height, + int line_width, colour c, bool dotted, bool dashed); +static bool pdf_plot_line(int x0, int y0, int x1, int y1, int width, + colour c, bool dotted, bool dashed); +static bool pdf_plot_polygon(int *p, unsigned int n, colour fill); +static bool pdf_plot_fill(int x0, int y0, int x1, int y1, colour c); +static bool pdf_plot_clip(int clip_x0, int clip_y0, + int clip_x1, int clip_y1); +static bool pdf_plot_text(int x, int y, const struct css_style *style, + const char *text, size_t length, colour bg, colour c); +static bool pdf_plot_disc(int x, int y, int radius, colour c, bool filled); +static bool pdf_plot_arc(int x, int y, int radius, int angle1, int angle2, + colour c); +static bool pdf_plot_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, struct content *content); +static bool pdf_plot_bitmap_tile(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bool repeat_x, bool repeat_y, struct content *content); +static bool pdf_plot_path(float *p, unsigned int n, colour fill, float width, + colour c, float *transform); + +static void pdf_set_solid(void); +static void pdf_set_dashed(void); +static void pdf_set_dotted(void); + +static HPDF_Image pdf_extract_image(struct bitmap *bitmap, struct content *content); +static void apply_clip_and_mode(void); + + +static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, + void *user_data); + +#ifdef PDF_DEBUG +static void pdf_plot_grid(int x_dist,int y_dist,unsigned int colour); +#endif + +/*PDF Plotter - current doc,page and font*/ +static HPDF_Doc pdf_doc; +static HPDF_Page pdf_page; +static HPDF_Font pdf_font; + +/*PDF Page size*/ +static HPDF_REAL page_height, page_width; + +/*Remeber if pdf_plot_clip was invoked for current page*/ +static bool page_clipped; +int last_clip_x0, last_clip_y0, last_clip_x1, last_clip_y1; + +bool in_text_mode, text_mode_request; + +static struct print_settings* settings; + +static const struct plotter_table pdf_plotters = { + pdf_plot_clg, + pdf_plot_rectangle, + pdf_plot_line, + pdf_plot_polygon, + pdf_plot_fill, + pdf_plot_clip, + pdf_plot_text, + pdf_plot_disc, + pdf_plot_arc, + pdf_plot_bitmap, + pdf_plot_bitmap_tile, + NULL, + NULL, + NULL, + pdf_plot_path, + false +}; + +struct printer pdf_printer= { + &pdf_plotters, + pdf_begin, + pdf_next_page, + pdf_end +}; + +float pdf_scale; +static char *owner_pass; +static char *user_pass; + +bool pdf_plot_clg(colour c) +{ + return true; +} + +bool pdf_plot_rectangle(int x0, int y0, int width, int height, + int line_width, colour c, bool dotted, bool dashed) +{ +#ifdef PDF_DEBUG + LOG((".")); +#endif + apply_clip_and_mode(); + + HPDF_Page_SetLineWidth(pdf_page, line_width); + + if (dotted) + pdf_set_dotted(); + else if (dashed) + pdf_set_dashed(); + + HPDF_Page_SetRGBStroke(pdf_page, R(c), G(c), B(c)); + HPDF_Page_Rectangle(pdf_page, x0, page_height - y0 + height, width, height); + HPDF_Page_Stroke(pdf_page); + + if (dotted||dashed) + pdf_set_solid(); + + return true; +} + +bool pdf_plot_line(int x0, int y0, int x1, int y1, int width, + colour c, bool dotted, bool dashed) +{ +#ifdef PDF_DEBUG + LOG((".")); +#endif + + apply_clip_and_mode(); + + HPDF_Page_SetLineWidth(pdf_page, width); + + if (dotted) + pdf_set_dotted(); + else if (dashed) + pdf_set_dashed(); + + HPDF_Page_SetRGBStroke(pdf_page, R(c), G(c), B(c)); + HPDF_Page_SetLineWidth(pdf_page, width); + HPDF_Page_MoveTo(pdf_page, x0, page_height - y0); + HPDF_Page_LineTo(pdf_page, x1, page_height - y1); + HPDF_Page_Stroke(pdf_page); + + if (dotted||dashed) + pdf_set_solid(); + + return true; +} + +bool pdf_plot_polygon(int *p, unsigned int n, colour fill) +{ + int i; +#ifdef PDF_DEBUG + int pmaxx = p[0], pmaxy = p[1]; + int pminx = p[0], pminy = p[1]; + LOG((".")); +#endif + if (n == 0) + return true; + + apply_clip_and_mode(); + + HPDF_Page_SetRGBFill(pdf_page, R(fill), G(fill), B(fill)); + HPDF_Page_MoveTo(pdf_page, p[0], page_height - p[1]); + + for (i = 1 ; ifont_size.value.length.unit == CSS_UNIT_PX) + size = style->font_size.value.length.value; + else + size = css_len2pt(&style->font_size.value.length, style); + + /*this can be removed when export options get added for riscos*/ +#ifdef riscos + size *= DEFAULT_EXPORT_SCALE; +#else + size *= pdf_scale; +#endif + + if (size <= 0) + return true; + + if (size > HPDF_MAX_FONTSIZE) + size = HPDF_MAX_FONTSIZE; + + haru_nsfont_apply_style(style, pdf_doc, pdf_page, &pdf_font); + + descent = size * (HPDF_Font_GetDescent(pdf_font) / 1000.0); + text_bottom_position = page_height - y + descent; + + word = (char*) malloc( sizeof(char) * (length+1) ); + if (word == NULL) + return false; + + memcpy(word, text, length); + word[length] = '\0'; + + HPDF_Page_SetRGBFill(pdf_page, R(c), G(c), B(c)); + + HPDF_Page_SetFontAndSize (pdf_page, pdf_font, size); + HPDF_Page_TextOut (pdf_page, x, page_height - y, word); + + free(word); + + return true; +} + +bool pdf_plot_disc(int x, int y, int radius, colour c, bool filled) +{ +#ifdef PDF_DEBUG + LOG((".")); +#endif + apply_clip_and_mode(); + + if (filled) + HPDF_Page_SetRGBFill(pdf_page, R(c), G(c), B(c)); + else + HPDF_Page_SetRGBStroke(pdf_page, R(c), G(c), B(c)); + + HPDF_Page_Circle(pdf_page, x, page_height-y, radius); + + if (filled) + HPDF_Page_Fill(pdf_page); + else + HPDF_Page_Stroke(pdf_page); + + return true; +} + +bool pdf_plot_arc(int x, int y, int radius, int angle1, int angle2, colour c) +{ +#ifdef PDF_DEBUG + LOG(("%d %d %d %d %d %X", x, y, radius, angle1, angle2, c)); +#endif + + /*Normalize angles*/ + angle1 %= 360; + angle2 %= 360; + if (angle1 > angle2) + angle1 -= 360; + + apply_clip_and_mode(); + + HPDF_Page_SetRGBStroke(pdf_page, R(c), G(c), B(c)); + + HPDF_Page_Arc(pdf_page, x, page_height-y, radius, angle1, angle2); + + HPDF_Page_Stroke(pdf_page); + return true; +} + +bool pdf_plot_bitmap(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, struct content *content) +{ + HPDF_Image image; + +#ifdef PDF_DEBUG + LOG(("%d %d %d %d %X %X %X", x, y, width, height, + bitmap, bg, content)); +#endif + if (width == 0 || height == 0) + return true; + + apply_clip_and_mode(); + + image = pdf_extract_image(bitmap, content); + + if (!image) + return false; + + HPDF_Page_DrawImage(pdf_page, image, + x, page_height-y-height, + width, height); + return true; + + +} + +bool pdf_plot_bitmap_tile(int x, int y, int width, int height, + struct bitmap *bitmap, colour bg, + bool repeat_x, bool repeat_y, struct content *content) +{ + HPDF_Image image; + +#ifdef PDF_DEBUG + LOG(("%d %d %d %d %X %X %X", x, y, width, height, + bitmap, bg, content)); +#endif + if (width == 0 || height == 0) + return true; + + apply_clip_and_mode(); + + image = pdf_extract_image(bitmap, content); + + if (image) { + /*The position of the next tile*/ + HPDF_REAL current_x, current_y ; + HPDF_REAL max_width, max_height; + + max_width = (repeat_x ? page_width : width); + max_height = (repeat_y ? page_height: height); + + + for (current_y=0; current_y < max_height; current_y += height) + for (current_x=0; current_x < max_width; current_x += width) + HPDF_Page_DrawImage(pdf_page, image, + current_x + x, + page_height-current_y - y - height, + width, height); + + return true; + } + else + return false; + + return true; +} + +HPDF_Image pdf_extract_image(struct bitmap *bitmap, struct content *content) +{ + HPDF_Image image = NULL,smask; + char *img_buffer, *rgb_buffer, *alpha_buffer; + int img_width, img_height, img_rowstride; + int i, j; + + if (content) { + /*Not sure if I don't have to check if downloading has been + finished. + Other way - lock pdf plotting while fetching a website + */ + switch(content->type){ + /*Handle "embeddable" types of images*/ + case CONTENT_JPEG: + image = HPDF_LoadJpegImageFromMem(pdf_doc, + content->source_data, + content->source_size); + break; + + /*Disabled until HARU PNG support will be more stable. + + case CONTENT_PNG: + image = HPDF_LoadPngImageFromMem(pdf_doc, + content->source_data, + content->total_size); + break;*/ + default: + break; + } + } + + if (!image) { + /*Handle pixmaps*/ + img_buffer = bitmap_get_buffer(bitmap); + img_width = bitmap_get_width(bitmap); + img_height = bitmap_get_height(bitmap); + img_rowstride = bitmap_get_rowstride(bitmap); + + rgb_buffer = (char*)malloc(3 * img_width * img_height); + if (rgb_buffer == NULL) { + LOG(("Not enough memory to create RGB buffer")); + return NULL; + } + + alpha_buffer = (char*)malloc(img_width * img_height); + if (alpha_buffer == NULL) { + LOG(("Not enough memory to create alpha buffer")); + free(rgb_buffer); + return NULL; + } + + + for (i = 0; ipage_width - settings->margins[MARGINLEFT] - + settings->margins[MARGINRIGHT]; + + page_height = settings->page_height - settings->margins[MARGINTOP]; + + + if (option_enable_PDF_compression) + HPDF_SetCompressionMode(pdf_doc, HPDF_COMP_ALL); /*Compression on*/ + + pdf_font = HPDF_GetFont (pdf_doc, "Times-Roman", "StandardEncoding"); + + pdf_page = NULL; + +#ifdef PDF_DEBUG + LOG(("pdf_begin finishes")); +#endif + return true; +} + + +bool pdf_next_page(void) +{ +#ifdef PDF_DEBUG + if (pdf_page != NULL) { + HPDF_Page_GRestore(pdf_page); + if (page_clipped) + HPDF_Page_GRestore(pdf_page); + pdf_plot_grid(10, 10, 0xCCCCCC); + pdf_plot_grid(100, 100, 0xCCCCFF); + } +#endif + pdf_page = HPDF_AddPage(pdf_doc); + if (pdf_page == NULL) + return false; + + HPDF_Page_SetWidth (pdf_page, settings->page_width); + HPDF_Page_SetHeight(pdf_page, settings->page_height); + + HPDF_Page_Concat(pdf_page,1,0,0,1,settings->margins[MARGINLEFT],0); + + page_clipped = false; + HPDF_Page_GSave(pdf_page); + + text_mode_request = false; + in_text_mode = false; + +#ifdef PDF_DEBUG + LOG(("%f %f", page_width, page_height)); +#endif + + return true; +} + + +void pdf_end(void) +{ + char *path; +#ifdef PDF_DEBUG + LOG(("pdf_end begins")); + if (pdf_page != NULL) { + HPDF_Page_GRestore(pdf_page); + if (page_clipped) + HPDF_Page_GRestore(pdf_page); + pdf_plot_grid(10, 10, 0xCCCCCC); + pdf_plot_grid(100, 100, 0xCCCCFF); + } +#endif + + if (settings->output != NULL) + path = strdup(settings->output); + else + path = NULL; + + /*Encryption on*/ + if (option_enable_PDF_password) + PDF_Password(&owner_pass, &user_pass, path); + else + save_pdf(path); +#ifdef PDF_DEBUG + LOG(("pdf_end finishes")); +#endif +} +/** saves the pdf optionally encrypting it before*/ +void save_pdf(char *path) +{ + bool success = false; + + if (option_enable_PDF_password && owner_pass != NULL ) { + HPDF_SetPassword(pdf_doc, owner_pass, user_pass); + HPDF_SetEncryptionMode(pdf_doc, HPDF_ENCRYPT_R3, 16); + free(owner_pass); + free(user_pass); + } + + if (path != NULL) { + if (HPDF_SaveToFile(pdf_doc, path) != HPDF_OK) + remove(path); + else + success = true; + + free(path); + } + + if (!success) + warn_user("Unable to save PDF file.", 0); + + HPDF_Free(pdf_doc); +} + + +/** + * Haru error handler + * for debugging purposes - it immediately exits the program on the first error, + * as it would otherwise flood the user with all resulting complications, + * covering the most important error source. +*/ +static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, + void *user_data) +{ + LOG(("ERROR:\n\terror_no=%x\n\tdetail_no=%d\n", (HPDF_UINT)error_no, + (HPDF_UINT)detail_no)); +#ifdef PDF_DEBUG + exit(1); +#endif +} + +/** + * This function plots a grid - used for debug purposes to check if all + * elements' final coordinates are correct. +*/ +#ifdef PDF_DEBUG +void pdf_plot_grid(int x_dist, int y_dist, unsigned int colour) +{ + int i; + + for (int i = x_dist ; i < page_width ; i += x_dist) + pdf_plot_line(i, 0, i, page_height, 1, colour, false, false); + + for (int i = y_dist ; i < page_height ; i += x_dist) + pdf_plot_line(0, i, page_width, i, 1, colour, false, false); + +} +#endif + +/**used to synch the text scale with the scale for the whole content*/ +void pdf_set_scale(float s) +{ + pdf_scale = s; +} + +#endif /* WITH_PDF_EXPORT */ + diff --git a/desktop/save_pdf/pdf_plotters.h b/desktop/save_pdf/pdf_plotters.h new file mode 100644 index 000000000..ba815c552 --- /dev/null +++ b/desktop/save_pdf/pdf_plotters.h @@ -0,0 +1,44 @@ +/* + * Copyright 2008 Adam Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + PDF Plotting +*/ + +#ifndef NETSURF_PDF_PLOTTERS_H +#define NETSURF_PDF_PLOTTERS_H + +#include "desktop/print.h" +struct plotter_table; + +extern struct printer pdf_printer; + +/**Start plotting a pdf file*/ +bool pdf_begin(struct print_settings* settings); + +/**Finish the current page and start a new one*/ +bool pdf_next_page(void); + +/**Close pdf document and save changes to file*/ +void pdf_end(void); + +void pdf_set_scale(float s); + +void save_pdf(char *path); + +#endif /*NETSURF_PDF_PLOTTERS_H*/ -- cgit v1.2.3