summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Docs/Doxyfile2
-rw-r--r--Makefile17
-rw-r--r--Makefile.sources15
-rw-r--r--beos/beos_font.cpp15
-rw-r--r--desktop/browser.c11
-rw-r--r--desktop/gui.h1
-rw-r--r--desktop/history_core.c4
-rw-r--r--desktop/knockout.c22
-rw-r--r--desktop/plotters.h6
-rw-r--r--desktop/print.c280
-rw-r--r--desktop/print.h75
-rw-r--r--desktop/printer.h41
-rw-r--r--desktop/selection.c2
-rw-r--r--desktop/textinput.c20
-rw-r--r--gtk/font_pango.c36
-rw-r--r--gtk/font_pango.h9
-rw-r--r--gtk/gtk_bitmap.c8
-rw-r--r--gtk/gtk_plotters.c10
-rw-r--r--gtk/gtk_plotters.h1
-rw-r--r--gtk/gtk_print.c573
-rw-r--r--gtk/gtk_print.h48
-rw-r--r--gtk/gtk_scaffolding.c91
-rw-r--r--gtk/res/netsurf.glade19
-rw-r--r--image/bmp.c4
-rw-r--r--image/gif.c4
-rw-r--r--image/ico.c4
-rw-r--r--image/jpeg.c4
-rw-r--r--image/mng.c4
-rw-r--r--image/nssprite.c2
-rw-r--r--image/rsvg.c2
-rw-r--r--pdf/TODO19
-rw-r--r--pdf/font_haru.c373
-rw-r--r--pdf/font_haru.h36
-rw-r--r--pdf/pdf_plotters.c774
-rw-r--r--pdf/pdf_plotters.h40
-rw-r--r--pdf/pdf_printer.h36
-rw-r--r--render/box.c203
-rw-r--r--render/box.h2
-rw-r--r--render/font.h23
-rw-r--r--render/html.c1
-rw-r--r--render/html.h1
-rw-r--r--render/html_redraw.c8
-rw-r--r--render/layout.c92
-rw-r--r--render/loosen.c415
-rw-r--r--render/loosen.h35
-rw-r--r--render/textplain.c10
-rw-r--r--riscos/font.c26
-rw-r--r--riscos/menus.c20
-rw-r--r--riscos/menus.h1
-rw-r--r--riscos/plotters.c8
-rw-r--r--riscos/save.c25
-rw-r--r--riscos/save_draw.c8
-rw-r--r--riscos/save_pdf.c55
-rw-r--r--riscos/save_pdf.h30
-rw-r--r--testres/jpeg.jpegbin0 -> 6170 bytes
-rw-r--r--testres/png.pngbin0 -> 4922 bytes
-rw-r--r--testres/text.html68
-rw-r--r--testres/text2.html29
-rw-r--r--utils/config.h15
59 files changed, 3527 insertions, 156 deletions
diff --git a/Docs/Doxyfile b/Docs/Doxyfile
index 810686b0c..e471703e6 100644
--- a/Docs/Doxyfile
+++ b/Docs/Doxyfile
@@ -894,7 +894,7 @@ INCLUDE_FILE_PATTERNS =
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
-PREDEFINED = riscos CSS_INTERNALS WITH_ARTWORKS WITH_AUTH WITH_BMP WITH_DRAW WITH_DRAW_EXPORT WITH_GIF WITH_JPEG WITH_MMAP WITH_MNG WITH_NSSPRITE WITH_NS_SVG WITH_PLUGIN WITH_PRINT WITH_RSVG WITH_SAVE_COMPLETE WITH_SEARCH WITH_SPRITE WITH_SSL WITH_THEME_INSTALL WITH_URI WITH_URL
+PREDEFINED = riscos CSS_INTERNALS WITH_ARTWORKS WITH_AUTH WITH_BMP WITH_DRAW WITH_DRAW_EXPORT WITH_GIF WITH_JPEG WITH_MMAP WITH_MNG WITH_NSSPRITE WITH_NS_SVG WITH_PLUGIN WITH_PRINT WITH_RSVG WITH_SAVE_COMPLETE WITH_SEARCH WITH_SPRITE WITH_SSL WITH_THEME_INSTALL WITH_URI WITH_URL WITH_PDF_EXPORT
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
diff --git a/Makefile b/Makefile
index 175db59ea..630c4b769 100644
--- a/Makefile
+++ b/Makefile
@@ -114,6 +114,10 @@ SUBTARGET := -aof
EXEEXT := ,ff8
endif
PKG_CONFIG := $(GCCSDK_INSTALL_ENV)/ro-pkg-config
+CCACHE := $(shell which ccache)
+ifneq ($(CCACHE),)
+CC := $(CCACHE) $(CC)
+endif
endif
else
ifeq ($(TARGET),beos)
@@ -145,15 +149,9 @@ LDFLAGS += -lxml2 -lz -lcurl -lssl -lcrypto -ljpeg -liconv
LDFLAGS += -lmng -ljpeg
else
LDFLAGS := $(shell $(PKG_CONFIG) --libs libxml-2.0 libcurl openssl)
-LDFLAGS += -lz -lm -lmng -ljpeg
-
-CCACHE := $(shell which ccache)
-
-ifneq ($(CCACHE),)
-CC := $(CCACHE) $(CC)
endif
-endif
-
+# Common libraries without pkgconfig support:
+LDFLAGS += -lz -lm -lmng -ljpeg -lhpdf -lpng
endif
ifeq ($(TARGET),gtk)
@@ -170,7 +168,10 @@ GTKCFLAGS := -std=c99 -Dgtk -Dnsgtk \
$(shell $(PKG_CONFIG) --cflags librosprite) \
$(shell xml2-config --cflags)
+#GTKLDFLAGS := $(shell $(PKG_CONFIG) --cflags --libs libglade-2.0 gtk+-2.0 gthread-2.0 gmodule-2.0 librosprite)
GTKLDFLAGS := $(shell $(PKG_CONFIG) --cflags --libs libglade-2.0 gtk+-2.0 gthread-2.0 gmodule-2.0 librsvg-2.0 librosprite)
+#CFLAGS += $(GTKCFLAGS) -g
+#LDFLAGS += $(GTKLDFLAGS) $(shell $(PKG_CONFIG) --libs lcms) -lsvgtiny
CFLAGS += $(GTKCFLAGS)
LDFLAGS += $(GTKLDFLAGS) $(shell $(PKG_CONFIG) --libs lcms)
diff --git a/Makefile.sources b/Makefile.sources
index 3ac1298f5..ce237b563 100644
--- a/Makefile.sources
+++ b/Makefile.sources
@@ -39,7 +39,7 @@ S_RISCOS := 401login.c artworks.c assert.c awrender.s bitmap.c buffer.c \
cookies.c configure.c debugwin.c dialog.c download.c draw.c filetype.c \
font.c global_history.c gui.c help.c history.c hotlist.c image.c \
menus.c message.c palettes.c plotters.c plugin.c print.c query.c \
- save.c save_complete.c save_draw.c schedule.c search.c sprite.c \
+ save.c save_complete.c save_draw.c save_pdf.c schedule.c search.c sprite.c \
sslcert.c textarea.c textselection.c theme.c theme_install.c \
thumbnail.c \
treeview.c ucstables.c uri.c url_complete.c url_protocol.c wimp.c wimp_event.c \
@@ -58,9 +58,16 @@ S_DIALOGS := $(addprefix gtk/dialogs/,$(S_DIALOGS))
S_GTK := font_pango.c gtk_bitmap.c gtk_gui.c gtk_schedule.c \
gtk_thumbnail.c gtk_plotters.c gtk_treeview.c gtk_scaffolding.c \
gtk_completion.c gtk_login.c gtk_throbber.c gtk_selection.c \
- gtk_history.c gtk_window.c gtk_filetype.c gtk_download.c
+ gtk_history.c gtk_window.c gtk_filetype.c gtk_download.c gtk_print.c
S_GTK := $(addprefix gtk/,$(S_GTK))
+# S_PDF are sources of the pdf plotter + the ones for paged-printing
+S_PDF := pdf_plotters.c font_haru.c
+S_PRINT := print.c
+S_LOOSE := loosen.c
+S_PDF := $(addprefix pdf/,$(S_PDF)) $(addprefix desktop/,$(S_PRINT)) \
+ $(addprefix render/,$(S_LOOSE))
+
# S_BEOS are sources purely for the BeOS build
S_BEOS := beos_bitmap.cpp beos_fetch_rsrc.cpp beos_filetype.cpp beos_font.cpp \
beos_gui.cpp beos_history.cpp beos_login.cpp beos_options.cpp \
@@ -122,12 +129,12 @@ CLEANS += clean-intermediates
# Finally select the correct set of sources for this build...
ifeq ($(TARGET),riscos)
-SOURCES := $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_RISCOS)
+SOURCES := $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_PDF) $(S_RISCOS)
EXETARGET := !NetSurf/!RunImage$(EXEEXT)
endif
ifeq ($(TARGET),gtk)
-SOURCES := $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_GTK) $(S_DIALOGS)
+SOURCES := $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_PDF) $(S_GTK) $(S_DIALOGS)
EXETARGET := nsgtk
endif
diff --git a/beos/beos_font.cpp b/beos/beos_font.cpp
index 7379f2e89..81c66b4bb 100644
--- a/beos/beos_font.cpp
+++ b/beos/beos_font.cpp
@@ -44,6 +44,21 @@ extern "C" {
static void nsfont_style_to_font(BFont &font,
const struct css_style *style);
+static bool nsfont_width(const struct css_style *style,
+ const char *string, size_t length,
+ int *width);
+static bool 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 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 nsfont = {
+ nsfont_width,
+ nsfont_position_in_string,
+ nsfont_split
+};
/**
* Measure the width of a string.
diff --git a/desktop/browser.c b/desktop/browser.c
index 155febc50..495cdf9ee 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -1442,7 +1442,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
int pixel_offset;
size_t idx;
- nsfont_position_in_string(text_box->style,
+ nsfont.font_position_in_string(text_box->style,
text_box->text,
text_box->length,
x - gadget_box_x - text_box->x,
@@ -1483,7 +1483,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
BROWSER_MOUSE_DRAG_2))
selection_init(bw->sel, gadget_box);
- nsfont_position_in_string(text_box->style,
+ nsfont.font_position_in_string(text_box->style,
text_box->text,
text_box->length,
x - gadget_box_x - text_box->x,
@@ -1573,7 +1573,7 @@ void browser_window_mouse_action_html(struct browser_window *bw,
int pixel_offset;
size_t idx;
- nsfont_position_in_string(text_box->style,
+ nsfont.font_position_in_string(text_box->style,
text_box->text,
text_box->length,
x - text_box_x,
@@ -1836,8 +1836,7 @@ void browser_window_mouse_track_html(struct browser_window *bw,
if (box) {
int pixel_offset;
size_t idx;
-
- nsfont_position_in_string(box->style,
+ nsfont.font_position_in_string(box->style,
box->text, box->length,
dx, &idx, &pixel_offset);
@@ -1917,7 +1916,7 @@ void browser_window_mouse_drag_end(struct browser_window *bw,
box = browser_window_pick_text_box(bw,
x, y, dir, &dx, &dy);
if (box) {
- nsfont_position_in_string(
+ nsfont.font_position_in_string(
box->style,
box->text,
box->length,
diff --git a/desktop/gui.h b/desktop/gui.h
index 77fd861c3..c6b1b49cb 100644
--- a/desktop/gui.h
+++ b/desktop/gui.h
@@ -27,6 +27,7 @@
typedef enum {
GUI_SAVE_SOURCE,
GUI_SAVE_DRAW,
+ GUI_SAVE_PDF,
GUI_SAVE_TEXT,
GUI_SAVE_COMPLETE,
GUI_SAVE_OBJECT_ORIG,
diff --git a/desktop/history_core.c b/desktop/history_core.c
index 2081d132d..9fa085d8d 100644
--- a/desktop/history_core.c
+++ b/desktop/history_core.c
@@ -597,13 +597,13 @@ bool history_redraw_entry(struct history *history,
int tailsize = 5;
if (!plot.bitmap(entry->x, entry->y, WIDTH, HEIGHT,
- entry->bitmap, 0xffffff))
+ entry->bitmap, 0xffffff, NULL))
return false;
if (!plot.rectangle(entry->x - 1, entry->y - 1, WIDTH + 1, HEIGHT + 1,
entry == history->current ? 2 : 1, c, false, false))
return false;
- if (!nsfont_position_in_string(&css_base_style, entry->page.title,
+ if (!nsfont.font_position_in_string(&css_base_style, entry->page.title,
strlen(entry->page.title), WIDTH,
&char_offset, &actual_x))
return false;
diff --git a/desktop/knockout.c b/desktop/knockout.c
index f57ae5b54..cbffa3d39 100644
--- a/desktop/knockout.c
+++ b/desktop/knockout.c
@@ -61,10 +61,10 @@ static bool knockout_plot_disc(int x, int y, int radius, colour colour, bool fil
static bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2,
colour c);
static bool knockout_plot_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg);
+ struct bitmap *bitmap, colour bg, struct content *content);
static bool knockout_plot_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y);
+ bool repeat_x, bool repeat_y, struct content *content);
static bool knockout_plot_group_start(const char *name);
static bool knockout_plot_group_end(void);
@@ -194,6 +194,7 @@ struct knockout_entry {
int height;
struct bitmap *bitmap;
colour bg;
+ struct content *content;
} bitmap;
struct {
int x;
@@ -204,6 +205,7 @@ struct knockout_entry {
colour bg;
bool repeat_x;
bool repeat_y;
+ struct content *content;
} bitmap_tile;
struct {
const char *name;
@@ -379,7 +381,8 @@ bool knockout_plot_flush(void)
knockout_entries[i].data.bitmap.width,
knockout_entries[i].data.bitmap.height,
knockout_entries[i].data.bitmap.bitmap,
- knockout_entries[i].data.bitmap.bg);
+ knockout_entries[i].data.bitmap.bg,
+ knockout_entries[i].data.bitmap.content);
break;
case KNOCKOUT_PLOT_BITMAP_TILE:
box = knockout_entries[i].box->child;
@@ -403,7 +406,9 @@ bool knockout_plot_flush(void)
knockout_entries[i].data.
bitmap_tile.repeat_x,
knockout_entries[i].data.
- bitmap_tile.repeat_y);
+ bitmap_tile.repeat_y,
+ knockout_entries[i].data.
+ bitmap_tile.content);
}
break;
case KNOCKOUT_PLOT_GROUP_START:
@@ -608,7 +613,8 @@ bool knockout_plot_bitmap_tile_recursive(struct knockout_box *box,
entry->data.bitmap_tile.bitmap,
entry->data.bitmap_tile.bg,
entry->data.bitmap_tile.repeat_x,
- entry->data.bitmap_tile.repeat_y);
+ entry->data.bitmap_tile.repeat_y,
+ entry->data.bitmap_tile.content);
}
}
return success;
@@ -802,7 +808,7 @@ bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, colour
}
bool knockout_plot_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg)
+ struct bitmap *bitmap, colour bg, struct content *content)
{
int kx0, ky0, kx1, ky1;
@@ -824,6 +830,7 @@ bool knockout_plot_bitmap(int x, int y, int width, int height,
knockout_entries[knockout_entry_cur].data.bitmap.height = height;
knockout_entries[knockout_entry_cur].data.bitmap.bitmap = bitmap;
knockout_entries[knockout_entry_cur].data.bitmap.bg = bg;
+ knockout_entries[knockout_entry_cur].data.bitmap.content = content;
knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_BITMAP;
if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
knockout_plot_flush();
@@ -833,7 +840,7 @@ bool knockout_plot_bitmap(int x, int y, int width, int height,
bool knockout_plot_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y)
+ bool repeat_x, bool repeat_y, struct content *content)
{
int kx0, ky0, kx1, ky1;
@@ -879,6 +886,7 @@ bool knockout_plot_bitmap_tile(int x, int y, int width, int height,
knockout_entries[knockout_entry_cur].data.bitmap_tile.bg = bg;
knockout_entries[knockout_entry_cur].data.bitmap_tile.repeat_x = repeat_x;
knockout_entries[knockout_entry_cur].data.bitmap_tile.repeat_y = repeat_y;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.content = content;
knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_BITMAP_TILE;
if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) ||
(++knockout_box_cur >= KNOCKOUT_BOXES))
diff --git a/desktop/plotters.h b/desktop/plotters.h
index 79eac8170..16e0d47e6 100644
--- a/desktop/plotters.h
+++ b/desktop/plotters.h
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include "css/css.h"
+#include "content/content.h"
struct bitmap;
@@ -46,10 +47,11 @@ struct plotter_table {
bool (*arc)(int x, int y, int radius, int angle1, int angle2,
colour c);
bool (*bitmap)(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg);
+ struct bitmap *bitmap, colour bg,
+ struct content *content);
bool (*bitmap_tile)(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y);
+ bool repeat_x, bool repeat_y, struct content *content);
bool (*group_start)(const char *name); /** optional */
bool (*group_end)(void); /** optional */
bool (*flush)(void);
diff --git a/desktop/print.c b/desktop/print.c
new file mode 100644
index 000000000..0852fa34c
--- /dev/null
+++ b/desktop/print.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Output-in-pages implementation
+*/
+
+#include "desktop/print.h"
+#include "desktop/printer.h"
+
+#include "content/content.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;
+
+/**
+ * This function calls print setup, prints page after page until the whole
+ * content is printed calls cleaning up afterwise.
+ * \param content The content to be printed
+ * \param printer The printer interface for the printer to be used
+ * \param settings The settings for printing to use or NULL for DEFAULT
+ * \return true if successful, false otherwise
+*/
+bool print_basic_run(struct content *content,
+ const struct printer *printer,
+ struct print_settings *settings)
+{
+ bool ret = true;
+
+ if (settings == NULL)
+ settings = print_make_settings(DEFAULT);
+
+ if (!print_set_up(content, printer, settings, NULL))
+ ret = false;
+
+ while (ret && (done_height < printed_content->height) )
+ ret = print_draw_next_page(printer, settings);
+
+ print_cleanup(content, printer);
+
+ return ret;
+}
+
+/**
+ * This function prepares the content to be printed. The current browser content
+ * is duplicated and resized, printer initialization is called.
+ * \param content The content to be printed
+ * \param printer The printer interface for the printer to be used
+ * \param settings The settings for printing to use
+ * \param height updated to the height of the printed content
+ * \return true if successful, false otherwise
+*/
+bool print_set_up(struct content *content,
+ const struct printer *printer, struct print_settings *settings,
+ double *height)
+{
+
+ printed_content = print_init(content, settings);
+
+ if (!printed_content)
+ return false;
+
+ print_apply_settings(printed_content, settings);
+
+ if (height)
+ *height = printed_content->height;
+
+ printer->print_begin(settings);
+
+ done_height = 0;
+
+ return true;
+}
+
+/**
+ * This function draws one page, beginning with the height offset of done_height
+ * \param printer The printer interface for the printer to be used
+ * \param settings The settings for printing to use
+ * \return true if successful, false otherwise
+ */
+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*/
+ plot = *(printer->plotter);
+
+ printer->print_next_page();
+ if( !content_redraw(printed_content,
+ 0,
+ -done_height,
+ 0,0,
+ 0,
+ 0,
+ page_content_width * settings->scale,
+ page_content_height * settings->scale,
+ settings->scale, 0xffffff))
+ return false;
+ done_height += page_content_height - text_margin_height;
+
+ return true;
+}
+
+/**
+ * The content passed to the function is duplicated with its boxes, font
+ * measuring functions are being set.
+ * \param content The content to be printed
+ * \param settings The settings for printing to use
+ * \return true if successful, false otherwise
+ */
+struct content *print_init(struct content *content,
+ struct print_settings *settings)
+{
+ struct content* printed_content;
+ struct content_user *user_sentinel;
+
+ content_add_user(content, NULL, (intptr_t)print_init, 0);
+
+ printed_content = talloc_memdup(content, content, sizeof *content);
+
+ if (!printed_content)
+ return NULL;
+
+ printed_content->data.html.bw = 0;
+
+ user_sentinel = talloc(printed_content, struct content_user);
+ user_sentinel->callback = 0;
+ user_sentinel->p1 = user_sentinel->p2 = 0;
+ user_sentinel->next = 0;
+ printed_content->user_list = user_sentinel;
+ content_add_user(printed_content, NULL, (intptr_t)print_init, 0);
+
+ printed_content->data.html.layout =
+ box_duplicate_tree(content->data.html.layout,
+ printed_content);
+
+ if (!printed_content->data.html.layout)
+ return NULL;
+
+ if (settings->font_func == NULL)
+ printed_content->data.html.font_func = &haru_nsfont;
+ else
+ printed_content->data.html.font_func = settings->font_func;
+
+ return printed_content;
+}
+
+/**
+ * The content is resized to fit page width. In case it is to wide, it is
+ * loosened.
+ * \param content The content to be printed
+ * \param settings The settings for printing to use
+ * \return true if successful, false otherwise
+ */
+bool print_apply_settings(struct content *content,
+ struct print_settings *settings)
+{
+ if (settings == NULL)
+ 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;
+
+ page_content_height = (settings->page_height - settings->margins[MARGINTOP] -
+ settings->margins[MARGINBOTTOM]) / settings->scale;
+
+ content_reformat(content, page_content_width, 0);
+ 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);
+
+ return true;
+}
+
+/**
+ * Memory allocated during printing is being freed here.
+ * \param content The original content
+ * \param printer The printer interface for the printer to be used
+ * \return true if successful, false otherwise
+ */
+bool print_cleanup(struct content *content,
+ const struct printer *printer)
+{
+ printer->print_end();
+
+ if (printed_content) {
+ content_remove_user(printed_content, NULL, (intptr_t)print_init, 0);
+ talloc_free(printed_content);
+ }
+
+ content_remove_user(content, NULL, (intptr_t)print_init, 0);
+
+ return true;
+}
+
+/**
+ * Generates one of the predefined print settings sets.
+ * \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 *settings;
+
+ switch (configuration){
+ case DEFAULT:
+ settings = (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->margins[MARGINLEFT] = 30;
+ settings->margins[MARGINRIGHT] = 30;
+ settings->margins[MARGINTOP] = 30;
+ settings->margins[MARGINBOTTOM] = 30;
+
+ settings->margins[MARGINTEXT] = 10;
+
+ settings->output = "out.pdf";
+ settings->font_func = &haru_nsfont;
+
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return settings;
+}
diff --git a/desktop/print.h b/desktop/print.h
new file mode 100644
index 000000000..dc4cde642
--- /dev/null
+++ b/desktop/print.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Conception:
+ * Generalized output-in-pages. Allows the current content to be 'printed':
+ * either into a pdf file (any other later?) or to any kind of other output
+ * destination that divides the website into pages - as a printer.
+ * The basic usage is calling print_basic_run which sets everything up,
+ * prints page after page until the whole content is printed and cleans
+ * everyting up.
+ * If there are any other, printer specific routines to be performed in the
+ * meantime - there can be set up any other printing funcion, which can use
+ * print_set_up, print_draw_next_page and print_cleanup directly.
+*/
+
+#ifndef NETSURF_DESKTOP_PRINT_H
+#define NETSURF_DESKTOP_PRINT_H
+
+#include <stdbool.h>
+
+struct content;
+struct printer;
+
+enum { MARGINLEFT = 0, MARGINRIGHT = 1, MARGINTOP = 2, MARGINBOTTOM = 3,
+ MARGINTEXT = 4};
+
+/** Predefined printing configuration names*/
+typedef enum {DEFAULT} print_configuration;
+
+/** Settings for a print - filled in by print_make_settings or
+ * 'manually' by the caller
+*/
+struct print_settings{
+ /*Standard parameters*/
+ float page_width, page_height;
+ float margins[5];
+
+ float scale;
+
+ unsigned int copies;
+
+ /*Output destinations - file/printer name*/
+ const char *output;
+
+ /*TODO: more options?*/
+ 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 *);
+
+struct print_settings *print_make_settings(print_configuration configuration);
+
+#endif
diff --git a/desktop/printer.h b/desktop/printer.h
new file mode 100644
index 000000000..358922379
--- /dev/null
+++ b/desktop/printer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Printer interface - contains plotter to use, functions for
+ * initialization, handling pages and cleaning up.
+*/
+
+#ifndef NETSURF_DESKTOP_PRINTER_H
+#define NETSURF_DESKTOP_PRINTER_H
+
+#include "desktop/plotters.h"
+#include "desktop/print.h"
+
+/** Printer interface */
+struct printer{
+ const struct plotter_table *plotter;
+
+ bool (*print_begin) (struct print_settings*);
+
+ bool (*print_next_page)(void);
+
+ void (*print_end)(void);
+};
+
+#endif
diff --git a/desktop/selection.c b/desktop/selection.c
index 2be6853a2..0dc8e493e 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -601,7 +601,7 @@ bool redraw_handler(const char *text, size_t length, struct box *box,
height = box->padding[TOP] + box->height + box->padding[BOTTOM];
if (box->type == BOX_TEXT && box->space &&
- nsfont_width(box->style, " ", 1, &space_width))
+ nsfont.font_width(box->style, " ", 1, &space_width))
width += space_width;
if (r->inited) {
diff --git a/desktop/textinput.c b/desktop/textinput.c
index a90919afe..8cfe01050 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -181,7 +181,7 @@ struct box *textarea_get_position(struct box *textarea, int x, int y,
assert(text_box->type == BOX_TEXT);
assert(text_box->text);
/** \todo handle errors */
- nsfont_position_in_string(text_box->style, text_box->text,
+ nsfont.font_position_in_string(text_box->style, text_box->text,
text_box->length,
(unsigned int)(x - text_box->x),
(size_t *) pchar_offset, ppixel_offset);
@@ -203,7 +203,7 @@ struct box *textarea_get_position(struct box *textarea, int x, int y,
text_box = inline_container->last;
assert(text_box->type == BOX_TEXT);
assert(text_box->text);
- nsfont_position_in_string(text_box->style,
+ nsfont.font_position_in_string(text_box->style,
text_box->text,
text_box->length,
textarea->width,
@@ -223,7 +223,7 @@ struct box *textarea_get_position(struct box *textarea, int x, int y,
}
assert(text_box->type == BOX_TEXT);
assert(text_box->text);
- nsfont_position_in_string(text_box->style,
+ nsfont.font_position_in_string(text_box->style,
text_box->text,
text_box->length,
(unsigned int)(x - text_box->x),
@@ -761,7 +761,7 @@ bool browser_window_textarea_callback(struct browser_window *bw,
}
}
- nsfont_width(text_box->style, text_box->text,
+ nsfont.font_width(text_box->style, text_box->text,
char_offset, &pixel_offset);
selection_clear(bw->sel, true);
@@ -813,7 +813,7 @@ void browser_window_input_click(struct browser_window* bw,
int pixel_offset = 0, dx = 0;
struct box *text_box = input->children->children;
- nsfont_position_in_string(text_box->style, text_box->text,
+ nsfont.font_position_in_string(text_box->style, text_box->text,
text_box->length, x - text_box->x,
&char_offset, &pixel_offset);
assert(char_offset <= text_box->length);
@@ -1375,7 +1375,7 @@ bool browser_window_textarea_paste_text(struct browser_window *bw,
textarea->gadget->caret_text_box = text_box;
textarea->gadget->caret_box_offset = char_offset;
- nsfont_width(text_box->style, text_box->text,
+ nsfont.font_width(text_box->style, text_box->text,
char_offset, &pixel_offset);
textarea->gadget->caret_pixel_offset = pixel_offset;
@@ -1508,7 +1508,7 @@ void browser_window_textarea_move_caret(struct browser_window *bw, void *p)
box_x -= textarea->scroll_x;
box_y -= textarea->scroll_y;
- nsfont_width(text_box->style, text_box->text,
+ nsfont.font_width(text_box->style, text_box->text,
char_offset, &pixel_offset);
browser_window_place_caret(bw,
@@ -1541,7 +1541,7 @@ void browser_window_input_move_caret(struct browser_window *bw, void *p)
box_coords(input, &box_x, &box_y);
- nsfont_width(text_box->style, text_box->text, box_offset,
+ nsfont.font_width(text_box->style, text_box->text, box_offset,
&pixel_offset);
browser_window_place_caret(bw,
@@ -1576,12 +1576,12 @@ void input_update_display(struct browser_window *bw, struct box *input,
int dx;
if (redraw)
- nsfont_width(text_box->style, text_box->text, text_box->length,
+ nsfont.font_width(text_box->style, text_box->text, text_box->length,
&text_box->width);
box_coords(input, &box_x, &box_y);
- nsfont_width(text_box->style, text_box->text, box_offset,
+ nsfont.font_width(text_box->style, text_box->text, box_offset,
(int *) &pixel_offset);
/* Shift text box horizontally, so caret is visible */
diff --git a/gtk/font_pango.c b/gtk/font_pango.c
index c6ddae102..e1ec7b6fe 100644
--- a/gtk/font_pango.c
+++ b/gtk/font_pango.c
@@ -37,8 +37,26 @@
/* Until we can consider the descenders etc, we need to not render using cairo */
#undef CAIRO_VERSION
-static PangoFontDescription *nsfont_style_to_description(
- const struct css_style *style);
+static bool nsfont_width(const struct css_style *style,
+ const char *string, size_t length,
+ int *width);
+
+static bool 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 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 nsfont = {
+ nsfont_width,
+ nsfont_position_in_string,
+ nsfont_split
+};
+
+
+
/**
@@ -203,11 +221,11 @@ bool nsfont_paint(const struct css_style *style,
{
PangoFontDescription *desc;
PangoLayout *layout;
+ PangoLayoutLine *line;
gint size;
#ifdef CAIRO_VERSION
int width, height;
-#else
- PangoLayoutLine *line;
+#else
PangoContext *context;
GdkColor colour = { 0,
((c & 0xff) << 8) | (c & 0xff),
@@ -234,14 +252,13 @@ bool nsfont_paint(const struct css_style *style,
pango_layout_set_font_description(layout, desc);
pango_layout_set_text(layout, string, length);
-
+ line = pango_layout_get_line(layout, 0);
+
#ifdef CAIRO_VERSION
- pango_layout_get_pixel_size(layout, &width, &height);
- cairo_move_to(current_cr, x, y - height);
+ cairo_move_to(current_cr, x, y);
nsgtk_set_colour(c);
- pango_cairo_show_layout(current_cr, layout);
+ pango_cairo_show_layout_line(current_cr, layout, line);
#else
- line = pango_layout_get_line(layout, 0);
gdk_draw_layout_line_with_colors(current_drawable, current_gc,
x, y, line, &colour, 0);
@@ -350,3 +367,4 @@ PangoFontDescription *nsfont_style_to_description(
return desc;
}
+
diff --git a/gtk/font_pango.h b/gtk/font_pango.h
index b3b08b95a..cbae6b2a7 100644
--- a/gtk/font_pango.h
+++ b/gtk/font_pango.h
@@ -20,6 +20,9 @@
* Font handling (GTK interface).
*/
+#ifndef _NETSURF_GTK_FONT_PANGO_H_
+#define _NETSURF_GTK_FONT_PANGO_H_
+
#include <stdbool.h>
@@ -28,3 +31,9 @@ struct css_style;
bool nsfont_paint(const struct css_style *style,
const char *string, size_t length,
int x, int y, colour c);
+
+PangoFontDescription *nsfont_style_to_description(
+ const struct css_style *style);
+
+
+#endif
diff --git a/gtk/gtk_bitmap.c b/gtk/gtk_bitmap.c
index 2f4e1085c..bf36a94e6 100644
--- a/gtk/gtk_bitmap.c
+++ b/gtk/gtk_bitmap.c
@@ -211,6 +211,14 @@ void bitmap_set_suspendable(struct bitmap *bitmap, void *private_word,
void (*invalidate)(struct bitmap *bitmap, void *private_word)) {
}
+int bitmap_get_width(struct bitmap *bitmap){
+ return gdk_pixbuf_get_width(bitmap->primary);
+}
+
+int bitmap_get_height(struct bitmap *bitmap){
+ return gdk_pixbuf_get_height(bitmap->primary);
+}
+
static GdkPixbuf *
gtk_bitmap_generate_pretile(GdkPixbuf *primary, int repeat_x, int repeat_y)
{
diff --git a/gtk/gtk_plotters.c b/gtk/gtk_plotters.c
index 62d31539e..7a696e84a 100644
--- a/gtk/gtk_plotters.c
+++ b/gtk/gtk_plotters.c
@@ -65,10 +65,10 @@ static bool nsgtk_plot_disc(int x, int y, int radius, colour c, bool filled);
static bool nsgtk_plot_arc(int x, int y, int radius, int angle1, int angle2,
colour c);
static bool nsgtk_plot_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg);
+ struct bitmap *bitmap, colour bg, struct content *content);
static bool nsgtk_plot_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y);
+ bool repeat_x, bool repeat_y, struct content *content);
static void nsgtk_set_solid(void); /**< Set for drawing solid lines */
static void nsgtk_set_dotted(void); /**< Set for drawing dotted lines */
static void nsgtk_set_dashed(void); /**< Set for drawing dashed lines */
@@ -281,7 +281,7 @@ static bool nsgtk_plot_pixbuf(int x, int y, int width, int height,
}
bool nsgtk_plot_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg)
+ struct bitmap *bitmap, colour bg, struct content *content)
{
GdkPixbuf *pixbuf = gtk_bitmap_get_primary(bitmap);
return nsgtk_plot_pixbuf(x, y, width, height, pixbuf, bg);
@@ -289,7 +289,7 @@ bool nsgtk_plot_bitmap(int x, int y, int width, int height,
bool nsgtk_plot_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y)
+ bool repeat_x, bool repeat_y, struct content *content)
{
int doneheight = 0, donewidth = 0;
GdkPixbuf *primary;
@@ -297,7 +297,7 @@ bool nsgtk_plot_bitmap_tile(int x, int y, int width, int height,
if (!(repeat_x || repeat_y)) {
/* Not repeating at all, so just pass it on */
- return nsgtk_plot_bitmap(x,y,width,height,bitmap,bg);
+ return nsgtk_plot_bitmap(x,y,width,height,bitmap,bg,content);
}
if (repeat_x && !repeat_y)
diff --git a/gtk/gtk_plotters.h b/gtk/gtk_plotters.h
index 2fb940904..33e1a9484 100644
--- a/gtk/gtk_plotters.h
+++ b/gtk/gtk_plotters.h
@@ -42,3 +42,4 @@ void nsgtk_set_colour(colour c);
void nsgtk_plot_caret(int x, int y, int h);
#endif /* NETSURF_GTK_PLOTTERS_H */
+
diff --git a/gtk/gtk_print.c b/gtk/gtk_print.c
new file mode 100644
index 000000000..6056eec61
--- /dev/null
+++ b/gtk/gtk_print.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+ /** \file
+ * GTK printing (implementation).
+ * All the functions and structures necessary for printing( signal handlers,
+ * plotters, printer) are here.
+ * Most of the plotters have been copied from the gtk_plotters.c file.
+ */
+
+
+#include <math.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include "desktop/plotters.h"
+#include "gtk/font_pango.h"
+#include "gtk/gtk_scaffolding.h"
+#include "render/font.h"
+#include "utils/log.h"
+#include "desktop/options.h"
+#include "gtk/options.h"
+#include "gtk/gtk_bitmap.h"
+
+#include "desktop/print.h"
+#include "desktop/printer.h"
+#include "content/content.h"
+#include "gtk_print.h"
+#include "utils/utils.h"
+
+cairo_t *gtk_print_current_cr;
+static struct print_settings* settings;
+struct content *content_to_print;
+
+static bool nsgtk_print_plot_clg(colour c);
+static bool nsgtk_print_plot_rectangle(int x0, int y0, int width, int height,
+ int line_width, colour c, bool dotted, bool dashed);
+static bool nsgtk_print_plot_line(int x0, int y0, int x1, int y1, int width,
+ colour c, bool dotted, bool dashed);
+static bool nsgtk_print_plot_polygon(int *p, unsigned int n, colour fill);
+static bool nsgtk_print_plot_path(float *p, unsigned int n, colour fill, float width,
+ colour c, float *transform);
+static bool nsgtk_print_plot_fill(int x0, int y0, int x1, int y1, colour c);
+static bool nsgtk_print_plot_clip(int clip_x0, int clip_y0,
+ int clip_x1, int clip_y1);
+static bool nsgtk_print_plot_text(int x, int y, const struct css_style *style,
+ const char *text, size_t length, colour bg, colour c);
+static bool nsgtk_print_plot_disc(int x, int y, int radius, colour c, bool filled);
+static bool nsgtk_print_plot_arc(int x, int y, int radius, int angle1, int angle2,
+ colour c);
+static bool nsgtk_print_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg, struct content *content);
+static bool nsgtk_print_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 void nsgtk_print_set_solid(void); /**< Set for drawing solid lines */
+static void nsgtk_print_set_dotted(void); /**< Set for drawing dotted lines */
+static void nsgtk_print_set_dashed(void); /**< Set for drawing dashed lines */
+
+
+static void nsgtk_print_set_colour(colour c);
+static void nsgtk_print_plot_caret(int x, int y, int h);
+
+
+static bool gtk_print_font_paint(const struct css_style *style,
+ const char *string, size_t length,
+ int x, int y, colour c);
+
+
+static bool gtk_print_begin(struct print_settings* settings);
+static bool gtk_print_next_page(void);
+static void gtk_print_end(void);
+
+
+static GdkRectangle cliprect;
+
+struct plotter_table plot;
+
+static const struct plotter_table nsgtk_print_plotters = {
+ nsgtk_print_plot_clg,
+ nsgtk_print_plot_rectangle,
+ nsgtk_print_plot_line,
+ nsgtk_print_plot_polygon,
+ nsgtk_print_plot_fill,
+ nsgtk_print_plot_clip,
+ nsgtk_print_plot_text,
+ nsgtk_print_plot_disc,
+ nsgtk_print_plot_arc,
+ nsgtk_print_plot_bitmap,
+ nsgtk_print_plot_bitmap_tile,
+ NULL,
+ NULL,
+ NULL,
+ nsgtk_print_plot_path
+};
+
+static const struct printer gtk_printer= {
+ &nsgtk_print_plotters,
+ gtk_print_begin,
+ gtk_print_next_page,
+ gtk_print_end
+};
+
+bool nsgtk_print_plot_clg(colour c)
+{
+ return true;
+}
+
+bool nsgtk_print_plot_rectangle(int x0, int y0, int width, int height,
+ int line_width, colour c, bool dotted, bool dashed)
+{
+ LOG(("Plotting rectangle. width: %i ; height: %i", width, height));
+ nsgtk_print_set_colour(c);
+ if (dotted)
+ nsgtk_print_set_dotted();
+ else if (dashed)
+ nsgtk_print_set_dashed();
+ else
+ nsgtk_print_set_solid();
+
+
+ if (line_width == 0)
+ line_width = 1;
+
+ cairo_set_line_width(gtk_print_current_cr, line_width);
+ cairo_rectangle(gtk_print_current_cr, x0, y0, width, height);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+
+bool nsgtk_print_plot_line(int x0, int y0, int x1, int y1, int width,
+ colour c, bool dotted, bool dashed)
+{
+ nsgtk_print_set_colour(c);
+ if (dotted)
+ nsgtk_print_set_dotted();
+ else if (dashed)
+ nsgtk_print_set_dashed();
+ else
+ nsgtk_print_set_solid();
+
+
+ if (width == 0)
+ width = 1;
+
+ cairo_set_line_width(gtk_print_current_cr, width);
+ cairo_move_to(gtk_print_current_cr, x0, y0 - 0.5);
+ cairo_line_to(gtk_print_current_cr, x1, y1 - 0.5);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+
+bool nsgtk_print_plot_polygon(int *p, unsigned int n, colour fill)
+{
+
+ LOG(("Plotting polygon."));
+ unsigned int i;
+
+ nsgtk_print_set_colour(fill);
+ nsgtk_print_set_solid();
+
+
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_move_to(gtk_print_current_cr, p[0], p[1]);
+ LOG(("Starting line at: %i\t%i",p[0],p[1]));
+ for (i = 1; i != n; i++) {
+ cairo_line_to(gtk_print_current_cr, p[i * 2], p[i * 2 + 1]);
+ LOG(("Drawing line to: %i\t%i",p[i * 2], p[i * 2 + 1]));
+ }
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+
+bool nsgtk_print_plot_fill(int x0, int y0, int x1, int y1, colour c)
+{
+ LOG(("Plotting fill. x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", x0,y0,x1,y1));
+ nsgtk_print_set_colour(c);
+ nsgtk_print_set_solid();
+
+ /*Normalize boundaries of the area - to prevent overflows.
+ See comment in pdf_plot_fill.
+ */
+ x0 = min(max(x0, 0), settings->page_width);
+ y0 = min(max(y0, 0), settings->page_height);
+ x1 = min(max(x1, 0), settings->page_width);
+ y1 = min(max(y1, 0), settings->page_height);
+
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+
+bool nsgtk_print_plot_clip(int clip_x0, int clip_y0,
+ int clip_x1, int clip_y1)
+{
+ LOG(("Clipping. x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i",
+ clip_x0,clip_y0,clip_x1,clip_y1));
+
+ /*Normalize cllipping area - to prevent overflows.
+ See comment in pdf_plot_fill.
+ */
+ clip_x0 = min(max(clip_x0, 0), settings->page_width);
+ clip_y0 = min(max(clip_y0, 0), settings->page_height);
+ clip_x1 = min(max(clip_x1, 0), settings->page_width);
+ clip_y1 = min(max(clip_y1, 0), settings->page_height);
+
+
+ cairo_reset_clip(gtk_print_current_cr);
+ cairo_rectangle(gtk_print_current_cr, clip_x0, clip_y0,
+ clip_x1 - clip_x0, clip_y1 - clip_y0);
+ cairo_clip(gtk_print_current_cr);
+
+ cliprect.x = clip_x0;
+ cliprect.y = clip_y0;
+ cliprect.width = clip_x1 - clip_x0;
+ cliprect.height = clip_y1 - clip_y0;
+// gdk_gc_set_clip_rectangle(gtk_print_current_gc, &cliprect);
+ return true;
+
+}
+
+
+bool nsgtk_print_plot_text(int x, int y, const struct css_style *style,
+ const char *text, size_t length, colour bg, colour c)
+{
+ return gtk_print_font_paint(style, text, length, x, y, c);
+}
+
+
+bool nsgtk_print_plot_disc(int x, int y, int radius, colour c, bool filled)
+{
+ nsgtk_print_set_colour(c);
+ nsgtk_print_set_solid();
+
+ if (filled)
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ else
+ cairo_set_line_width(gtk_print_current_cr, 1);
+
+ cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2);
+
+ if (filled)
+ cairo_fill(gtk_print_current_cr);
+
+ cairo_stroke(gtk_print_current_cr);
+
+
+ return true;
+}
+
+bool nsgtk_print_plot_arc(int x, int y, int radius, int angle1, int angle2, colour c)
+{
+ nsgtk_print_set_colour(c);
+ nsgtk_print_set_solid();
+
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ cairo_arc(gtk_print_current_cr, x, y, radius,
+ (angle1 + 90) * (M_PI / 180),
+ (angle2 + 90) * (M_PI / 180));
+ cairo_stroke(gtk_print_current_cr);
+
+
+ return true;
+}
+
+static bool nsgtk_print_plot_pixbuf(int x, int y, int width, int height,
+ GdkPixbuf *pixbuf, colour bg)
+{
+ /* XXX: This currently ignores the background colour supplied.
+ * Does this matter?
+ */
+
+ if (width == 0 || height == 0)
+ return true;
+
+ if (gdk_pixbuf_get_width(pixbuf) == width &&
+ gdk_pixbuf_get_height(pixbuf) == height) {
+ gdk_cairo_set_source_pixbuf(gtk_print_current_cr, pixbuf, x, y);
+ cairo_paint(gtk_print_current_cr);
+
+ } else {
+ GdkPixbuf *scaled;
+ scaled = gdk_pixbuf_scale_simple(pixbuf,
+ width, height,
+ /* plotting for the printer doesn't have to be fast
+ * so we can use always the interp_style
+ * that gives better quality
+ */
+ GDK_INTERP_BILINEAR
+ );
+ if (!scaled)
+ return false;
+ gdk_cairo_set_source_pixbuf(gtk_print_current_cr, scaled, x, y);
+ cairo_paint(gtk_print_current_cr);
+
+ g_object_unref(scaled);
+ }
+
+ return true;
+}
+
+bool nsgtk_print_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg, struct content *content)
+{
+ GdkPixbuf *pixbuf = gtk_bitmap_get_primary(bitmap);
+ return nsgtk_print_plot_pixbuf(x, y, width, height, pixbuf, bg);
+}
+
+bool nsgtk_print_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)
+{
+ int doneheight = 0, donewidth = 0;
+ GdkPixbuf *primary;
+ GdkPixbuf *pretiled;
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just pass it on */
+ return nsgtk_print_plot_bitmap(x,y,width,height,bitmap,bg,content);
+ }
+
+ if (repeat_x && !repeat_y)
+ pretiled = gtk_bitmap_get_pretile_x(bitmap);
+ if (repeat_x && repeat_y)
+ pretiled = gtk_bitmap_get_pretile_xy(bitmap);
+ if (!repeat_x && repeat_y)
+ pretiled = gtk_bitmap_get_pretile_y(bitmap);
+ primary = gtk_bitmap_get_primary(bitmap);
+ /* use the primary and pretiled widths to scale the w/h provided */
+ width *= gdk_pixbuf_get_width(pretiled);
+ width /= gdk_pixbuf_get_width(primary);
+ height *= gdk_pixbuf_get_height(pretiled);
+ height /= gdk_pixbuf_get_height(primary);
+
+ if (y > cliprect.y)
+ doneheight = (cliprect.y - height) + ((y - cliprect.y) % height);
+ else
+ doneheight = y;
+
+ while (doneheight < (cliprect.y + cliprect.height)) {
+ if (x > cliprect.x)
+ donewidth = (cliprect.x - width) + ((x - cliprect.x) % width);
+ else
+ donewidth = x;
+ while (donewidth < (cliprect.x + cliprect.width)) {
+ nsgtk_print_plot_pixbuf(donewidth, doneheight,
+ width, height, pretiled, bg);
+ donewidth += width;
+ if (!repeat_x) break;
+ }
+ doneheight += height;
+ if (!repeat_y) break;
+ }
+
+
+ return true;
+}
+
+bool nsgtk_print_plot_path(float *p, unsigned int n, colour fill, float width,
+ colour c, float *transform)
+{
+ /* Only the internal SVG renderer uses this plot call currently,
+ * and the GTK version uses librsvg. Thus, we ignore this complexity,
+ * and just return true obliviously.
+ */
+
+ return true;
+}
+
+void nsgtk_print_set_colour(colour c)
+{
+ int r, g, b;
+ GdkColor colour;
+
+ r = c & 0xff;
+ g = (c & 0xff00) >> 8;
+ b = (c & 0xff0000) >> 16;
+
+ colour.red = r | (r << 8);
+ colour.green = g | (g << 8);
+ colour.blue = b | (b << 8);
+ colour.pixel = (r << 16) | (g << 8) | b;
+
+ gdk_color_alloc(gdk_colormap_get_system(),
+ &colour);
+// gdk_gc_set_foreground(gtk_print_current_gc, &colour);
+
+ cairo_set_source_rgba(gtk_print_current_cr, r / 255.0,
+ g / 255.0, b / 255.0, 1.0);
+
+}
+
+
+void nsgtk_print_set_solid()
+{
+
+ double dashes = 0;
+
+ cairo_set_dash(gtk_print_current_cr, &dashes, 0, 0);
+
+}
+
+void nsgtk_print_set_dotted()
+{
+ double cdashes = 1;
+ gint8 dashes[] = { 1, 1 };
+
+ cairo_set_dash(gtk_print_current_cr, &cdashes, 1, 0);
+
+}
+
+void nsgtk_print_set_dashed()
+{
+ double cdashes = 3;
+ gint8 dashes[] = { 3, 3 };
+
+ cairo_set_dash(gtk_print_current_cr, &cdashes, 1, 0);
+}
+
+bool gtk_print_font_paint(const struct css_style *style,
+ const char *string, size_t length,
+ int x, int y, colour c)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ gint size;
+ PangoLayoutLine *line;
+
+ int width, height;
+
+
+ if (length == 0)
+ return true;
+
+ desc = nsfont_style_to_description(style);
+ size = (gint)((double)pango_font_description_get_size(desc) * settings->scale);
+ if (pango_font_description_get_size_is_absolute(desc))
+ pango_font_description_set_absolute_size(desc, size);
+ else
+ pango_font_description_set_size(desc, size);
+
+
+ layout = pango_cairo_create_layout(gtk_print_current_cr);
+
+ pango_layout_set_font_description(layout, desc);
+ pango_layout_set_text(layout, string, length);
+
+ line = pango_layout_get_line(layout, 0);
+
+ cairo_move_to(gtk_print_current_cr, x, y);
+ nsgtk_print_set_colour(c);
+ pango_cairo_show_layout_line(gtk_print_current_cr, line);
+
+
+ g_object_unref(layout);
+ pango_font_description_free(desc);
+
+ return true;
+}
+
+
+
+static bool gtk_print_begin(struct print_settings* settings)
+{
+ return true;
+}
+static bool gtk_print_next_page()
+{
+ return true;
+}
+static void gtk_print_end()
+{
+}
+
+/** Handle the begin_print signal from the GtkPrintOperation
+ * \param operation the operation which emited the signal
+ * \param context the print context used to set up the pages
+ * \param user_data nothing in here
+ */
+
+void gtk_print_signal_begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data)
+{
+
+ int page_number;
+ double height_on_page, height_to_print;
+
+ LOG(("Begin print"));
+
+
+ settings = print_make_settings(DEFAULT);
+
+ settings->margins[MARGINTEXT] = 0;
+ settings->margins[MARGINTOP] = 0;
+ settings->margins[MARGINLEFT] = 0;
+ settings->margins[MARGINBOTTOM] = 0;
+ settings->margins[MARGINRIGHT] = 0;
+ settings->page_width = gtk_print_context_get_width(context);
+ settings->page_height = gtk_print_context_get_height(context);
+ settings->scale = 0.7;
+ settings->font_func = &nsfont;
+
+ print_set_up(content_to_print, &gtk_printer, settings, &height_to_print);
+
+ LOG(("page_width: %f ;page_height: %f; content height: %lf",settings->page_width,
+ settings->page_height, height_to_print));
+
+ height_on_page = settings->page_height;
+ height_on_page = height_on_page - settings->margins[MARGINTOP]
+ - settings->margins[MARGINBOTTOM];
+ height_to_print *= settings->scale;
+
+ page_number = height_to_print / height_on_page;
+ if (height_to_print - page_number * height_on_page > 0)
+ page_number += 1;
+
+
+ gtk_print_operation_set_n_pages(operation, page_number);
+}
+
+/** Handle the draw_page signal from the GtkPrintOperation.
+ * This function changes only the cairo context to print on.
+ */
+
+void gtk_print_signal_draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ gpointer user_data)
+{
+ LOG(("Draw Page"));
+ gtk_print_current_cr = gtk_print_context_get_cairo_context(context);
+ print_draw_next_page(&gtk_printer, settings);
+}
+
+/** Handle the end_print signal from the GtkPrintOperation.
+ * This functions calls only the print_cleanup function from the print interface
+ */
+
+void gtk_print_signal_end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data)
+{
+ LOG(("End print"));
+ print_cleanup(content_to_print, &gtk_printer);
+}
diff --git a/gtk/gtk_print.h b/gtk/gtk_print.h
new file mode 100644
index 000000000..b232920de
--- /dev/null
+++ b/gtk/gtk_print.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+ /** \file
+ * GTK printing (interface).
+ */
+
+#ifndef NETSURF_GTK_PRINT_PLOTTERS_H
+#define NETSURF_GTK_PRINT_PLOTTERS_H
+
+
+#include <gtk/gtk.h>
+
+extern cairo_t *gtk_print_current_cr;
+
+extern struct content *content_to_print;
+
+
+/*handlers for signals from the GTK print operation*/
+void gtk_print_signal_begin_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data);
+
+void gtk_print_signal_draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ gpointer user_data);
+
+void gtk_print_signal_end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data);
+
+#endif
diff --git a/gtk/gtk_scaffolding.c b/gtk/gtk_scaffolding.c
index c13acbad5..1165d785a 100644
--- a/gtk/gtk_scaffolding.c
+++ b/gtk/gtk_scaffolding.c
@@ -49,6 +49,11 @@
#include "render/html.h"
#include "utils/messages.h"
#include "utils/utils.h"
+
+#include "pdf/pdf_plotters.h"
+#include "desktop/print.h"
+#include "gtk/gtk_print.h"
+
#undef NDEBUG
#include "utils/log.h"
@@ -138,6 +143,9 @@ void nsgtk_openfile_open(char *filename);
MENUPROTO(new_window);
MENUPROTO(open_location);
MENUPROTO(open_file);
+MENUPROTO(export_pdf);
+MENUPROTO(print);
+MENUPROTO(print_preview);
MENUPROTO(close_window);
MENUPROTO(quit);
@@ -181,6 +189,9 @@ static struct menu_events menu_events[] = {
MENUEVENT(new_window),
MENUEVENT(open_location),
MENUEVENT(open_file),
+ MENUEVENT(export_pdf),
+ MENUEVENT(print),
+ MENUEVENT(print_preview),
MENUEVENT(close_window),
MENUEVENT(quit),
@@ -467,6 +478,86 @@ MENUHANDLER(open_file)
return TRUE;
}
+MENUHANDLER(export_pdf){
+
+ GtkWidget *save_dialog;
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+ struct print_settings* settings;
+
+ LOG(("Print preview (generating PDF) started."));
+
+ settings = print_make_settings(DEFAULT);
+
+ save_dialog = gtk_file_chooser_dialog_new("Export to PDF", gw->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ getenv("HOME") ? getenv("HOME") : "/");
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ "out.pdf");
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ settings->output = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+ print_basic_run(bw->current_content, &pdf_printer, settings);
+
+ return TRUE;
+}
+
+MENUHANDLER(print){
+
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+
+ GtkPrintOperation* print_op;
+ GtkPageSetup* page_setup;
+ struct print_settings* settings;
+
+ settings = print_make_settings(DEFAULT);
+
+ print_op = gtk_print_operation_new();
+ page_setup = gtk_page_setup_new();
+
+ content_to_print = bw->current_content;
+
+ page_setup = gtk_print_run_page_setup_dialog(gw->window, page_setup, NULL);
+ gtk_print_operation_set_default_page_setup (print_op, page_setup);
+
+ g_signal_connect(print_op, "begin_print", G_CALLBACK (gtk_print_signal_begin_print), NULL);
+ g_signal_connect(print_op, "draw_page", G_CALLBACK (gtk_print_signal_draw_page), NULL);
+ g_signal_connect(print_op, "end_print", G_CALLBACK (gtk_print_signal_end_print), NULL);
+
+
+ gtk_print_operation_run(print_op,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ gw->window,
+ NULL);
+
+
+ return TRUE;
+}
+
+MENUHANDLER(print_preview){
+
+ struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
+ struct browser_window *bw = nsgtk_get_browser_for_gui(gw->top_level);
+
+ LOG(("Print preview (generating PDF) started."));
+
+ print_basic_run(bw->current_content, &pdf_printer, NULL);
+
+ return TRUE;
+}
+
MENUHANDLER(close_window)
{
struct gtk_scaffolding *gw = (struct gtk_scaffolding *)g;
diff --git a/gtk/res/netsurf.glade b/gtk/res/netsurf.glade
index 09af6fea9..9f9e036f4 100644
--- a/gtk/res/netsurf.glade
+++ b/gtk/res/netsurf.glade
@@ -91,7 +91,7 @@
<child>
<widget class="GtkMenuItem" id="export">
<property name="visible">True</property>
- <property name="sensitive">False</property>
+ <property name="sensitive">True</property>
<property name="tooltip" translatable="yes">Export the page to a different format.</property>
<property name="label" translatable="yes">Export</property>
<property name="use_underline">True</property>
@@ -100,6 +100,7 @@
<child>
<widget class="GtkMenuItem" id="export_plain_text">
<property name="visible">True</property>
+ <property name="sensitive">False</property>
<property name="tooltip" translatable="yes">Plain ASCII text, readable in text editors and views.</property>
<property name="label" translatable="yes">Plain text...</property>
<property name="use_underline">True</property>
@@ -108,6 +109,7 @@
<child>
<widget class="GtkMenuItem" id="export_drawfile">
<property name="visible">True</property>
+ <property name="sensitive">False</property>
<property name="tooltip" translatable="yes">RISC OS Drawfile vector graphic.</property>
<property name="label" translatable="yes">Drawfile...</property>
<property name="use_underline">True</property>
@@ -116,11 +118,21 @@
<child>
<widget class="GtkMenuItem" id="export_postscript">
<property name="visible">True</property>
+ <property name="sensitive">False</property>
<property name="tooltip" translatable="yes">PostScript for printing and converting to PDFs.</property>
<property name="label" translatable="yes">PostScript...</property>
<property name="use_underline">True</property>
</widget>
</child>
+ <child>
+ <widget class="GtkMenuItem" id="export_pdf">
+ <property name="visible">True</property>
+ <property name="sensitive">True</property>
+ <property name="tooltip" translatable="yes">Portable Document Format.</property>
+ <property name="label" translatable="yes">PDF...</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
</widget>
</child>
</widget>
@@ -133,10 +145,11 @@
<child>
<widget class="GtkImageMenuItem" id="print_preview">
<property name="visible">True</property>
- <property name="sensitive">False</property>
+ <property name="sensitive">True</property>
<property name="tooltip" translatable="yes">Show how a print out might look like.</property>
<property name="label" translatable="yes">Print preview...</property>
<property name="use_underline">True</property>
+ <accelerator key="P" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image558">
<property name="visible">True</property>
@@ -149,7 +162,7 @@
<child>
<widget class="GtkImageMenuItem" id="print">
<property name="visible">True</property>
- <property name="sensitive">False</property>
+ <property name="sensitive">True</property>
<property name="tooltip" translatable="yes">Produce a hardcopy on your printer.</property>
<property name="label" translatable="yes">Print...</property>
<property name="use_underline">True</property>
diff --git a/image/bmp.c b/image/bmp.c
index 74b03e77b..1025edd94 100644
--- a/image/bmp.c
+++ b/image/bmp.c
@@ -98,7 +98,7 @@ bool nsbmp_redraw(struct content *c, int x, int y,
if (!c->data.bmp.bmp->decoded)
bmp_decode(c->data.bmp.bmp);
c->bitmap = c->data.bmp.bmp->bitmap;
- return plot.bitmap(x, y, width, height, c->bitmap, background_colour);
+ return plot.bitmap(x, y, width, height, c->bitmap, background_colour, c);
}
@@ -112,7 +112,7 @@ bool nsbmp_redraw_tiled(struct content *c, int x, int y,
bmp_decode(c->data.bmp.bmp);
c->bitmap = c->data.bmp.bmp->bitmap;
return plot.bitmap_tile(x, y, width, height, c->bitmap,
- background_colour, repeat_x, repeat_y);
+ background_colour, repeat_x, repeat_y, c);
}
diff --git a/image/gif.c b/image/gif.c
index 02cb89bcb..52e057920 100644
--- a/image/gif.c
+++ b/image/gif.c
@@ -141,7 +141,7 @@ bool nsgif_redraw(struct content *c, int x, int y,
if (c->data.gif.current_frame != c->data.gif.gif->decoded_frame)
nsgif_get_frame(c);
c->bitmap = c->data.gif.gif->frame_image;
- return plot.bitmap(x, y, width, height, c->bitmap, background_colour);
+ return plot.bitmap(x, y, width, height, c->bitmap, background_colour, c);
}
@@ -155,7 +155,7 @@ bool nsgif_redraw_tiled(struct content *c, int x, int y,
nsgif_get_frame(c);
c->bitmap = c->data.gif.gif->frame_image;
return plot.bitmap_tile(x, y, width, height, c->bitmap, background_colour,
- repeat_x, repeat_y);
+ repeat_x, repeat_y, c);
}
diff --git a/image/ico.c b/image/ico.c
index fa82c1619..bf2647710 100644
--- a/image/ico.c
+++ b/image/ico.c
@@ -101,7 +101,7 @@ bool nsico_redraw(struct content *c, int x, int y,
bmp_decode(bmp);
c->bitmap = bmp->bitmap;
return plot.bitmap(x, y, width, height, c->bitmap,
- background_colour);
+ background_colour, c);
}
@@ -115,7 +115,7 @@ bool nsico_redraw_tiled(struct content *c, int x, int y,
bmp_decode(bmp);
c->bitmap = bmp->bitmap;
return plot.bitmap_tile(x, y, width, height, c->bitmap,
- background_colour, repeat_x, repeat_y);
+ background_colour, repeat_x, repeat_y, c);
}
diff --git a/image/jpeg.c b/image/jpeg.c
index d4f5f537d..10ff5736e 100644
--- a/image/jpeg.c
+++ b/image/jpeg.c
@@ -235,7 +235,7 @@ bool nsjpeg_redraw(struct content *c, int x, int y,
float scale, unsigned long background_colour)
{
return plot.bitmap(x, y, width, height,
- c->bitmap, background_colour);
+ c->bitmap, background_colour, c);
}
@@ -251,7 +251,7 @@ bool nsjpeg_redraw_tiled(struct content *c, int x, int y,
{
return plot.bitmap_tile(x, y, width, height,
c->bitmap, background_colour,
- repeat_x, repeat_y);
+ repeat_x, repeat_y, c);
}
diff --git a/image/mng.c b/image/mng.c
index 7e92193cf..e4e375922 100644
--- a/image/mng.c
+++ b/image/mng.c
@@ -505,7 +505,7 @@ bool nsmng_redraw(struct content *c, int x, int y,
}
ret = plot.bitmap(x, y, width, height,
- c->bitmap, background_colour);
+ c->bitmap, background_colour, c);
/* Check if we need to restart the animation
*/
@@ -534,7 +534,7 @@ bool nsmng_redraw_tiled(struct content *c, int x, int y,
ret = plot.bitmap_tile(x, y, width, height,
c->bitmap, background_colour,
- repeat_x, repeat_y);
+ repeat_x, repeat_y, c);
/* Check if we need to restart the animation
*/
diff --git a/image/nssprite.c b/image/nssprite.c
index e7e40683b..ce472fea0 100644
--- a/image/nssprite.c
+++ b/image/nssprite.c
@@ -131,7 +131,7 @@ bool nssprite_redraw(struct content *c, int x, int y,
float scale, colour background_colour)
{
return plot.bitmap(x, y, width, height,
- c->bitmap, background_colour);
+ c->bitmap, background_colour, c);
}
#endif
diff --git a/image/rsvg.c b/image/rsvg.c
index bc483e8d3..c25870aa9 100644
--- a/image/rsvg.c
+++ b/image/rsvg.c
@@ -182,7 +182,7 @@ bool rsvg_redraw(struct content *c, int x, int y, int width, int height,
int clip_x0, int clip_y0, int clip_x1, int clip_y1,
float scale, unsigned long background_colour)
{
- plot.bitmap(x, y, width, height, c->bitmap, background_colour);
+ plot.bitmap(x, y, width, height, c->bitmap, background_colour, c);
return true;
}
diff --git a/pdf/TODO b/pdf/TODO
new file mode 100644
index 000000000..1b3a896b9
--- /dev/null
+++ b/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/pdf/font_haru.c b/pdf/font_haru.c
new file mode 100644
index 000000000..045d481a2
--- /dev/null
+++ b/pdf/font_haru.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+ /** \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.
+ */
+
+#define FONT_HARU_DEBUG
+
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "css/css.h"
+#include "hpdf.h"
+#include "render/font.h"
+#include "pdf/font_haru.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;
+
+ if (length == 0) {
+ *width = 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;
+ HPDF_REAL width;
+
+
+ if (!haru_nsfont_init(&pdf, &page, string, &string_nt, length))
+ return false;
+
+ if (!HPDF_Page_SetWidth(page, x)
+ || !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)
+ || !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
+
+ if (font != NULL) {
+ pdf_font = HPDF_GetFont(doc, font_name, "StandardEncoding");
+ if (pdf_font == NULL)
+ return false;
+ *font = pdf_font;
+ }
+ 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);
+
+ /*with 0.7 the pages look the best, this should be kept the same
+ as the scale in print settings*/
+ size = size / 0.7;
+
+ pdf_font = HPDF_GetFont(doc, font_name, "StandardEncoding");
+ if (pdf_font == NULL)
+ return false;
+ HPDF_Page_SetFontAndSize(page, pdf_font, size);
+ }
+
+ return true;
+}
diff --git a/pdf/font_haru.h b/pdf/font_haru.h
new file mode 100644
index 000000000..326f6497b
--- /dev/null
+++ b/pdf/font_haru.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+ /** \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;
+
+
+#endif
diff --git a/pdf/pdf_plotters.c b/pdf/pdf_plotters.c
new file mode 100644
index 000000000..50d7067eb
--- /dev/null
+++ b/pdf/pdf_plotters.c
@@ -0,0 +1,774 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+ /** \file
+ * Target independent PDF plotting using Haru Free PDF Library.
+ * Contains also the current solution for some text being cropped over page
+ * boundaries a 'fuzzy' bottom margin.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "hpdf.h"
+
+#include "desktop/plotters.h"
+#include "desktop/print.h"
+#include "desktop/printer.h"
+#include "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_flush(void);
+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 void pdf_page_apply_notext_clip(void);
+
+static HPDF_Image pdf_extract_image(struct bitmap *bitmap, struct content *content);
+
+static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no,
+ void*user_data);
+
+static void pdf_plot_grid(int x_dist,int y_dist,unsigned int colour);
+
+/*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;
+
+static struct print_settings* settings;
+/*this is added to the bottom margin as a place where text can be plotted
+ when it overflows just a little bit*/
+static float text_margin;
+
+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,
+ pdf_plot_flush,
+ pdf_plot_path
+};
+
+struct printer pdf_printer= {
+ &pdf_plotters,
+ pdf_begin,
+ pdf_next_page,
+ pdf_end
+};
+
+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
+ 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
+ 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;
+
+ 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 ; i<n ; i++) {
+ HPDF_Page_LineTo(pdf_page, p[i*2], page_height - p[i*2+1]);
+#ifdef PDF_DEBUG
+ pmaxx = max(pmaxx, p[i*2]);
+ pmaxy = max(pmaxy, p[i*2+1]);
+ pminx = min(pminx, p[i*2]);
+ pminy = min(pminy, p[i*2+1]);
+#endif
+ }
+
+#ifdef PDF_DEBUG
+ LOG(("%d %d %d %d %f", pminx, pminy, pmaxx, pmaxy, page_height-pminy));
+#endif
+
+ HPDF_Page_LineTo(pdf_page, p[0], page_height - p[1]);
+ HPDF_Page_Fill(pdf_page);
+
+ return true;
+}
+
+bool pdf_plot_fill(int x0, int y0, int x1, int y1, colour c){
+
+#ifdef PDF_DEBUG
+ LOG(("%d %d %d %d %f %X", x0, y0, x1, y1, page_height-y0, c));
+#endif
+
+ /*Normalize boundaries of the area - to prevent overflows.
+ It is needed only in a few functions, where integers are subtracted.
+ When the whole browser window is meant min and max int values are used
+ what must be handled in paged output.
+ */
+ x0 = min(max(x0, 0), page_width);
+ y0 = min(max(y0, 0), page_height);
+ x1 = min(max(x1, 0), page_width);
+ y1 = min(max(y1, 0), page_height);
+
+ HPDF_Page_SetRGBFill(pdf_page, R(c), G(c), B(c));
+ HPDF_Page_Rectangle(pdf_page, x0, page_height - y1, x1-x0, y1-y0);
+ HPDF_Page_Fill(pdf_page);
+
+ return true;
+}
+
+bool pdf_plot_clip(int clip_x0, int clip_y0,
+ int clip_x1, int clip_y1){
+
+#ifdef PDF_DEBUG
+ LOG(("%d %d %d %d", clip_x0, clip_y0, clip_x1, clip_y1));
+#endif
+
+ HPDF_Page_GRestore(pdf_page);
+ if (page_clipped)
+ HPDF_Page_GRestore(pdf_page);
+
+ /*Normalize cllipping area - to prevent overflows.
+ See comment in pdf_plot_fill.
+ */
+ clip_x0 = min(max(clip_x0, 0), page_width);
+ clip_y0 = min(max(clip_y0, 0), page_height);
+ clip_x1 = min(max(clip_x1, 0), page_width);
+ clip_y1 = min(max(clip_y1, 0), page_height);
+
+
+ HPDF_Page_GSave(pdf_page);
+ HPDF_Page_Rectangle(pdf_page, clip_x0, page_height-clip_y1,
+ clip_x1-clip_x0, clip_y1-clip_y0);
+ HPDF_Page_Clip(pdf_page);
+ HPDF_Page_EndPath(pdf_page);
+
+ pdf_page_apply_notext_clip();
+
+ page_clipped = true;
+
+ return true;
+}
+
+bool pdf_plot_text(int x, int y, const struct css_style *style,
+ const char *text, size_t length, colour bg, colour c){
+#ifdef PDF_DEBUG
+ LOG((". %d %d %s", x, y, text));
+#endif
+ char *word;
+ HPDF_REAL size;
+ bool fuzzy=false;
+ float text_bottom_position, descent;
+
+ if (length == 0)
+ return true;
+
+ 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);
+
+ 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;
+
+ if ( (size > y) && (y - descent <= text_margin) )
+ return true;
+
+ if (text_bottom_position < settings->margins[MARGINBOTTOM] + text_margin ) {
+ if ((text_bottom_position >= settings->margins[MARGINBOTTOM]) &&
+ (page_height - (y - size) >
+ settings->margins[MARGINBOTTOM] + text_margin)) {
+ fuzzy = true;
+ HPDF_Page_GRestore(pdf_page);
+ }
+ }
+
+ 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_BeginText(pdf_page);
+ HPDF_Page_SetFontAndSize (pdf_page, pdf_font, size);
+ HPDF_Page_TextOut (pdf_page, x, page_height - y, word);
+ HPDF_Page_EndText(pdf_page);
+
+ if (fuzzy)
+ pdf_page_apply_notext_clip();
+
+ free(word);
+
+ return true;
+}
+
+bool pdf_plot_disc(int x, int y, int radius, colour c, bool filled){
+#ifdef PDF_DEBUG
+ LOG(("."));
+#endif
+ 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;
+
+ 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;
+
+ 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;
+
+ 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,
+ page_height-current_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*/
+ /*TODO:something seems to be wrong with HPDF_LoadJpegImageFromMem
+ no embedding at all till I'll figure it out
+ */
+// case CONTENT_JPEG:
+// image = HPDF_LoadJpegImageFromMem(pdf_doc,
+// content->source_data,
+// content->total_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;*/
+ }
+ }
+
+ 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; i<img_height; i++)
+ for (j = 0 ; j<img_width ; j++) {
+ rgb_buffer[((i * img_width) + j) * 3] =
+ img_buffer[(i * img_rowstride) + (j * 4)];
+
+ rgb_buffer[(((i * img_width) + j) * 3) + 1] =
+ img_buffer[(i * img_rowstride) + (j * 4) + 1];
+
+ rgb_buffer[(((i * img_width) + j) * 3) + 2] =
+ img_buffer[(i * img_rowstride) + (j * 4) + 2];
+
+ alpha_buffer[(i * img_width)+j] =
+ img_buffer[(i * img_rowstride) + (j * 4) + 3];
+ }
+
+ smask = HPDF_LoadRawImageFromMem(pdf_doc, alpha_buffer,
+ img_width, img_height,
+ HPDF_CS_DEVICE_GRAY, 8);
+
+ image = HPDF_LoadRawImageFromMem(pdf_doc, rgb_buffer,
+ img_width, img_height,
+ HPDF_CS_DEVICE_RGB, 8);
+
+ if (HPDF_Image_AddSMask(pdf_doc, image,smask) != HPDF_OK)
+ image = NULL;
+
+ free(rgb_buffer);
+ free(alpha_buffer);
+ }
+
+ return image;
+}
+
+
+bool pdf_plot_flush(){
+ return true;
+}
+
+
+static inline float transform_x(float *transform,float x,float y){
+ return ((transform[0] * x) + (transform[2] * (-y) ) + transform[4]) * 2;
+}
+
+static inline float transform_y(float *transform,float x,float y){
+ return page_height - (((transform[1] * x) +
+ (transform[3] * (-y)) - transform[5]) * 2);
+}
+
+bool pdf_plot_path(float *p, unsigned int n, colour fill, float width,
+ colour c, float *transform){
+#ifdef PDF_DEBUG
+ LOG(("."));
+#endif
+ unsigned int i;
+ bool empty_path = true;
+
+ if (n == 0)
+ return true;
+
+ if ((c == TRANSPARENT) && (fill == TRANSPARENT))
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ return false;
+ }
+
+ HPDF_Page_SetRGBFill(pdf_page, R(fill), G(fill), B(fill));
+ HPDF_Page_SetRGBStroke(pdf_page, R(c), G(c), B(c));
+
+ transform[0] = 0.1;
+ transform[1] = 0;
+ transform[2] = 0;
+ transform[3] = -0.1;
+ transform[4] = 0;
+ transform[5] = 0;
+
+ for (i = 0 ; i<n ; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ HPDF_Page_MoveTo(pdf_page,
+ transform_x(transform, p[i+1], p[i+2]),
+ transform_y(transform, p[i+1], p[i+2]));
+ i+= 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ if (!empty_path)
+ HPDF_Page_ClosePath(pdf_page);
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ HPDF_Page_LineTo(pdf_page,
+ transform_x(transform, p[i+1], p[i+2]),
+ transform_y(transform, p[i+1], p[i+2]));
+ i+=3;
+ empty_path = false;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ HPDF_Page_CurveTo(pdf_page,
+ transform_x(transform, p[i+1], p[i+2]),
+ transform_y(transform, p[i+1], p[i+2]),
+ transform_x(transform, p[i+3], p[i+4]),
+ transform_y(transform, p[i+3], p[i+4]),
+ transform_x(transform, p[i+5], p[i+6]),
+ transform_y(transform, p[i+5], p[i+6]));
+ i += 7;
+ empty_path = false;
+ } else {
+ LOG(("bad path command %f", p[i]));
+ return false;
+ }
+ }
+
+ if (empty_path) {
+ HPDF_Page_EndPath(pdf_page);
+ return true;
+ }
+
+ if (fill!=TRANSPARENT) {
+ if (c != TRANSPARENT)
+ HPDF_Page_FillStroke(pdf_page);
+ else
+ HPDF_Page_Fill(pdf_page);
+ }
+ else
+ HPDF_Page_Stroke(pdf_page);
+
+ return true;
+}
+
+void pdf_set_solid()
+{
+ HPDF_Page_SetDash(pdf_page, NULL, 0, 0);
+}
+
+void pdf_set_dashed()
+{
+ HPDF_UINT16 dash_ptn[] = {3};
+ HPDF_Page_SetDash(pdf_page, dash_ptn, 1, 1);
+}
+
+void pdf_set_dotted()
+{
+ HPDF_UINT16 dash_ptn[] = {1};
+ HPDF_Page_SetDash(pdf_page, dash_ptn, 1, 1);
+}
+
+/**
+ * Begin pdf plotting - initialize a new document
+ * \param path Output file path
+ * \param pg_width page width
+ * \param pg_height page height
+ */
+bool pdf_begin(struct print_settings* print_settings)
+{
+
+ pdf_doc = NULL;
+
+ pdf_doc = HPDF_New(error_handler, NULL);
+ if (!pdf_doc) {
+ LOG(("Error creating pdf_doc"));
+ return false;
+ }
+
+ settings = print_settings;
+
+ page_width = settings->page_width - settings->margins[MARGINLEFT] -
+ settings->margins[MARGINRIGHT];
+
+ page_height = settings->page_height - settings->margins[MARGINTOP];
+
+ text_margin = settings->margins[MARGINTEXT];
+
+// 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()
+{
+#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);
+
+ pdf_page_apply_notext_clip();
+
+ page_clipped = false;
+
+#ifdef PDF_DEBUG
+ LOG(("%f %f", page_width, page_height));
+#endif
+
+ return true;
+}
+
+
+void pdf_end()
+{
+#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
+
+ /*TODO: if false notify user*/
+ if (settings->output)
+ HPDF_SaveToFile(pdf_doc, settings->output);
+
+
+ HPDF_Free(pdf_doc);
+
+#ifdef PDF_DEBUG
+ LOG(("pdf_end finishes"));
+#endif
+}
+
+
+/**
+ * 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
+
+/**
+ * A solution for fuzzy margins - saves the current clipping and puts the main
+ * clip frame (page without margins) over it.
+*/
+void pdf_page_apply_notext_clip()
+{
+ /*Save state underneath*/
+ HPDF_Page_GSave(pdf_page);
+
+ /*Apply no-text clipping (stadard page)*/
+ HPDF_Page_Rectangle(pdf_page,
+ 0,
+ text_margin + settings->margins[MARGINBOTTOM],
+ page_width,
+ page_height - settings->margins[MARGINTOP] - text_margin);
+
+ HPDF_Page_Clip(pdf_page);
+
+#ifdef PDF_DEBUG
+ HPDF_Page_Stroke(pdf_page);
+#else
+ HPDF_Page_EndPath(pdf_page);
+#endif
+}
+
+
diff --git a/pdf/pdf_plotters.h b/pdf/pdf_plotters.h
new file mode 100644
index 000000000..874518980
--- /dev/null
+++ b/pdf/pdf_plotters.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \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);
+
+#endif /*NETSURF_PDF_PLOTTERS_H*/
diff --git a/pdf/pdf_printer.h b/pdf/pdf_printer.h
new file mode 100644
index 000000000..a8d7d7466
--- /dev/null
+++ b/pdf/pdf_printer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * PDF Plotter and flow manipulating functions combined as a printer
+*/
+
+#ifndef NETSURF_PDF_PRINTER_H
+#define NETSURF_PDF_PRINTER_H
+
+#include "desktop/printer.h"
+#include "pdf/pdf_plotters.h"
+
+struct printer pdf_printer= {
+ &pdf_plotters,
+ pdf_begin,
+ pdf_next_page,
+ pdf_end
+};
+
+#endif
diff --git a/render/box.c b/render/box.c
index 2a29b06c9..e6495174e 100644
--- a/render/box.c
+++ b/render/box.c
@@ -33,7 +33,6 @@
#include "utils/log.h"
#include "utils/talloc.h"
-
static bool box_contains_point(struct box *box, int x, int y);
#define box_is_float(box) (box->type == BOX_FLOAT_LEFT || \
@@ -545,6 +544,10 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth)
fprintf(stream, "(%i %i %i %i) ",
box->descendant_x0, box->descendant_y0,
box->descendant_x1, box->descendant_y1);
+
+ fprintf(stream, "m(%i %i %i %i) ",
+ box->margin[TOP], box->margin[LEFT],
+ box->margin[BOTTOM], box->margin[RIGHT]);
switch (box->type) {
case BOX_BLOCK: fprintf(stream, "BLOCK "); break;
@@ -638,3 +641,201 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth)
box_dump(stream, c, depth + 1);
}
}
+
+/* Box tree duplication below
+*/
+
+/** structure for translating addresses in the box tree */
+struct box_dict_element{
+ struct box *old, *new;
+};
+
+static bool box_duplicate_main_tree(struct box *box, struct content *c,
+ int *count);
+static void box_duplicate_create_dict(struct box *old_box, struct box *new_box,
+ struct box_dict_element **dict);
+static void box_duplicate_update( struct box *box,
+ struct box_dict_element *dict,
+ int n);
+
+static int box_compare_dict_elements(const struct box_dict_element *a,
+ const struct box_dict_element *b);
+
+int box_compare_dict_elements(const struct box_dict_element *a,
+ const struct box_dict_element *b)
+{
+ return (a->old < b->old) ? -1 : ((a->old > b->old) ? 1 : 0);
+}
+
+/** Duplication of a box tree. We assume that all the content is fetched,
+fallbacks have been applied where necessary and we reuse a lot of content
+like strings, fetched objects etc - just replicating all we need to create
+two different layouts.
+\return true on success, false on lack of memory
+*/
+struct box* box_duplicate_tree(struct box *root, struct content *c)
+{
+ struct box *new_root;/**< Root of the new box tree*/
+ int box_number = 0;
+ struct box *old_addr, *new_addr;
+ struct box_dict_element *box_dict, *box_dict_end;
+
+ /* 1. Duplicate parent - children structure, list_markers*/
+ new_root = talloc_memdup(c, root, sizeof (struct box));
+ if (!box_duplicate_main_tree(new_root, c, &box_number))
+ return NULL;
+
+ /* 2. Create address translation dictionary*/
+ /*TODO: dont save unnecessary addresses*/
+
+ box_dict_end = box_dict = malloc(box_number *
+ sizeof(struct box_dict_element));
+
+ if (box_dict == NULL)
+ return NULL;
+ box_duplicate_create_dict(root, new_root, &box_dict_end);
+
+ assert((box_dict_end - box_dict) == box_number);
+
+ /*3. Sort it*/
+
+ qsort(box_dict, (box_dict_end - box_dict), sizeof(struct box_dict_element),
+ (int (*)(const void *, const void *))box_compare_dict_elements);
+
+ /* 4. Update inline_end and float_children pointers */
+
+ box_duplicate_update(new_root, box_dict, (box_dict_end - box_dict));
+
+ free(box_dict);
+
+ return new_root;
+}
+
+/**
+ * Recursively duplicates children of an element, and also if present - its
+ * list_marker, style and text.
+ * \param box Current box to duplicate its children
+ * \param c talloc memory pool
+ * \param count number of boxes seen so far
+ * \return true if successful, false otherwise (lack of memory)
+*/
+bool box_duplicate_main_tree(struct box *box, struct content *c, int *count)
+{
+
+ struct box *b, *prev, *copy;
+
+ prev = NULL;
+
+ for (b = box->children; b; b = b->next) {
+ /*Copy child*/
+ copy = talloc_memdup(c, b, sizeof (struct box));
+ if (copy == NULL)
+ return false;
+
+ copy->parent = box;
+
+ if (prev != NULL)
+ prev->next = copy;
+ else
+ box->children = copy;
+
+ /* Recursively visit child */
+ box_duplicate_main_tree(copy, c, count);
+
+ prev = copy;
+ }
+
+ box->last = prev;
+
+ if (box->list_marker) {
+ box->list_marker = talloc_memdup(c, box->list_marker,
+ sizeof *box->list_marker);
+ if (box->list_marker == NULL)
+ return false;
+ box->list_marker->parent = box;
+ }
+
+ if (box->text) {
+ box->text = talloc_memdup(c, box->text, box->length);
+ if (box->text == NULL)
+ return false;
+ }
+
+ if (box->style) {
+ box->style = talloc_memdup(c, box->style, sizeof *box->style);
+ if (box->style == NULL)
+ return false;
+ }
+
+ /*Make layout calculate the size of this element later
+ (might change because of font change etc.) */
+ box->width = UNKNOWN_WIDTH;
+ box->min_width = 0;
+ box->max_width = UNKNOWN_MAX_WIDTH;
+
+ (*count)++;
+
+ return true;
+}
+
+/**
+ * Recursively creates a dictionary of addresses - binding the address of a box
+ * with its copy.
+ * \param old_box original box
+ * \param new_box copy of the original box
+ * \param dict pointer to a pointer to the current position in the dictionary
+ */
+void box_duplicate_create_dict(struct box *old_box, struct box *new_box,
+ struct box_dict_element **dict)
+{
+ /**Children of the old and new boxes*/
+ struct box *b_old, *b_new;
+
+ for (b_old = old_box->children, b_new = new_box->children;
+ b_old != NULL && b_new != NULL;
+ b_old = b_old->next, b_new = b_new->next)
+ box_duplicate_create_dict(b_old, b_new, dict);
+
+ /*The new tree should be a exact copy*/
+ assert(b_old == NULL && b_new == NULL);
+
+ (*dict)->old = old_box;
+ (*dict)->new = new_box;
+ (*dict)++;
+}
+
+/**
+ * Recursively updates pointers in box tree.
+ * \param box current box in the new box tree
+ * \param box_dict box pointers dictionary
+ * \param n number of memory addresses in the dictionary
+ */
+void box_duplicate_update(struct box *box,
+ struct box_dict_element *box_dict,
+ int n)
+{
+ struct box_dict_element *box_dict_element;
+ struct box *b;
+ struct box_dict_element element;
+
+ for (b = box->children; b; b = b->next)
+ box_duplicate_update(b, box_dict, n);
+
+ if (box->float_children) {
+ element.old = box->float_children;
+ box_dict_element = bsearch(&element,
+ box_dict, n,
+ sizeof(struct box_dict_element),
+ (int (*)(const void *, const void *))box_compare_dict_elements);
+ box->float_children = box_dict_element->new;
+ }
+
+ if (box->next_float) {
+ element.old = box->next_float;
+ box_dict_element = bsearch(&element,
+ box_dict, n,
+ sizeof(struct box_dict_element),
+ (int (*)(const void *, const void *))box_compare_dict_elements);
+ box->next_float = box_dict_element->new;
+ }
+}
diff --git a/render/box.h b/render/box.h
index f4c9cfe2d..a670fddb5 100644
--- a/render/box.h
+++ b/render/box.h
@@ -311,4 +311,6 @@ bool xml_to_box(xmlNode *n, struct content *c);
bool box_normalise_block(struct box *block, struct content *c);
+struct box* box_duplicate_tree(struct box *root, struct content *c);
+
#endif
diff --git a/render/font.h b/render/font.h
index 1bb26e693..277ff810e 100644
--- a/render/font.h
+++ b/render/font.h
@@ -39,14 +39,19 @@
struct css_style;
-bool nsfont_width(const struct css_style *style,
- const char *string, size_t length,
- int *width);
-bool nsfont_position_in_string(const struct css_style *style,
- const char *string, size_t length,
- int x, size_t *char_offset, int *actual_x);
-bool nsfont_split(const struct css_style *style,
- const char *string, size_t length,
- int x, size_t *char_offset, int *actual_x);
+struct font_functions
+{
+ bool (*font_width)(const struct css_style *style,
+ const char *string, size_t length,
+ int *width);
+ bool (*font_position_in_string)(const struct css_style *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+ bool (*font_split)(const struct css_style *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+};
+
+extern const struct font_functions nsfont;
#endif
diff --git a/render/html.c b/render/html.c
index 59cbde11f..64d3e1386 100644
--- a/render/html.c
+++ b/render/html.c
@@ -122,6 +122,7 @@ bool html_create(struct content *c, const char *params[])
html->page = 0;
html->index = 0;
html->box = 0;
+ html->font_func = &nsfont;
for (i = 0; params[i]; i += 2) {
if (strcasecmp(params[i], "charset") == 0) {
diff --git a/render/html.h b/render/html.h
index af6a886bf..5851b83b8 100644
--- a/render/html.h
+++ b/render/html.h
@@ -130,6 +130,7 @@ struct content_html_data {
struct box *layout; /**< Box tree, or 0. */
colour background_colour; /**< Document background colour. */
+ const struct font_functions *font_func;
/** Number of entries in stylesheet_content. */
unsigned int stylesheet_count;
diff --git a/render/html_redraw.c b/render/html_redraw.c
index ae008f81f..5169faf4a 100644
--- a/render/html_redraw.c
+++ b/render/html_redraw.c
@@ -623,17 +623,17 @@ bool text_redraw(const char *utf8_text, size_t utf8_len,
endtxt_idx = utf8_len;
}
- if (!nsfont_width(style, utf8_text, start_idx, &startx))
+ if (!nsfont.font_width(style, utf8_text, start_idx, &startx))
startx = 0;
- if (!nsfont_width(style, utf8_text, endtxt_idx, &endx))
+ if (!nsfont.font_width(style, utf8_text, endtxt_idx, &endx))
endx = 0;
/* is there a trailing space that should be highlighted as well? */
if (end_idx > utf8_len) {
int spc_width;
/* \todo is there a more elegant/efficient solution? */
- if (nsfont_width(style, " ", 1, &spc_width))
+ if (nsfont.font_width(style, " ", 1, &spc_width))
endx += spc_width;
}
@@ -1175,7 +1175,7 @@ bool html_redraw_file(int x, int y, int width, int height,
text = messages_get("Form_Drop");
length = strlen(text);
- if (!nsfont_width(box->style, text, length, &text_width))
+ if (!nsfont.font_width(box->style, text, length, &text_width))
return false;
text_width *= scale;
if (width < text_width + 8)
diff --git a/render/layout.c b/render/layout.c
index 09eaac6a0..300513f66 100644
--- a/render/layout.c
+++ b/render/layout.c
@@ -55,7 +55,8 @@
#define AUTO INT_MIN
-static void layout_minmax_block(struct box *block);
+static void layout_minmax_block(struct box *block,
+ const struct font_functions *font_func);
static bool layout_block_object(struct box *block);
static void layout_block_find_dimensions(int available_width, struct box *box);
static bool layout_apply_minmax_height(struct box *box);
@@ -71,24 +72,28 @@ static void layout_find_dimensions(int available_width,
static int layout_clear(struct box *fl, css_clear clear);
static void find_sides(struct box *fl, int y0, int y1,
int *x0, int *x1, struct box **left, struct box **right);
-static void layout_minmax_inline_container(struct box *inline_container);
+static void layout_minmax_inline_container(struct box *inline_container,
+ const struct font_functions *font_func);
static int line_height(struct css_style *style);
static bool layout_line(struct box *first, int *width, int *y,
int cx, int cy, struct box *cont, bool indent,
bool has_text_children,
struct content *content, struct box **next_box);
-static struct box *layout_minmax_line(struct box *first, int *min, int *max);
+static struct box *layout_minmax_line(struct box *first, int *min, int *max,
+ const struct font_functions *font_func);
static int layout_text_indent(struct css_style *style, int width);
static bool layout_float(struct box *b, int width, struct content *content);
static void place_float_below(struct box *c, int width, int cx, int y,
struct box *cont);
static bool layout_table(struct box *box, int available_width,
struct content *content);
-static void layout_minmax_table(struct box *table);
+static void layout_minmax_table(struct box *table,
+ const struct font_functions *font_func);
static void layout_move_children(struct box *box, int x, int y);
static void calculate_mbp_width(struct css_style *style, unsigned int side,
int *fixed, float *frac);
-static void layout_lists(struct box *box);
+static void layout_lists(struct box *box,
+ const struct font_functions *font_func);
static void layout_position_relative(struct box *root);
static void layout_compute_relative_offset(struct box *box, int *x, int *y);
static bool layout_position_absolute(struct box *box,
@@ -116,10 +121,11 @@ bool layout_document(struct content *content, int width, int height)
{
bool ret;
struct box *doc = content->data.html.layout;
+ const struct font_functions *font_func = content->data.html.font_func;
assert(content->type == CONTENT_HTML);
- layout_minmax_block(doc);
+ layout_minmax_block(doc, font_func);
layout_block_find_dimensions(width, doc);
doc->x = doc->margin[LEFT] + doc->border[LEFT];
@@ -150,7 +156,7 @@ bool layout_document(struct content *content, int width, int height)
doc->children->margin[BOTTOM]);
}
- layout_lists(doc);
+ layout_lists(doc, font_func);
layout_position_absolute(doc, doc, 0, 0, content);
layout_position_relative(doc);
@@ -516,7 +522,7 @@ bool layout_block_context(struct box *block, struct content *content)
* 0 <= block->min_width <= block->max_width
*/
-void layout_minmax_block(struct box *block)
+void layout_minmax_block(struct box *block, const struct font_functions *font_func)
{
struct box *child;
int min = 0, max = 0;
@@ -545,7 +551,8 @@ void layout_minmax_block(struct box *block)
if (block->object) {
if (block->object->type == CONTENT_HTML) {
- layout_minmax_block(block->object->data.html.layout);
+ layout_minmax_block(block->object->data.html.layout,
+ font_func);
min = block->object->data.html.layout->min_width;
max = block->object->data.html.layout->max_width;
} else {
@@ -556,13 +563,14 @@ void layout_minmax_block(struct box *block)
for (child = block->children; child; child = child->next) {
switch (child->type) {
case BOX_BLOCK:
- layout_minmax_block(child);
+ layout_minmax_block(child, font_func);
break;
case BOX_INLINE_CONTAINER:
- layout_minmax_inline_container(child);
+ layout_minmax_inline_container(child,
+ font_func);
break;
case BOX_TABLE:
- layout_minmax_table(child);
+ layout_minmax_table(child, font_func);
break;
default:
assert(0);
@@ -1139,7 +1147,8 @@ bool layout_inline_container(struct box *inline_container, int width,
* 0 <= inline_container->min_width <= inline_container->max_width
*/
-void layout_minmax_inline_container(struct box *inline_container)
+void layout_minmax_inline_container(struct box *inline_container,
+ const struct font_functions *font_func)
{
struct box *child;
int line_min = 0, line_max = 0;
@@ -1152,7 +1161,9 @@ void layout_minmax_inline_container(struct box *inline_container)
return;
for (child = inline_container->children; child; ) {
- child = layout_minmax_line(child, &line_min, &line_max);
+ child = layout_minmax_line(child,
+ &line_min, &line_max,
+ font_func);
if (min < line_min)
min = line_min;
if (max < line_max)
@@ -1242,6 +1253,8 @@ bool layout_line(struct box *first, int *width, int *y,
struct css_length gadget_size; /* Checkbox / radio buttons */
gadget_size.unit = CSS_UNIT_EM;
gadget_size.value = 1;
+
+ const struct font_functions *font_func = content->data.html.font_func;
LOG(("first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i",
first, (int) first->length, first->text, *width,
@@ -1334,7 +1347,8 @@ bool layout_line(struct box *first, int *width, int *y,
b->width = 0;
if (b->space) {
/** \todo optimize out */
- nsfont_width(b->style, " ", 1, &space_after);
+ font_func->font_width(b->style, " ", 1,
+ &space_after);
} else {
space_after = 0;
}
@@ -1371,7 +1385,8 @@ bool layout_line(struct box *first, int *width, int *y,
data.select.items; o;
o = o->next) {
int opt_width;
- nsfont_width(b->style, o->text,
+ font_func->font_width(b->style,
+ o->text,
strlen(o->text),
&opt_width);
@@ -1381,7 +1396,7 @@ bool layout_line(struct box *first, int *width, int *y,
b->width = opt_maxwidth;
} else {
- nsfont_width(b->style, b->text,
+ font_func->font_width(b->style, b->text,
b->length, &b->width);
}
}
@@ -1389,7 +1404,8 @@ bool layout_line(struct box *first, int *width, int *y,
x += b->width;
if (b->space)
/** \todo optimize out */
- nsfont_width(b->style, " ", 1, &space_after);
+ font_func->font_width(b->style, " ", 1,
+ &space_after);
else
space_after = 0;
@@ -1529,7 +1545,7 @@ bool layout_line(struct box *first, int *width, int *y,
space_after = 0;
if (b->space)
/** \todo handle errors, optimize */
- nsfont_width(b->style, " ", 1,
+ font_func->font_width(b->style, " ", 1,
&space_after);
} else
space_after = 0;
@@ -1654,7 +1670,7 @@ bool layout_line(struct box *first, int *width, int *y,
w = split_box->width;
else
/** \todo handle errors */
- nsfont_width(split_box->style, split_box->text,
+ font_func->font_width(split_box->style, split_box->text,
space, &w);
LOG(("splitting: split_box %p \"%.*s\", space %zu, w %i, "
@@ -1725,7 +1741,7 @@ bool layout_line(struct box *first, int *width, int *y,
/* fit as many words as possible */
assert(space != 0);
/** \todo handle errors */
- nsfont_split(split_box->style,
+ font_func->font_split(split_box->style,
split_box->text, split_box->length,
x1 - x0 - x - space_before, &space, &w);
LOG(("'%.*s' %i %zu %i", (int) split_box->length,
@@ -1827,7 +1843,8 @@ bool layout_line(struct box *first, int *width, int *y,
*/
struct box *layout_minmax_line(struct box *first,
- int *line_min, int *line_max)
+ int *line_min, int *line_max,
+ const struct font_functions *font_func)
{
int min = 0, max = 0, width, height, fixed;
float frac;
@@ -1855,9 +1872,9 @@ struct box *layout_minmax_line(struct box *first,
if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) {
assert(b->children);
if (b->children->type == BOX_BLOCK)
- layout_minmax_block(b->children);
+ layout_minmax_block(b->children, font_func);
else
- layout_minmax_table(b->children);
+ layout_minmax_table(b->children, font_func);
b->min_width = b->children->min_width;
b->max_width = b->children->max_width;
if (min < b->min_width)
@@ -1867,7 +1884,7 @@ struct box *layout_minmax_line(struct box *first,
}
if (b->type == BOX_INLINE_BLOCK) {
- layout_minmax_block(b);
+ layout_minmax_block(b, font_func);
if (min < b->min_width)
min = b->min_width;
max += b->max_width;
@@ -1890,7 +1907,7 @@ struct box *layout_minmax_line(struct box *first,
if (0 < fixed)
max += fixed;
if (b->next && b->space) {
- nsfont_width(b->style, " ", 1, &width);
+ font_func->font_width(b->style, " ", 1, &width);
max += width;
}
continue;
@@ -1916,7 +1933,8 @@ struct box *layout_minmax_line(struct box *first,
data.select.items; o;
o = o->next) {
int opt_width;
- nsfont_width(b->style, o->text,
+ font_func->font_width(b->style,
+ o->text,
strlen(o->text),
&opt_width);
@@ -1926,13 +1944,13 @@ struct box *layout_minmax_line(struct box *first,
b->width = opt_maxwidth;
} else {
- nsfont_width(b->style, b->text,
+ font_func->font_width(b->style, b->text,
b->length, &b->width);
}
}
max += b->width;
if (b->next && b->space) {
- nsfont_width(b->style, " ", 1, &width);
+ font_func->font_width(b->style, " ", 1, &width);
max += width;
}
@@ -1942,7 +1960,7 @@ struct box *layout_minmax_line(struct box *first,
for (j = i; j != b->length &&
b->text[j] != ' '; j++)
;
- nsfont_width(b->style, b->text + i,
+ font_func->font_width(b->style, b->text + i,
j - i, &width);
if (min < width)
min = width;
@@ -2560,7 +2578,8 @@ bool layout_table(struct box *table, int available_width,
* 0 <= table->min_width <= table->max_width
*/
-void layout_minmax_table(struct box *table)
+void layout_minmax_table(struct box *table,
+ const struct font_functions *font_func)
{
unsigned int i, j;
int border_spacing_h = 0;
@@ -2597,7 +2616,7 @@ void layout_minmax_table(struct box *table)
if (cell->columns != 1)
continue;
- layout_minmax_block(cell);
+ layout_minmax_block(cell, font_func);
i = cell->start_column;
if (col[i].positioned)
@@ -2620,7 +2639,7 @@ void layout_minmax_table(struct box *table)
if (cell->columns == 1)
continue;
- layout_minmax_block(cell);
+ layout_minmax_block(cell, font_func);
i = cell->start_column;
/* find min width so far of spanned columns, and count
@@ -2766,7 +2785,8 @@ void calculate_mbp_width(struct css_style *style, unsigned int side,
* Layout list markers.
*/
-void layout_lists(struct box *box)
+void layout_lists(struct box *box,
+ const struct font_functions *font_func)
{
struct box *child;
struct box *marker;
@@ -2782,7 +2802,7 @@ void layout_lists(struct box *box)
marker->height) / 2;
} else if (marker->text) {
if (marker->width == UNKNOWN_WIDTH)
- nsfont_width(marker->style,
+ font_func->font_width(marker->style,
marker->text,
marker->length,
&marker->width);
@@ -2798,7 +2818,7 @@ void layout_lists(struct box *box)
/* Gap between marker and content */
marker->x -= 4;
}
- layout_lists(child);
+ layout_lists(child, font_func);
}
}
diff --git a/render/loosen.c b/render/loosen.c
new file mode 100644
index 000000000..91c4056ef
--- /dev/null
+++ b/render/loosen.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <assert.h>
+
+#include "content/content.h"
+
+#include "render/box.h"
+#include "render/font.h"
+
+#include "utils/log.h"
+#include "utils/talloc.h"
+
+#define AUTO INT_MIN
+
+
+static bool loosen_text(struct box *text, int width, struct content *content);
+
+static bool loosen_table(struct box *box, int available_width,
+ struct content *content);
+
+static bool loosen_position_static(struct box *box, int width, int cx,
+ struct content *content);
+
+static bool loosen_shrink_object(struct box *box, int width);
+
+static bool loosen_all_first_pass(struct box *box, int width, int cx,
+ struct content *content);
+static bool loosen_all_second_pass(struct box *box, int width, int cx,
+ struct content *content);
+static bool loosen_all_third_pass(struct box *box, int width, int cx,
+ struct content *content);
+
+/**
+ * Main loosing procedure
+ * \param content Reformated content - talloc memory pool for new boxes
+ * \param layout Root of the loosened box tree
+ * \param width Width the content is intended to fit
+ * \param height Height of a single page - to be taken into consideration for \
+ * preventing elements for being cropped at top/bottom edges of pages.
+ * \return true if successful, false otherwise (lack of memory)
+*/
+bool loosen_document_layout(struct content *content, struct box *layout,
+ int width, int height)
+{
+ /* Optional try - if the current layout is not more than xx% too wide,
+ * maybe we scale the content to preserve the original layout?
+ */
+
+ if (!loosen_all_first_pass(layout, width, 0, content))
+ return false;
+ layout->min_width = 0;
+ layout->max_width = UNKNOWN_MAX_WIDTH;
+ content_reformat(content, width, 0);
+
+ /*Check if pass 1 was enough - if re-layouting doesn't give
+ *us the right width, go on to pass 2. And again - if pass 2 was not
+ *enough - go on to pass 3
+ */
+
+ if (content->width > width) {
+ if (!loosen_all_second_pass(layout, width, 0, content))
+ return false;
+ layout->min_width = 0;
+ layout->max_width = UNKNOWN_MAX_WIDTH;
+ content_reformat(content, width, 0);
+ }
+
+ if (content->width > width) {
+ if (!loosen_all_third_pass(layout, width, 0, content))
+ return false;
+ layout->min_width = 0;
+ layout->max_width = UNKNOWN_MAX_WIDTH;
+ content_reformat(content, width, 0);
+ }
+
+ return true;
+}
+
+/** Primarily - break too wide words into pieces.
+ * \param text - the box that contains text to be broken
+ * \param width Width the content is intended to fit
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+*/
+bool loosen_text(struct box *text, int width, struct content *content)
+{
+ unsigned int offset;
+ int actual_x;
+
+ int *breaks;
+ int break_count;
+
+ int position, i;
+ const struct font_functions *font_func;
+
+ if (content->type == CONTENT_HTML)
+ font_func = content->data.html.font_func;
+ else
+ return false;
+
+ if (text->width <= width) {
+ LOG(("loosen_text called unnecessary?"));
+ /*Still - not an error for this function*/
+ return true;
+ }
+
+ breaks = malloc( sizeof(int) * text->length);
+ if (breaks == NULL)
+ return false;
+
+ break_count = 0;
+ position = 0;
+
+ while (position < text->length) {
+ font_func->font_position_in_string(text->style,
+ text->text + position,
+ text->length - position,
+ width, &offset, &actual_x);
+
+ if (offset < text->length - position) {
+ /*Another break*/
+ LOG(("Current text broken at offset %d",
+ position + offset));
+ breaks[break_count++] = position + offset-1;
+ }
+
+ position += offset;
+ }
+
+ text->text = talloc_realloc(content, text->text, char,
+ text->length + break_count);
+
+ i = text->length-1;
+ text->length = text->length + break_count;
+
+ for (; i>=0; i--) {
+ text->text[i + break_count] = text->text[i];
+ if (i == breaks[break_count - 1]) {
+ break_count--;
+ text->text[i + break_count] = ' ';
+ }
+ }
+
+ free(breaks);
+
+ return true;
+}
+
+/**
+ * Changing table layout and structure to fit the contents width.
+ * In the most extreme case - the table has no influence on the width
+ * (each row is broken into one-cell rows).
+ * \param table - the box that contains table to be broken
+ * \param width Width the content is intended to fit
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_table(struct box *table, int width, struct content *content)
+{
+
+ struct box *row_group, *row, *cell, *br, *prev, *inline_container;
+ unsigned int row_sum;
+ bool first_cell_in_row;
+
+ if (table->min_width <= width)
+ return true;
+
+ inline_container = box_create(0, 0, 0, 0, 0, content);
+ inline_container->type = BOX_INLINE_CONTAINER;
+ inline_container->parent = table;
+ inline_container->style = talloc_memdup(content, table->style,
+ sizeof *table->style);
+
+ prev = NULL;
+
+ for (row_group = table->children; row_group;
+ row_group = row_group->next) {
+ for (row = row_group->children; row; row = row->next) {
+
+ for (cell = row->children; cell; cell = cell->next) {
+ cell->type = BOX_INLINE_BLOCK;
+ cell->prev = prev;
+ cell->parent = inline_container;
+ cell->max_width = width;
+ cell->min_width = 0;
+
+ if (prev!=NULL)
+ prev->next = cell;
+ else
+ inline_container->children = cell;
+
+ prev = cell;
+ }
+
+ br = box_create(0, 0, 0, 0, 0, content);
+ br->type = BOX_BR;
+ br->parent = inline_container;
+ br->prev = prev;
+ br->style = talloc_memdup(content, table->style,
+ sizeof *table->style);
+ br->style->clear = CSS_CLEAR_BOTH;
+
+ if (prev != NULL)
+ prev->next = br;
+ else
+ inline_container->children = br;
+
+ prev = br;
+ }
+ }
+ inline_container->last = prev;
+
+ table->type = BOX_BLOCK;
+ table->children = table->last = inline_container;
+ table->col = NULL;
+
+ return true;
+}
+
+
+/**
+ * Change absolute and relative positioned elements into block elements
+ * in case they are positioned to far to the rigth
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_position_static(struct box *box, int width, int cx,
+ struct content *content)
+{
+ assert(box->style);
+
+ if (box->style->position == CSS_POSITION_ABSOLUTE) {
+ box->style->position = CSS_POSITION_NOT_SET;
+ }
+
+ return true;
+}
+
+/**
+ * Shrink an object (esp. an image) to fit the page-width
+ * \note Not sure wheter it won't be better for images to be cropped
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \return true if successful, false otherwise
+*/
+bool loosen_shrink_object(struct box *box, int width)
+{
+ assert(box->object != NULL);
+
+ box->height = AUTO;
+ box->width = width;
+
+ if (box->style) {
+ box->style->width.width = CSS_WIDTH_PERCENT;
+ box->style->width.value.percent = 100;
+ box->style->height.height= CSS_HEIGHT_AUTO;
+ }
+
+ return true;
+}
+
+/**
+ * Pass 1 of loosening - do such obvious changes as: breaking too long words,
+ * moving absolute positioned objects into the visibile scope of width.
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+*/
+bool loosen_all_first_pass(struct box *box, int width, int cx,
+ struct content *content)
+{
+ struct box* c;
+ int got_width;
+ int x;
+
+ for (c = box->children; c ; c = c->next) {
+ x = cx + c->x;
+ if (c->children != NULL)
+ if (!loosen_all_first_pass(c, width, x, content))
+ return false;
+
+ if (c->style) {
+ if (c->style->position == CSS_POSITION_RELATIVE ||
+ c->style->position == CSS_POSITION_ABSOLUTE )
+ if (!loosen_position_static(c, width, cx, content))
+ return false;
+ if ( c->style->width.width == CSS_WIDTH_LENGTH &&
+ css_len2px(&c->style->width.value.length, c->style) > width)
+ c->style->width.width = CSS_WIDTH_NOT_SET;
+ }
+
+ if (c->object && c->width > width)
+ if (!loosen_shrink_object(c, width))
+ return false;
+
+ switch (c->type) {
+ case BOX_TEXT:
+ if (!loosen_text(c, width, content))
+ return false;
+ break;
+ }
+
+ c->min_width = 0;
+ c->max_width = UNKNOWN_MAX_WIDTH;
+
+ }
+
+ return true;
+}
+
+/**
+ * Pass 2 of loosening - break tables
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_all_second_pass(struct box *box, int width, int cx,
+ struct content *content)
+{
+ struct box *c;
+ int got_width;
+ int x;
+
+ for (c = box->children; c; c = c->next) {
+ x = cx + c->x;
+ if (c->children != NULL)
+ if (!loosen_all_second_pass(c, width, x, content))
+ return false;
+
+ switch (c->type) {
+ case BOX_TABLE:
+ if (!loosen_table(c, width, content))
+ return false;
+ break;
+ }
+
+ c->min_width = 0;
+ c->max_width = UNKNOWN_MAX_WIDTH;
+ }
+
+ return true;
+}
+
+
+/**
+ * Pass 3 of loosening -zero all margins and paddings
+ * \param box - the box that should be changed
+ * \param width Width the content is intended to fit
+ * \param cx current x - not yet in use
+ * \param content talloc memory pool for new boxes
+ * \return true if successful, false otherwise
+ */
+bool loosen_all_third_pass(struct box *box, int width, int cx,
+ struct content *content)
+{
+ struct box *c;
+ int got_width;
+ int x;
+
+ for (c = box->children; c; c = c->next) {
+ x = cx + c->x;
+ if (c->children != NULL)
+ if (!loosen_all_third_pass(c, width, x, content))
+ return false;
+
+ c->padding[LEFT] = c->padding[RIGHT] = 0;
+ c->margin[LEFT] = c->margin[RIGHT] = 0;
+
+ if (c->style) {
+ c->style->margin[LEFT].margin = CSS_MARGIN_PERCENT;
+ c->style->margin[LEFT].value.percent = 0;
+
+ c->style->margin[RIGHT].margin = CSS_MARGIN_PERCENT;
+ c->style->margin[RIGHT].value.percent = 0;
+
+ c->style->padding[LEFT].padding = CSS_PADDING_PERCENT;
+ c->style->padding[LEFT].value.percent = 0;
+
+ c->style->padding[RIGHT].padding = CSS_PADDING_PERCENT;
+ c->style->padding[RIGHT].value.percent = 0;
+
+ }
+
+ c->min_width = 0;
+ c->max_width = UNKNOWN_MAX_WIDTH;
+
+ }
+
+ return true;
+}
+
diff --git a/render/loosen.h b/render/loosen.h
new file mode 100644
index 000000000..a5b3822bd
--- /dev/null
+++ b/render/loosen.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+\file
+General idea - a set of routines working themselves recursively through
+the box tree and trying to change the layout of the document as little
+as possible to acquire the desired width ( - to make it fit in a printed
+page ), where possible - also taking the dividing height into consideration,
+to prevent objects being cut by ends of pages.
+*/
+
+#ifndef NETSURF_RENDER_LOOSEN_H
+#define NETSURF_RENDER_LOOSEN_H
+#include <stdbool.h>
+
+bool loosen_document_layout(struct content *content, struct box *layout,
+ int width, int height);
+
+#endif
diff --git a/render/textplain.c b/render/textplain.c
index 63610ba17..a16f834c2 100644
--- a/render/textplain.c
+++ b/render/textplain.c
@@ -213,7 +213,7 @@ void textplain_reformat(struct content *c, int width, int height)
/* compute available columns (assuming monospaced font) - use 8
* characters for better accuracy */
- if (!nsfont_width(&textplain_style, "ABCDEFGH", 8, &character_width))
+ if (!nsfont.font_width(&textplain_style, "ABCDEFGH", 8, &character_width))
return;
columns = (width - MARGIN - MARGIN) * 8 / character_width;
textplain_tab_width = (TAB_WIDTH * character_width) / 8;
@@ -402,7 +402,7 @@ bool textplain_redraw(struct content *c, int x, int y,
break;
/* locate end of string and align to next tab position */
- if (nsfont_width(&textplain_style, &text[offset],
+ if (nsfont.font_width(&textplain_style, &text[offset],
next_offset - offset, &width))
tx += (int)(width * scale);
@@ -499,13 +499,13 @@ size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir)
next_offset = utf8_next(text, length, next_offset);
if (next_offset < length)
- nsfont_width(&textplain_style, text, next_offset, &width);
+ nsfont.font_width(&textplain_style, text, next_offset, &width);
if (x <= width) {
int pixel_offset;
size_t char_offset;
- nsfont_position_in_string(&textplain_style,
+ nsfont.font_position_in_string(&textplain_style,
text, next_offset, x,
&char_offset, &pixel_offset);
@@ -584,7 +584,7 @@ int textplain_coord_from_offset(const char *text, size_t offset, size_t length)
while (next_offset < offset && text[next_offset] != '\t')
next_offset = utf8_next(text, length, next_offset);
- nsfont_width(&textplain_style, text, next_offset, &tx);
+ nsfont.font_width(&textplain_style, text, next_offset, &tx);
x += tx;
if (next_offset >= offset)
diff --git a/riscos/font.c b/riscos/font.c
index d320b2070..54a985ae0 100644
--- a/riscos/font.c
+++ b/riscos/font.c
@@ -35,20 +35,32 @@
#include "utils/messages.h"
#include "utils/utils.h"
-
-/** desktop font, size and style being used */
-char ro_gui_desktop_font_family[80];
-int ro_gui_desktop_font_size = 12;
-rufl_style ro_gui_desktop_font_style = rufl_WEIGHT_400;
-
-
static void nsfont_check_option(char **option, const char *family,
const char *fallback);
static int nsfont_list_cmp(const void *keyval, const void *datum);
static void nsfont_check_fonts(void);
static void ro_gui_wimp_desktop_font(char *family, size_t bufsize, int *psize,
rufl_style *pstyle);
+static bool nsfont_width(const struct css_style *style,
+ const char *string, size_t length,
+ int *width);
+static bool 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 nsfont_split(const struct css_style *style,
+ const char *string, size_t length,
+ int x, size_t *char_offset, int *actual_x);
+
+/** desktop font, size and style being used */
+char ro_gui_desktop_font_family[80];
+int ro_gui_desktop_font_size = 12;
+rufl_style ro_gui_desktop_font_style = rufl_WEIGHT_400;
+const struct font_functions nsfont = {
+ nsfont_width,
+ nsfont_position_in_string,
+ nsfont_split
+};
/**
* Initialize font handling.
diff --git a/riscos/menus.c b/riscos/menus.c
index adfb9a109..bee1890c4 100644
--- a/riscos/menus.c
+++ b/riscos/menus.c
@@ -182,9 +182,16 @@ void ro_gui_menu_init(void)
{ "Page", BROWSER_PAGE, 0 },
{ "Page.PageInfo",BROWSER_PAGE_INFO, dialog_pageinfo },
{ "Page.Save", BROWSER_SAVE, dialog_saveas },
+#ifdef WITH_SAVE_COMPLETE
{ "Page.SaveComp", BROWSER_SAVE_COMPLETE, dialog_saveas },
+#endif
{ "Page.Export", NO_ACTION, 0 },
+#ifdef WITH_DRAW_EXPORT
{ "Page.Export.Draw", BROWSER_EXPORT_DRAW, dialog_saveas },
+#endif
+#ifdef WITH_PDF_EXPORT
+ { "Page.Export.PDF", BROWSER_EXPORT_PDF, dialog_saveas },
+#endif
{ "Page.Export.Text", BROWSER_EXPORT_TEXT, dialog_saveas },
{ "Page.SaveURL", NO_ACTION, 0 },
{ "Page.SaveURL.URI", BROWSER_SAVE_URL_URI, dialog_saveas },
@@ -1520,15 +1527,18 @@ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action,
case BROWSER_OBJECT_SAVE_URL_TEXT:
c = current_menu_object_box ?
current_menu_object_box->object : NULL;
+ /* Fall through */
case BROWSER_SAVE:
case BROWSER_SAVE_COMPLETE:
case BROWSER_EXPORT_DRAW:
+ case BROWSER_EXPORT_PDF:
case BROWSER_EXPORT_TEXT:
case BROWSER_SAVE_URL_URI:
case BROWSER_SAVE_URL_URL:
case BROWSER_SAVE_URL_TEXT:
if (!c)
return false;
+ /* Fall through */
case HOTLIST_EXPORT:
case HISTORY_EXPORT:
ro_gui_menu_prepare_action(owner, action, true);
@@ -1872,6 +1882,7 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action,
if ((windows) && (current_menu_object_box))
ro_gui_menu_prepare_objectinfo(
current_menu_object_box);
+ /* Fall through */
case BROWSER_OBJECT_RELOAD:
ro_gui_menu_set_entry_shaded(current_menu, action,
!current_menu_object_box);
@@ -1917,6 +1928,12 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action,
if ((c) && (windows))
ro_gui_save_prepare(GUI_SAVE_DRAW, c);
break;
+ case BROWSER_EXPORT_PDF:
+ ro_gui_menu_set_entry_shaded(current_menu,
+ action, !c);
+ if ((c) && (windows))
+ ro_gui_save_prepare(GUI_SAVE_PDF, c);
+ break;
case BROWSER_EXPORT_TEXT:
ro_gui_menu_set_entry_shaded(current_menu,
action, !c);
@@ -1926,6 +1943,7 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action,
case BROWSER_OBJECT_SAVE_URL_URI:
c = current_menu_object_box ?
current_menu_object_box->object : NULL;
+ /* Fall through */
case BROWSER_SAVE_URL_URI:
ro_gui_menu_set_entry_shaded(current_menu,
action, !c);
@@ -1935,6 +1953,7 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action,
case BROWSER_OBJECT_SAVE_URL_URL:
c = current_menu_object_box ?
current_menu_object_box->object : NULL;
+ /* Fall through */
case BROWSER_SAVE_URL_URL:
ro_gui_menu_set_entry_shaded(current_menu,
action, !c);
@@ -1944,6 +1963,7 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action,
case BROWSER_OBJECT_SAVE_URL_TEXT:
c = current_menu_object_box ?
current_menu_object_box->object : NULL;
+ /* Fall through */
case BROWSER_SAVE_URL_TEXT:
ro_gui_menu_set_entry_shaded(current_menu,
action, !c);
diff --git a/riscos/menus.h b/riscos/menus.h
index 8e7708413..edc439346 100644
--- a/riscos/menus.h
+++ b/riscos/menus.h
@@ -76,6 +76,7 @@ typedef enum {
BROWSER_SAVE,
BROWSER_SAVE_COMPLETE,
BROWSER_EXPORT_DRAW,
+ BROWSER_EXPORT_PDF,
BROWSER_EXPORT_TEXT,
BROWSER_SAVE_URL_URI,
BROWSER_SAVE_URL_URL,
diff --git a/riscos/plotters.c b/riscos/plotters.c
index 3650e2246..512e0572e 100644
--- a/riscos/plotters.c
+++ b/riscos/plotters.c
@@ -52,10 +52,10 @@ static bool ro_plot_disc(int x, int y, int radius, colour colour, bool filled);
static bool ro_plot_arc(int x, int y, int radius, int angle1, int angle2,
colour c);
static bool ro_plot_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg);
+ struct bitmap *bitmap, colour bg, struct content *content);
static bool ro_plot_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y);
+ bool repeat_x, bool repeat_y, struct content *content);
struct plotter_table plot;
@@ -496,7 +496,7 @@ bool ro_plot_arc(int x, int y, int radius, int angle1, int angle2, colour c)
}
bool ro_plot_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg)
+ struct bitmap *bitmap, colour bg, struct content *content)
{
char *buffer;
@@ -521,7 +521,7 @@ bool ro_plot_bitmap(int x, int y, int width, int height,
bool ro_plot_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y)
+ bool repeat_x, bool repeat_y, struct content *content)
{
char *buffer;
diff --git a/riscos/save.c b/riscos/save.c
index b701bd1d1..549b9e003 100644
--- a/riscos/save.c
+++ b/riscos/save.c
@@ -28,13 +28,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "oslib/dragasprite.h"
-#include "oslib/osbyte.h"
-#include "oslib/osfile.h"
-#include "oslib/osmodule.h"
+#include <oslib/dragasprite.h>
+#include <oslib/osbyte.h>
+#include <oslib/osfile.h>
+#include <oslib/osmodule.h>
#include <oslib/osspriteop.h>
-#include "oslib/wimp.h"
-#include "oslib/wimpspriteop.h"
+#include <oslib/wimp.h>
+#include <oslib/wimpspriteop.h>
#include "desktop/netsurf.h"
#include "desktop/save_text.h"
#include "desktop/selection.h"
@@ -47,6 +47,7 @@
#include "riscos/save.h"
#include "riscos/save_complete.h"
#include "riscos/save_draw.h"
+#include "riscos/save_pdf.h"
#include "riscos/textselection.h"
#include "riscos/thumbnail.h"
#include "riscos/wimp.h"
@@ -92,10 +93,12 @@ struct gui_save_table_entry {
/** Table of filetypes and default filenames. Must be in sync with
* gui_save_type (riscos/gui.h). A filetype of 0 indicates the content should
- * be used. */
-struct gui_save_table_entry gui_save_table[] = {
+ * be used.
+ */
+static const struct gui_save_table_entry gui_save_table[] = {
/* GUI_SAVE_SOURCE, */ { 0, "SaveSource" },
/* GUI_SAVE_DRAW, */ { 0xaff, "SaveDraw" },
+ /* GUI_SAVE_PDF, */ { 0xadf, "SavePDF" },
/* GUI_SAVE_TEXT, */ { 0xfff, "SaveText" },
/* GUI_SAVE_COMPLETE, */ { 0xfaf, "SaveComplete" },
/* GUI_SAVE_OBJECT_ORIG, */ { 0, "SaveObject" },
@@ -633,7 +636,7 @@ void ro_gui_save_datasave_ack(wimp_message *message)
/**
* Does the actual saving
*
- * \param c content to save (or 0 for other)
+ * \param c content to save (or NULL for other)
* \param path path to save as
* \return true on success, false on error and error reported
*/
@@ -647,6 +650,10 @@ bool ro_gui_save_content(struct content *c, char *path)
case GUI_SAVE_DRAW:
return save_as_draw(c, path);
#endif
+#ifdef WITH_PDF_EXPORT
+ case GUI_SAVE_PDF:
+ return save_as_pdf(c, path);
+#endif
case GUI_SAVE_TEXT:
save_as_text(c, path);
xosfile_set_type(path, 0xfff);
diff --git a/riscos/save_draw.c b/riscos/save_draw.c
index 5df1c0089..1af1db6f9 100644
--- a/riscos/save_draw.c
+++ b/riscos/save_draw.c
@@ -54,10 +54,10 @@ static bool ro_save_draw_disc(int x, int y, int radius, colour colour,
static bool ro_save_draw_arc(int x, int y, int radius, int angle1, int angle2,
colour c);
static bool ro_save_draw_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg);
+ struct bitmap *bitmap, colour bg, struct content *content);
static bool ro_save_draw_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y);
+ bool repeat_x, bool repeat_y, struct content *content);
static bool ro_save_draw_group_start(const char *name);
static bool ro_save_draw_group_end(void);
static bool ro_save_draw_error(pencil_code code);
@@ -377,7 +377,7 @@ bool ro_save_draw_arc(int x, int y, int radius, int angle1, int angle2,
}
bool ro_save_draw_bitmap(int x, int y, int width, int height,
- struct bitmap *bitmap, colour bg)
+ struct bitmap *bitmap, colour bg, struct content *content)
{
pencil_code code;
char *buffer;
@@ -401,7 +401,7 @@ bool ro_save_draw_bitmap(int x, int y, int width, int height,
bool ro_save_draw_bitmap_tile(int x, int y, int width, int height,
struct bitmap *bitmap, colour bg,
- bool repeat_x, bool repeat_y)
+ bool repeat_x, bool repeat_y, struct content *content)
{
return true;
}
diff --git a/riscos/save_pdf.c b/riscos/save_pdf.c
new file mode 100644
index 000000000..a5d2b010a
--- /dev/null
+++ b/riscos/save_pdf.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008 John Tytgat <John.Tytgat@aaug.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Export a content as a PDF file (implementation).
+ */
+
+#include <oslib/osfile.h>
+#include "content/content.h"
+#include "desktop/print.h"
+#include "pdf/pdf_plotters.h"
+#include "riscos/save_pdf.h"
+#include "utils/log.h"
+#include "utils/config.h"
+
+#ifdef WITH_PDF_EXPORT
+
+/**
+ * Export a content as a PDF file.
+ *
+ * \param c content to export
+ * \param path path to save PDF as
+ * \return true on success, false on error and error reported
+ */
+bool save_as_pdf(struct content *c, const char *path)
+{
+ struct print_settings *psettings;
+
+ psettings = print_make_settings(DEFAULT);
+ if (psettings == NULL)
+ return false;
+
+ psettings->output = path;
+ if (!print_basic_run(c, &pdf_printer, psettings))
+ return false;
+ xosfile_set_type(path, 0xadf);
+ return true;
+}
+
+#endif
diff --git a/riscos/save_pdf.h b/riscos/save_pdf.h
new file mode 100644
index 000000000..69f998764
--- /dev/null
+++ b/riscos/save_pdf.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008 John Tytgat <John.Tytgat@aaug.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_RISCOS_SAVE_PDF_H_
+#define _NETSURF_RISCOS_SAVE_PDF_H_
+
+#include "utils/config.h"
+
+#ifdef WITH_PDF_EXPORT
+struct content;
+
+bool save_as_pdf(struct content *c, const char *path);
+#endif
+
+#endif
diff --git a/testres/jpeg.jpeg b/testres/jpeg.jpeg
new file mode 100644
index 000000000..636127f1f
--- /dev/null
+++ b/testres/jpeg.jpeg
Binary files differ
diff --git a/testres/png.png b/testres/png.png
new file mode 100644
index 000000000..14e10d224
--- /dev/null
+++ b/testres/png.png
Binary files differ
diff --git a/testres/text.html b/testres/text.html
new file mode 100644
index 000000000..8b22fcf78
--- /dev/null
+++ b/testres/text.html
@@ -0,0 +1,68 @@
+<html>
+<head>
+ <title>Sample page with lots of text</title>
+</head>
+<body style='font-size:14pt;background-color:#CCFFCC;'>
+<br><br><br><br><br><br><br><br>
+This demonstrates the fuzzy margin at the bottom. After pdf-printing this page,
+we can see the bottoms of letters printed just below the lower margin of this page.
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+a1312312312312dadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+a1312312312312dadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+a1312312312312dadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+a1312312312312dadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+asdadadafgsdsgsdfsdfsdfgdbbdfbxccvsds
+</body>
+</html> \ No newline at end of file
diff --git a/testres/text2.html b/testres/text2.html
new file mode 100644
index 000000000..07992a86e
--- /dev/null
+++ b/testres/text2.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+ <title>Sample page with some text</title>
+ <style type="text/css">
+ td {border: 2px;}
+ a { font-family: monospace;
+ font-weight: bold;
+ font-style: italic
+ }
+ body {font-style: oblique}
+ </style>
+</head>
+<body style='font-size:13pt;background-color:#CCFFCC;'>
+<a href='http://www.google.co.uk/search?hl=en&q=making+a+very+long+hyperlink+to+demonstrate+how+it+will+be+broken+&btnG=Search&meta='>
+http://www.google.co.uk/search?hl=en&q=making+a+very+long+hyperlink+to+demonstrate+how+it+will+be+broken+&btnG=Search&meta=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaassssssssssssssssssssssssssssssssssss</a>
+a1312312312312dadadafgsdsgsdddddddddddddddddddddddddddddfsdfsdfgdbbdfbxccvsdsddddddddddd
+12345678901011121314151617181920212223242526272829303132333435363738394041424344454647484950
+<table border=5>
+<tr>
+<td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</td>
+<td>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</td>
+</tr>
+<tr>
+<td>bbbbbbbbbbbbbbbb</td>
+<td>bbbbbbbbbbbbbb</td>
+</tr>
+</table>
+</body>
+</html> \ No newline at end of file
diff --git a/utils/config.h b/utils/config.h
index 39a394f47..4bf3598b3 100644
--- a/utils/config.h
+++ b/utils/config.h
@@ -35,7 +35,7 @@ char *strndup(const char *s, size_t n);
/* This section toggles build options on and off.
* Simply undefine a symbol to turn the relevant feature off.
*
- * IF ADDING A FEATURE HERE, ADD IT TO Docs/Doxyfile LINE 892 AS WELL.
+ * IF ADDING A FEATURE HERE, ADD IT TO Docs/Doxyfile's "PREDEFINED" DEFINITION AS WELL.
*/
/* HTTP Auth */
@@ -80,7 +80,14 @@ char *strndup(const char *s, size_t n);
#define WITH_RSVG
#endif
#endif
+#if defined(riscos) || defined(DEBUG_BUILD)
+ /* Export modules */
+ #define WITH_SAVE_COMPLETE
+ #define WITH_DRAW_EXPORT
+ #define WITH_PDF_EXPORT
+#endif
+/* Configuration sanity checks: */
#if defined(WITH_NS_SVG) && defined(WITH_RSVG)
#error Cannot build WITH_NS_SVG and WITH_RSVG both enabled
#endif
@@ -89,10 +96,4 @@ char *strndup(const char *s, size_t n);
#error Cannot build WITH_NSSPRITE and WITH_SPRITE both enabled
#endif
-#if defined(riscos) || defined(DEBUG_BUILD)
- /* Export modules */
- #define WITH_SAVE_COMPLETE
- #define WITH_DRAW_EXPORT
-#endif
-
#endif