From 35d3d6d0bb16abeeb6aad2fd0398c8e68a40d414 Mon Sep 17 00:00:00 2001 From: John Tytgat Date: Sat, 26 Jul 2008 16:01:59 +0000 Subject: First merge of Adam Blokus' GSoC work from his branch 'branches/adamblokus/netsurf'. Merged revisions 4212-4552,4554-4709,4711-4724 via svnmerge from svn://svn.netsurf-browser.org/branches/adamblokus/netsurf ........ r4212 | adamblokus | 2008-05-26 19:42:31 +0200 (Mon, 26 May 2008) | 4 lines Pdf plotting skeleton pinned on Print Preview in GTK. Just creates a file and draws lines. ........ r4213 | adamblokus | 2008-05-27 00:11:03 +0200 (Tue, 27 May 2008) | 4 lines Pdf plotter - added drawing some graphic primitives. Still with limited functionality, but a snapshot of the currently viewed page can be made and resembles the original. ........ r4214 | adamblokus | 2008-05-27 11:43:31 +0200 (Tue, 27 May 2008) | 2 lines Corrected encoding name ........ r4215 | adamblokus | 2008-05-27 12:47:26 +0200 (Tue, 27 May 2008) | 3 lines Colours and polygons added. ........ r4217 | adamblokus | 2008-05-27 21:39:35 +0200 (Tue, 27 May 2008) | 6 lines Added rectangles, filled boxes and clipping. Taken into consideration joty's comments. Added a todo list for this part. Added some debug stuff and checking boundaries. ........ r4218 | adamblokus | 2008-05-28 12:37:30 +0200 (Wed, 28 May 2008) | 2 lines Added path ploting (not sure if valid argument order for bezier) and dashed/dotted line styles ........ r4221 | adamblokus | 2008-05-28 22:11:05 +0200 (Wed, 28 May 2008) | 3 lines Some more options in graphic primitives and normalizing some parameters. ........ r4235 | adamblokus | 2008-05-31 22:54:56 +0200 (Sat, 31 May 2008) | 4 lines Plotting changed as jmb suggested (is the least invasive one from the possible) Added dummy bitmap plotting - way of plotting an image is determined by its type. ........ r4251 | adamblokus | 2008-06-03 17:12:15 +0200 (Tue, 03 Jun 2008) | 3 lines Added plotting jpg and png images - quite a lot to improve in this code, but it seems to work ;) ........ r4263 | adamblokus | 2008-06-05 14:20:32 +0200 (Thu, 05 Jun 2008) | 3 lines Added hadling images other than png and jpeg - with transparency. ........ r4267 | adamblokus | 2008-06-06 15:36:34 +0200 (Fri, 06 Jun 2008) | 5 lines Added handling NULL-returns from all mallocs. Added plot_bitmap_tile handling. Changed code style a little. ........ r4327 | adamblokus | 2008-06-12 17:46:34 +0200 (Thu, 12 Jun 2008) | 5 lines Added a first prototype of the paged-output organization. Still not sure about naming, file locations etc. Works with the same pdf plotting as before. ........ r4328 | adamblokus | 2008-06-13 13:52:15 +0200 (Fri, 13 Jun 2008) | 4 lines Added primitive width adjustment and outputing the whole website in multiple pages. ........ r4336 | joty | 2008-06-15 15:06:57 +0200 (Sun, 15 Jun 2008) | 1 line Fix RISC OS build failure (change r4235 wasn't complete). ........ r4337 | joty | 2008-06-15 18:15:32 +0200 (Sun, 15 Jun 2008) | 16 lines This enables "Export PDF" in RISC OS build: - Docs/Doxyfile(PREDEFINED): Added WITH_PDF_EXPORT - Makefile.sources(S_PDF): Add to RISC OS target as well. - utils/config.h: Define WITH_PDF_EXPORT which controls if we want to have PDF export functionality or not. - riscos/save_pdf.c,riscos/save_pdf.h(save_as_pdf): Use PDF print API made by Adam Blokus to write a PDF file under RISC OS. - riscos/save.c: Call save_as_pdf added. - riscos/menus.c: Add 'Export->PDF' menu entry. - riscos/menus.h(menu_action): Added BROWSER_EXPORT_PDF. - desktop/gui.h(gui_save_type): Added GUI_SAVE_PDF. - desktop/print.c(print_run): Added return value. - Makefile(CCACHE): Moved closed to the place where CC is set for the first time. (LDFLAGS): Centralised adding all non-pkgconfig libraries and added Haru + PNG libs. ........ r4343 | adamblokus | 2008-06-16 01:08:52 +0200 (Mon, 16 Jun 2008) | 3 lines Added margins and page size adjustment. ........ r4412 | adamblokus | 2008-06-21 20:22:07 +0200 (Sat, 21 Jun 2008) | 4 lines Added 'fuzzy' margins on page bottom. Disabled direct png embedding, because it is too unstable in Haru now. ........ r4421 | adamblokus | 2008-06-22 18:52:28 +0200 (Sun, 22 Jun 2008) | 2 lines Added "Save as.." dialog and Export->PDF menu entry. Print preview still works with default path. ........ r4437 | adamblokus | 2008-06-25 02:44:46 +0200 (Wed, 25 Jun 2008) | 4 lines Added skeleton of applying loose layout. Minor code cleaning-up. ........ r4492 | adamblokus | 2008-07-02 09:02:42 +0200 (Wed, 02 Jul 2008) | 5 lines Implemented the elementar ideas of the loose layout. Added scaling in the printing routine. Added some basic demonstrations. ........ r4493 | adamblokus | 2008-07-02 09:05:55 +0200 (Wed, 02 Jul 2008) | 3 lines Cleaned up the loosing code - commited to much of leftover rubbish code. ........ r4507 | adamblokus | 2008-07-04 14:25:48 +0200 (Fri, 04 Jul 2008) | 4 lines Added duplicating box tree and current content - window flickering during printing solved. Minor error checking after new HPDF_Image_AddSMask call. ........ r4515 | adamblokus | 2008-07-06 22:28:16 +0200 (Sun, 06 Jul 2008) | 2 lines Changes in loosen layout (image resizing). ........ r4517 | adamblokus | 2008-07-06 22:38:23 +0200 (Sun, 06 Jul 2008) | 2 lines Added pdf font handling and rendering functions with the use of Haru functions. ........ r4555 | adamblokus | 2008-07-10 00:59:05 +0200 (Thu, 10 Jul 2008) | 2 lines Added a very basic and still buggy GTK print implementation. ........ r4565 | adamblokus | 2008-07-10 14:50:16 +0200 (Thu, 10 Jul 2008) | 2 lines Added gtk printing one more time - I have forgotten to add the main file. ........ r4566 | adamblokus | 2008-07-10 14:57:02 +0200 (Thu, 10 Jul 2008) | 2 lines removed error with comment ........ r4569 | adamblokus | 2008-07-10 15:52:55 +0200 (Thu, 10 Jul 2008) | 5 lines Major style improvements - added a lot of doxygen comments, followed tlsa's style guide. Added some more error checking, too. ........ r4575 | adamblokus | 2008-07-10 18:48:26 +0200 (Thu, 10 Jul 2008) | 2 lines Cleaned up the code. ........ r4687 | adamblokus | 2008-07-17 14:17:19 +0200 (Thu, 17 Jul 2008) | 2 lines Changed everything according to jmb's review plus some minor bug fixes to gtk_print. ........ r4688 | adamblokus | 2008-07-17 17:16:34 +0200 (Thu, 17 Jul 2008) | 2 lines Solved the netsurf.glade clash from r4421. ........ r4693 | adamblokus | 2008-07-18 18:11:51 +0200 (Fri, 18 Jul 2008) | 2 lines Fixed bug with wrong number of pages in gtk printing. ........ r4695 | adamblokus | 2008-07-18 19:59:24 +0200 (Fri, 18 Jul 2008) | 3 lines - fixed uncommented line from the previous commit - fixed bug with scale bigger than 1.0 (incorretly clipped page) ........ r4696 | adamblokus | 2008-07-18 23:28:00 +0200 (Fri, 18 Jul 2008) | 2 lines Fixed bug in gtk_print_font_paint (and nsfont_paint). ........ r4697 | adamblokus | 2008-07-18 23:35:38 +0200 (Fri, 18 Jul 2008) | 2 lines Bug fix in nsfont_paint. ........ r4711 | adamblokus | 2008-07-19 22:44:15 +0200 (Sat, 19 Jul 2008) | 2 lines Added gtk_selection files. ........ r4712 | adamblokus | 2008-07-20 11:15:06 +0200 (Sun, 20 Jul 2008) | 2 lines Addam missing glade files. ........ r4713 | joty | 2008-07-20 17:13:10 +0200 (Sun, 20 Jul 2008) | 1 line Follow change r4517 for RISC OS and BeOS platforms : Added pdf font handling and rendering functions with the use of Haru functions. ........ r4714 | joty | 2008-07-20 18:19:50 +0200 (Sun, 20 Jul 2008) | 1 line Declare haru_nsfont iso define an instance for each C source including the font_haru.h header. This fixes breakage of PDF export on RISC OS. ........ r4724 | adamblokus | 2008-07-23 03:30:08 +0200 (Wed, 23 Jul 2008) | 6 lines Applied changes according to joty's review. Added checking the dimensions of a plotted image to pdf plotter. Commented out jpg embedding (it seems to cause some problems I'll bring it back when I figure out what's wrong) . Added back some files removed by mistake. ........ svn path=/trunk/netsurf/; revision=4741 --- Docs/Doxyfile | 2 +- Makefile | 17 +- Makefile.sources | 15 +- beos/beos_font.cpp | 15 + desktop/browser.c | 11 +- desktop/gui.h | 1 + desktop/history_core.c | 4 +- desktop/knockout.c | 22 +- desktop/plotters.h | 6 +- desktop/print.c | 280 ++++++++++++++++++ desktop/print.h | 75 +++++ desktop/printer.h | 41 +++ desktop/selection.c | 2 +- desktop/textinput.c | 20 +- gtk/font_pango.c | 36 ++- gtk/font_pango.h | 9 + gtk/gtk_bitmap.c | 8 + gtk/gtk_plotters.c | 10 +- gtk/gtk_plotters.h | 1 + gtk/gtk_print.c | 573 ++++++++++++++++++++++++++++++++++++ gtk/gtk_print.h | 48 +++ gtk/gtk_scaffolding.c | 91 ++++++ gtk/res/netsurf.glade | 19 +- image/bmp.c | 4 +- image/gif.c | 4 +- image/ico.c | 4 +- image/jpeg.c | 4 +- image/mng.c | 4 +- image/nssprite.c | 2 +- image/rsvg.c | 2 +- pdf/TODO | 19 ++ pdf/font_haru.c | 373 ++++++++++++++++++++++++ pdf/font_haru.h | 36 +++ pdf/pdf_plotters.c | 774 +++++++++++++++++++++++++++++++++++++++++++++++++ pdf/pdf_plotters.h | 40 +++ pdf/pdf_printer.h | 36 +++ render/box.c | 203 ++++++++++++- render/box.h | 2 + render/font.h | 23 +- render/html.c | 1 + render/html.h | 1 + render/html_redraw.c | 8 +- render/layout.c | 92 +++--- render/loosen.c | 415 ++++++++++++++++++++++++++ render/loosen.h | 35 +++ render/textplain.c | 10 +- riscos/font.c | 26 +- riscos/menus.c | 20 ++ riscos/menus.h | 1 + riscos/plotters.c | 8 +- riscos/save.c | 25 +- riscos/save_draw.c | 8 +- riscos/save_pdf.c | 55 ++++ riscos/save_pdf.h | 30 ++ testres/jpeg.jpeg | Bin 0 -> 6170 bytes testres/png.png | Bin 0 -> 4922 bytes testres/text.html | 68 +++++ testres/text2.html | 29 ++ utils/config.h | 15 +- 59 files changed, 3527 insertions(+), 156 deletions(-) create mode 100644 desktop/print.c create mode 100644 desktop/print.h create mode 100644 desktop/printer.h create mode 100644 gtk/gtk_print.c create mode 100644 gtk/gtk_print.h create mode 100644 pdf/TODO create mode 100644 pdf/font_haru.c create mode 100644 pdf/font_haru.h create mode 100644 pdf/pdf_plotters.c create mode 100644 pdf/pdf_plotters.h create mode 100644 pdf/pdf_printer.h create mode 100644 render/loosen.c create mode 100644 render/loosen.h create mode 100644 riscos/save_pdf.c create mode 100644 riscos/save_pdf.h create mode 100644 testres/jpeg.jpeg create mode 100644 testres/png.png create mode 100644 testres/text.html create mode 100644 testres/text2.html 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 #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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * 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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * 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 + +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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * 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 @@ -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 + * Copyright 2005 James Bursa + * Copyright 2008 Adam Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + /** \file + * 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 +#include +#include +#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, >k_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(>k_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, >k_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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * GTK printing (interface). + */ + +#ifndef NETSURF_GTK_PRINT_PLOTTERS_H +#define NETSURF_GTK_PRINT_PLOTTERS_H + + +#include + +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 @@ True - False + True Export the page to a different format. Export True @@ -100,6 +100,7 @@ True + False Plain ASCII text, readable in text editors and views. Plain text... True @@ -108,6 +109,7 @@ True + False RISC OS Drawfile vector graphic. Drawfile... True @@ -116,11 +118,21 @@ True + False PostScript for printing and converting to PDFs. PostScript... True + + + True + True + Portable Document Format. + PDF... + True + + @@ -133,10 +145,11 @@ True - False + True Show how a print out might look like. Print preview... True + True @@ -149,7 +162,7 @@ True - False + True Produce a hardcopy on your printer. Print... True 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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * Font handling in Haru pdf documents (implementation). + * + * The functions were written to implement the same interface as the Pango ones + * so that the usage of the latter wouldn't have to be modified. + */ + +#define FONT_HARU_DEBUG + +#include +#include +#include +#include + +#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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * Font handling in Haru pdf documents (interface). + */ + +#ifndef _NETSURF_RENDER_FONT_HARU_H_ +#define _NETSURF_RENDER_FONT_HARU_H_ + +#include "render/font.h" +#include "hpdf.h" + +bool haru_nsfont_apply_style(const struct css_style *style, + HPDF_Doc doc, HPDF_Page page, + HPDF_Font *font); + +extern const struct font_functions haru_nsfont; + + +#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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + /** \file + * Target independent PDF plotting using Haru Free PDF Library. + * Contains also the current solution for some text being cropped over page + * boundaries a 'fuzzy' bottom margin. + */ + +#include +#include +#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 ; ifont_size.value.length.unit == CSS_UNIT_PX) + size = style->font_size.value.length.value; + else + size = css_len2pt(&style->font_size.value.length, style); + + 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; ipage_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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + PDF Plotting +*/ + +#ifndef NETSURF_PDF_PLOTTERS_H +#define NETSURF_PDF_PLOTTERS_H + +#include "desktop/print.h" +struct plotter_table; + +extern struct printer pdf_printer; + +/**Start plotting a pdf file*/ +bool pdf_begin(struct print_settings* settings); + +/**Finish the current page and start a new one*/ +bool pdf_next_page(void); + +/**Close pdf document and save changes to file*/ +void pdf_end(void); + +#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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * PDF 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 + * + * 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 . + */ + +#include +#include + +#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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** +\file +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 + +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 #include #include -#include "oslib/dragasprite.h" -#include "oslib/osbyte.h" -#include "oslib/osfile.h" -#include "oslib/osmodule.h" +#include +#include +#include +#include #include -#include "oslib/wimp.h" -#include "oslib/wimpspriteop.h" +#include +#include #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 */ @@ -646,6 +649,10 @@ bool ro_gui_save_content(struct content *c, char *path) #ifdef WITH_DRAW_EXPORT 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); 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 + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file + * Export a content as a PDF file (implementation). + */ + +#include +#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 + * + * 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 . + */ + +#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 Binary files /dev/null and b/testres/jpeg.jpeg differ diff --git a/testres/png.png b/testres/png.png new file mode 100644 index 000000000..14e10d224 Binary files /dev/null and b/testres/png.png 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 @@ + + + Sample page with lots of text + + +







+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 + + \ 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 @@ + + + Sample page with some text + + + + +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 +a1312312312312dadadafgsdsgsdddddddddddddddddddddddddddddfsdfsdfgdbbdfbxccvsdsddddddddddd +12345678901011121314151617181920212223242526272829303132333435363738394041424344454647484950 + + + + + + + + + +
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+ + \ 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 -- cgit v1.2.3