From: auroux Date: Sat, 29 Aug 2009 01:02:19 +0000 (+0000) Subject: Fix various bugs; use poppler instead of pdftoppm X-Git-Url: https://git.donarmstrong.com/?p=xournal.git;a=commitdiff_plain;h=8d8f9e44b17ef9737084eea7693072e4777a0609 Fix various bugs; use poppler instead of pdftoppm --- diff --git a/Makefile.am b/Makefile.am index 2bfe3db..a9303f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,8 +5,7 @@ SUBDIRS = src EXTRA_DIST = \ autogen.sh \ xournal.glade xournal.gladep \ - xournal.xml x-xoj.desktop xournal.desktop \ - maemo/xournal.desktop maemo/xournal.service + xournal.xml x-xoj.desktop xournal.desktop install-data-local: @$(NORMAL_INSTALL) @@ -111,10 +110,3 @@ dist-hook: fi \ fi -if USE_HILDON -desktopdir = $(datadir)/applications/hildon -desktop_DATA = maemo/xournal.desktop - -servicedir = $(libdir)/dbus-1.0/services -service_DATA = maemo/xournal.service -endif diff --git a/configure.in b/configure.in index b85ae36..597bc6e 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(configure.in) -AM_INIT_AUTOMAKE(xournal, 0.4.2.1) +AM_INIT_AUTOMAKE(xournal, 0.4.2.9) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE @@ -10,20 +10,11 @@ AC_PROG_CC AM_PROG_CC_STDC AC_HEADER_STDC -dnl set this var to NONE (PC) or other thing (Maemo) -usehildon='NONE' -if test "x$usehildon" != xNONE -then - pkg_modules="gtk+-2.0 >= 2.4.0 libgnomecanvas-2.0 >= 2.4.0 libgnomeprintui-2.2 >= 2.0.0 hildon-libs libosso" -else - pkg_modules="gtk+-2.0 >= 2.4.0 libgnomecanvas-2.0 >= 2.4.0 libgnomeprintui-2.2 >= 2.0.0" -fi +pkg_modules="gtk+-2.0 >= 2.4.0 libgnomecanvas-2.0 >= 2.4.0 libgnomeprintui-2.2 >= 2.0.0 poppler-glib >= 0.6.1" PKG_CHECK_MODULES(PACKAGE, [$pkg_modules]) AC_SUBST(PACKAGE_CFLAGS) AC_SUBST(PACKAGE_LIBS) -AM_CONDITIONAL(USE_HILDON, test "x$usehildon" != xNONE) - AC_OUTPUT([ Makefile src/Makefile diff --git a/src/TODO b/src/TODO index d73e6dd..fe2b777 100644 --- a/src/TODO +++ b/src/TODO @@ -2,32 +2,118 @@ List of features to be implemented (not in any particular order) ---------------------------------------------------------------- - collaborative editing (see discussion with Erik Demaine) -- porting to Win32 and MacOS +- porting to Win32 and MacOS; merge Nokia port - multiple-scenario undo history +- collaborative: allow non-x86 endianness (for ints, for floats?) +- collaborative: have an initial undo item = contents of the initial + xoj file + attachments for bitmap, pdf, and ps backgrounds + +- cleanup of undo history (keep track of refcounts, delete old undo) + save-and-clear-undo ? + +- RandR / recalibration awareness? + (e.g. if xinput events are far away from core events, re-query geometry?) + or see if removing the GTK bugfix would help? -- e.g. by recalling an + internal gtk init function? + +DONE: fix GTK+ 2.16 XInput issues with scrollbars and menus +DONE: fix bug 2826845 (shape recognizer accel doesn't work in fullscreen mode) +DONE: bug affecting resize zone in statusbar +DONE: page spinbutton wide enough for 3 digits; avoid "deprecated" warning +DONE: edit->paste command should refresh toolbar to unselect color/thickness + (so can repaint sel) +DONE: cursor doesn't reset properly after selection operation if primary + tool is select mode (stays with arrow cursor instead of pointer) +DONE: device with "eraser" at the end of its name is of type ERASER + (Edward Yang) +DONE: fix_xinput_coords() replaces buggy xinput events by core pointer + coordinates +DONE: use poppler to render PDF bakgrounds (after patches contributed + by Mike Ter Louw and Bob McElrath) + + + +****** gnu-gettext patch? + +****** URGENT: gtkprint; new release by November end for Debian! + nb: libgnomeprint produces many warnings (spinbutton; gpa assertions) + + +- remove "antialias bg" flag, useless... see McElrath +- PDF bg memory usage throttling / delete oldest pdf backgrounds +- fix fix_xinput_coords so it works ok without ENABLE_XINPUT_BUGFIX ? + (with both old and new GTK+) + (need to shift by (sx,sy), + shift between canvas->window vs canvas in 2.17) +- Esc should leave text box if editing; and fullscreen if fullscreen? +- color chooser button (patch tracker?) +** auto-hide patch from ~/prog/src/xournal-autohide/ ? + (check for cpu usage, add flag if need be; handle BOTH edges + and only (un)hide stuff at the correct edge!) +** if bg pdf not found in absolute path, look in path of xoj file before + prompting user. +** GETTEXT i18n patch (sourceforge) +** PATCH TO WORK AROUND PPM LOADING ISSUE - see tracker + (make gdk_pixbuf_ref and g_object_unref(loader) as in the gv_bg + loading) or... switch to poppler? +** patch to fix focus + allow up/dn in single page mode (Bob McElrath) + also comment out contents of reset_focus() ? (tracker patch #2494022) +** patch to fix underscores in MRU filename display (McElrath) (tracker) + (fix memory leak) +** UI update (Bob McElrath) -- eliminating status bar, compact layout, + "compact interface" by default; themes, with line in config file + to load pixmaps from pixmaps/$THEME/ (see Jan 9, 2009 emails) +** antialias BG pixmaps doesn't do anything anymore? +** autosave patch (Edward Yang) (fix: optional only, w/ menu + cfgfile + entries; fix: should clean up autosave.xoj.bg* files too; config interval) +- patch (ikim@physics.wisc.edu): multicolumn mode + LASSO SELECTION +- patch: ortho/snap (revised Apr 13 2009) +** FIXME: get_pressure_multiplier() should access correct members + of event struct, like xinput_coords() +** FIX RECOGNIZER BUG: for single-segment strokes (e.g. single click), + I_xx and I_yy = 0, sometimes -0 -> Rad = nan (should I_xx = abs(...) ?) + also Det = division by 0, should return 0 if ixx + iyy = 0. + (MAYBE: fix inertia calc: integrate dm over each segment rather than dm.pt[i]) +- new recognizer icon (andruk on patches tracker) +- recognizer: if variable-width, associate average width +- recognizer should snap to existing recognized geometric shapes + - improve recognizer: two passes for polygons (low tolerance, then higher) to better detect elongated rectangles? (if low tolerance recognizer doesn't get a rectangle, then use higher tolerance for everything else, since otherwise there's too much risk of splitting a segment into 2) - snap-to-grid (also for ruler & recognizer vertices) and maybe also snap-to-vertices (option for ruler and recognizer)? -- allow broken PDF with some 19-byte xref entries (LF instead of CRLF).?? - (Phil Rhoades 3/25/08) +- config option: config save current tool options instead of default ones + +- "new page before/after" on a PDF bg page should ask: same page, other page of + PDF file, default paper +- bug in truetype subset generation w/ Adobe 9, see if gtk-print any better? - render page to bitmap: for export, preview, and copy-paste (render using libart, see how gnomecanvas does it?) + NO: render using Cairo !!! then can switch to GtkPrint as well. (copy-paste: config option to render only current layer or all below?) - cut-and-paste of selection into other apps (as bitmap; as SVG?) - navigation sidebar with bitmap page previews - bitmap preview for document icon in desktop environments? - "organizer" side panel (hierarchy of notes), cf. gjots +- allow toolbar to go vertical +- toolbar buttons should react to button 2/3 click to modify settings + for that tool? + - paste text directly into xournal, from xournal? (instead of starting a text item and pasting into/from it) +- insert links (to URLs; within document/to other xoj? hand mode navigates) - increase width of spinPageNo to fit 3 digits +- should escape '_' to '__' in MRU menu entries (else become mnemonics) - a command + keyboard shortcut to switch mappings (1<->2, 1<->3, 2<->3) (A. Rechnitzer Sept 11, 2007) +- modify encoding of TrueType font subsets or provide cmap so pdf text + can be extracted +- smoothing of strokes (for users without tablets / with deficient drivers) - lasso tool - internationalization / translation of interface - switch to poppler instead of pdftoppm; with exact float dpi settings @@ -40,13 +126,20 @@ List of features to be implemented (not in any particular order) (keep GnomePrint option for compatibility with GTK+ <2.10) - insert images (screen capture or from file or from clipboard), not as full-page backgrounds (new ITEM type) -- convert to/from Jarnal format; to/from MS Journal format??? +- convert to/from Jarnal format; to/from MS Journal format??? +- export as SVG, as bitmap (use Cairo for this) +- improved PDF viewer features (search text, hyperlink, page borders...) + (using full poppler api ?) +- use system paper size as default (/etc/papersize) +- autoload *.pdf.xoj if present when opening *.pdf ??? - sticky notes (anchor visually text box to a bg location) - use relative paths for bg documents (e.g. annotated PDF) - flush display queue when drawing over a slow X server? - more paper customization (in particular, 1/2 inch graph paper) + (2 custom papers with settings in config file? + a folder with blank PDF or xoj papers and quick-access?) - option to map a button to a context menu (incl. tool selection, ...) - option to map a button to "undo" - xournal_page-shadow.diff (Martin Kiefel Feb 5 2007) @@ -57,11 +150,15 @@ List of features to be implemented (not in any particular order) (http://shoffsta.afraid.org/Projects/Xournal/) - flatten (incl undo/redo...) - enabled only if nlayers>1 - color chooser (papercolor, pen color); maybe more default colors + cf. ojb patch #2083103 adds paper color chooser - printing: print-options, save printer settings (throughout a session, and on disk) (maybe a separate config file .xournal/gnome-print-settings) - help index - option for highlighter to be always at bottom of its layer - more pen/highlighter shapes (chisel) +- slanted tip pens (calligraphy) +- toolbar buttons to access custom preset tools (e.g. text or pen with settings) +- text boxes with opaque background - recalibration upon screen resize / compensation for miscalibration (use ConfigureNotify event and XInput? cf "Bugs" tracker 08/2007) - find a better behavior for vertical space tool across page boundaries ? @@ -79,6 +176,8 @@ List of features to be implemented (not in any particular order) - customize autogenerated save file names - layer dialog box to set visibility status of each layer regardless of which layer is being edited +- option to link layer creation and visibility status for all pages + (Eric Borghs 04/15/08) - display corruption on scroll down when bottom of window is obscured?? (probably a gnomecanvas or X bug -- expose event generated for wrong region, or not processed?) @@ -93,7 +192,9 @@ List of features to be implemented (not in any particular order) - EPOS: Export pages to pictures in the Jpg and Png formats. - EPOS: Rotate Ink in custom angle. - handwriting recognition???? (cellwriter?) unlikely. we don't have grids + see galileon comment on 2008-07-29 to tracker #1925309: word recognizer - handwritten stroke search in document (see cellwriter?) + (correlate inertia-normalized strokes in lift to unit cotangent bundle?) - option: export to PDF with incremental pages for successive layers (for presentations) (Daniel Brugarth 8/18/07) - Samuel Hoffstaetter: lasso, gettext localization, sidebar thumbnails, ... @@ -102,4 +203,5 @@ List of features to be implemented (not in any particular order) rotation doesn't work): gnome_canvas_item_affine_relative(canvas->root, ...) would rotate all but text items (still need to modify scroll bbox, and adjust event coordinates by inverse rotation). +- rotate PDF background pages (individually wrt each other, see #2099935) - switch to libglade, and allow customization of key shortcuts (accels) diff --git a/src/main.c b/src/main.c index f22125e..ac6e3eb 100644 --- a/src/main.c +++ b/src/main.c @@ -26,6 +26,21 @@ struct UndoItem *undo, *redo; // the undo and redo stacks double DEFAULT_ZOOM; +// prevent interface items from getting bogus XInput events +gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event, + gpointer user_data) +{ + // prevent scrollbars from reacting to XInput events + if (event->type == GDK_MOTION_NOTIFY && + event->motion.device != gdk_device_get_core_pointer()) + return TRUE; + if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS || + event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && + event->button.device != gdk_device_get_core_pointer()) + return TRUE; + return FALSE; +} + void init_stuff (int argc, char *argv[]) { GtkWidget *w; @@ -175,6 +190,8 @@ void init_stuff (int argc, char *argv[]) gdk_device_set_axis_use(device, 1, GDK_AXIS_IGNORE); #endif gdk_device_set_mode(device, GDK_MODE_SCREEN); + if (g_str_has_suffix(device->name, "eraser")) + gdk_device_set_source(device, GDK_SOURCE_ERASER); can_xinput = TRUE; } dev_list = dev_list->next; @@ -214,6 +231,37 @@ void init_stuff (int argc, char *argv[]) gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsUseXInput")), ui.use_xinput); + /* fix a bug in GTK+ 2.16 and beyond: scrollbars shouldn't get extended + input events from pointer motion when cursor moves into main window */ +#if GTK_CHECK_VERSION(2,14,0) + if (!gtk_check_version(2, 14, 0)) { + g_signal_connect ( + GET_COMPONENT("menubar"), + "event", G_CALLBACK (filter_extended_events), + NULL); + g_signal_connect ( + GET_COMPONENT("toolbarMain"), + "event", G_CALLBACK (filter_extended_events), + NULL); + g_signal_connect ( + GET_COMPONENT("toolbarPen"), + "event", G_CALLBACK (filter_extended_events), + NULL); + g_signal_connect ( + GET_COMPONENT("statusbar"), + "event", G_CALLBACK (filter_extended_events), + NULL); + g_signal_connect ( + (gpointer)(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(w))), + "event", G_CALLBACK (filter_extended_events), + NULL); + g_signal_connect ( + (gpointer)(gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(w))), + "event", G_CALLBACK (filter_extended_events), + NULL); + } +#endif + // load the MRU init_mru(); @@ -274,7 +322,6 @@ main (int argc, char *argv[]) gtk_main (); if (bgpdf.status != STATUS_NOT_INIT) shutdown_bgpdf(); - if (bgpdf.status != STATUS_NOT_INIT) end_bgpdf_shutdown(); save_mru_list(); if (ui.auto_save_prefs) save_config_to_file(); diff --git a/src/xo-callbacks.c b/src/xo-callbacks.c index d52b476..6c087d3 100644 --- a/src/xo-callbacks.c +++ b/src/xo-callbacks.c @@ -1048,7 +1048,6 @@ on_viewNextPage_activate (GtkMenuItem *menuitem, end_text(); reset_focus(); if (ui.pageno == journal.npages-1) { // create a page at end - if (page_ops_forbidden()) return; on_journalNewPageEnd_activate(menuitem, user_data); return; } @@ -1105,7 +1104,6 @@ on_journalNewPageBefore_activate (GtkMenuItem *menuitem, end_text(); reset_focus(); - if (page_ops_forbidden()) return; reset_selection(); pg = new_page(ui.cur_page); journal.pages = g_list_insert(journal.pages, pg, ui.pageno); @@ -1127,7 +1125,6 @@ on_journalNewPageAfter_activate (GtkMenuItem *menuitem, end_text(); reset_focus(); - if (page_ops_forbidden()) return; reset_selection(); pg = new_page(ui.cur_page); journal.pages = g_list_insert(journal.pages, pg, ui.pageno+1); @@ -1149,7 +1146,6 @@ on_journalNewPageEnd_activate (GtkMenuItem *menuitem, end_text(); reset_focus(); - if (page_ops_forbidden()) return; reset_selection(); pg = new_page((struct Page *)g_list_last(journal.pages)->data); journal.pages = g_list_append(journal.pages, pg); @@ -1172,7 +1168,6 @@ on_journalDeletePage_activate (GtkMenuItem *menuitem, end_text(); reset_focus(); - if (page_ops_forbidden()) return; if (journal.npages == 1) return; reset_selection(); prepare_new_undo(); @@ -2391,11 +2386,9 @@ on_canvas_button_press_event (GtkWidget *widget, if (event->button > 3) return FALSE; // no painting with the mouse wheel! if (event->type != GDK_BUTTON_PRESS) return FALSE; // double-clicks may have broken axes member (free'd) due to a bug in GDK - if (!is_core) { - // re-get the axis values since Synaptics sends bogus ones - gdk_device_get_state(event->device, event->window, event->axes, NULL); + if (!is_core) fix_xinput_coords((GdkEvent *)event); - } + #ifdef INPUT_DEBUG printf("DEBUG: ButtonDown (%s) (x,y)=(%.2f,%.2f)\n", is_core?"core":"xinput", event->x, event->y); @@ -2800,7 +2793,6 @@ on_spinPageNo_value_changed (GtkSpinButton *spinbutton, val = gtk_spin_button_get_value_as_int(spinbutton) - 1; if (val == journal.npages) { // create a page at end - if (page_ops_forbidden()) return; on_journalNewPageEnd_activate(NULL, NULL); return; } diff --git a/src/xo-file.c b/src/xo-file.c index 016fcd4..2b3f361 100644 --- a/src/xo-file.c +++ b/src/xo-file.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "xournal.h" #include "xo-interface.h" @@ -77,8 +78,6 @@ gboolean save_journal(const char *filename) struct Item *item; int i, is_clone; char *tmpfn, *tmpstr; - gchar *pdfbuf; - gsize pdflen; gboolean success; FILE *tmpf; GList *pagelist, *layerlist, *itemlist, *list; @@ -142,13 +141,11 @@ gboolean save_journal(const char *filename) if (pg->bg->file_domain == DOMAIN_ATTACH) { tmpfn = g_strdup_printf("%s.%s", filename, pg->bg->filename->s); success = FALSE; - if (bgpdf.status != STATUS_NOT_INIT && - g_file_get_contents(bgpdf.tmpfile_copy, &pdfbuf, &pdflen, NULL)) + if (bgpdf.status != STATUS_NOT_INIT && bgpdf.file_contents != NULL) { tmpf = fopen(tmpfn, "w"); - if (tmpf != NULL && fwrite(pdfbuf, 1, pdflen, tmpf) == pdflen) + if (tmpf != NULL && fwrite(bgpdf.file_contents, 1, bgpdf.file_length, tmpf) == bgpdf.file_length) success = TRUE; - g_free(pdfbuf); fclose(tmpf); } if (!success) { @@ -790,6 +787,7 @@ gboolean open_journal(char *filename) gnome_canvas_set_pixels_per_unit(canvas, ui.zoom); make_canvas_items(); update_page_stuff(); + rescale_bg_pixmaps(); // this requests the PDF pages if need be gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)), 0); return TRUE; } @@ -930,23 +928,6 @@ struct Background *attempt_screenshot_bg(void) /************** pdf annotation ***************/ -/* free tmp directory */ - -void end_bgpdf_shutdown(void) -{ - if (bgpdf.tmpdir!=NULL) { - if (bgpdf.tmpfile_copy!=NULL) { - g_unlink(bgpdf.tmpfile_copy); - g_free(bgpdf.tmpfile_copy); - bgpdf.tmpfile_copy = NULL; - } - g_rmdir(bgpdf.tmpdir); - g_free(bgpdf.tmpdir); - bgpdf.tmpdir = NULL; - } - bgpdf.status = STATUS_NOT_INIT; -} - /* cancel a request */ void cancel_bgpdf_request(struct BgPdfRequest *req) @@ -955,46 +936,50 @@ void cancel_bgpdf_request(struct BgPdfRequest *req) list_link = g_list_find(bgpdf.requests, req); if (list_link == NULL) return; - if (list_link->prev == NULL && bgpdf.pid > 0) { - // this is being processed: kill the child but don't remove the request yet - if (bgpdf.status == STATUS_RUNNING) bgpdf.status = STATUS_ABORTED; - kill(bgpdf.pid, SIGHUP); -// printf("Cancelling a request - killing %d\n", bgpdf.pid); - } - else { - // remove the request - bgpdf.requests = g_list_delete_link(bgpdf.requests, list_link); - g_free(req); -// printf("Cancelling a request - no kill needed\n"); - } + // remove the request + bgpdf.requests = g_list_delete_link(bgpdf.requests, list_link); + g_free(req); } -/* sigchld callback */ +/* process a bg PDF request from the queue, and recurse */ -void bgpdf_child_handler(GPid pid, gint status, gpointer data) +gboolean bgpdf_scheduler_callback(gpointer data) { struct BgPdfRequest *req; struct BgPdfPage *bgpg; - gchar *ppm_name; GdkPixbuf *pixbuf; - int npad, ret; - - if (bgpdf.requests == NULL) return; + GtkWidget *dialog; + PopplerPage *pdfpage; + gdouble height, width; + int scaled_height, scaled_width; + + // if all requests have been cancelled, remove ourselves from main loop + if (bgpdf.requests == NULL) { bgpdf.pid = 0; return FALSE; } + if (bgpdf.status == STATUS_NOT_INIT) + { printf("BGPDF not initialized??\n"); bgpdf.pid = 0; return FALSE; } + req = (struct BgPdfRequest *)bgpdf.requests->data; - + + // use poppler to generate the page pixbuf = NULL; - // pdftoppm used to generate p-nnnnnn.ppm (6 digits); new versions produce variable width - for (npad = 6; npad>0; npad--) { - ppm_name = g_strdup_printf("%s/p-%0*d.ppm", bgpdf.tmpdir, npad, req->pageno); - if (bgpdf.status != STATUS_ABORTED && bgpdf.status != STATUS_SHUTDOWN) - pixbuf = gdk_pixbuf_new_from_file(ppm_name, NULL); - ret = unlink(ppm_name); - g_free(ppm_name); - if (pixbuf != NULL || ret == 0) break; + pdfpage = poppler_document_get_page(bgpdf.document, req->pageno-1); + if (pdfpage) { +// printf("Processing request for page %d at %f dpi\n", req->pageno, req->dpi); + set_cursor_busy(TRUE); + poppler_page_get_size(pdfpage, &width, &height); + scaled_width = (int) (req->dpi * width/72); + scaled_height = (int) (req->dpi * height/72); + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + FALSE, 8, scaled_width, scaled_height); + poppler_page_render_to_pixbuf( + pdfpage, 0, 0, scaled_width, scaled_height, + req->dpi/72, 0, pixbuf); + g_object_unref(pdfpage); + set_cursor_busy(FALSE); } + // process the generated pixbuf... if (pixbuf != NULL) { // success -// printf("success\n"); while (req->pageno > bgpdf.npages) { bgpg = g_new(struct BgPdfPage, 1); bgpg->pixbuf = NULL; @@ -1005,125 +990,49 @@ void bgpdf_child_handler(GPid pid, gint status, gpointer data) if (bgpg->pixbuf!=NULL) gdk_pixbuf_unref(bgpg->pixbuf); bgpg->pixbuf = pixbuf; bgpg->dpi = req->dpi; - if (req->initial_request && bgpdf.create_pages) { - bgpdf_create_page_with_bg(req->pageno, bgpg); - // create page n, resize it, set its bg - all without any undo effect - } else { - if (!req->is_printing) bgpdf_update_bg(req->pageno, bgpg); - // look for all pages with this bg, and update their bg pixmaps - } - } - else { -// printf("failed or aborted\n"); - bgpdf.create_pages = FALSE; - req->initial_request = FALSE; - } - - bgpdf.pid = 0; - g_spawn_close_pid(pid); - - if (req->initial_request) - req->pageno++; // try for next page - else - bgpdf.requests = g_list_delete_link(bgpdf.requests, bgpdf.requests); - - if (bgpdf.status == STATUS_SHUTDOWN) { - end_bgpdf_shutdown(); - return; - } - - bgpdf.status = STATUS_IDLE; - if (bgpdf.requests != NULL) bgpdf_spawn_child(); -} - -/* spawn a child to process the head request */ - -void bgpdf_spawn_child(void) -{ - struct BgPdfRequest *req; - GPid pid; - gchar pageno_str[10], dpi_str[10]; - gchar *pdf_filename = bgpdf.tmpfile_copy; - gchar *ppm_root = g_strdup_printf("%s/p", bgpdf.tmpdir); - gchar *argv[]= PDFTOPPM_ARGV; - GtkWidget *dialog; - - if (bgpdf.requests == NULL) return; - req = (struct BgPdfRequest *)bgpdf.requests->data; - if (req->pageno > bgpdf.npages+1 || - (!req->initial_request && req->pageno <= bgpdf.npages && - req->dpi == ((struct BgPdfPage *)g_list_nth_data(bgpdf.pages, req->pageno-1))->dpi)) - { // ignore this request - it's redundant, or in outer space - bgpdf.pid = 0; - bgpdf.status = STATUS_IDLE; - bgpdf.requests = g_list_delete_link(bgpdf.requests, bgpdf.requests); - g_free(ppm_root); - if (bgpdf.requests != NULL) bgpdf_spawn_child(); - return; - } - g_snprintf(pageno_str, 10, "%d", req->pageno); - g_snprintf(dpi_str, 10, "%d", req->dpi); -/* printf("Processing request for page %d at %d dpi -- in %s\n", - req->pageno, req->dpi, ppm_root); */ - if (!g_spawn_async(NULL, argv, NULL, - G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, - NULL, NULL, &pid, NULL)) - { - // couldn't spawn... abort this request, try next one maybe ? -// printf("Couldn't spawn\n"); - bgpdf.pid = 0; - bgpdf.status = STATUS_IDLE; - bgpdf.requests = g_list_delete_link(bgpdf.requests, bgpdf.requests); - g_free(ppm_root); + bgpg->pixel_height = scaled_height; + bgpg->pixel_width = scaled_width; + bgpdf_update_bg(req->pageno, bgpg); // update all pages that have this bg + } else { // failure if (!bgpdf.has_failed) { dialog = gtk_message_dialog_new(GTK_WINDOW(winMain), GTK_DIALOG_MODAL, - GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Unable to start PDF loader %s.", argv[0]); + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Unable to render one or more PDF pages."); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } bgpdf.has_failed = TRUE; - if (bgpdf.requests != NULL) bgpdf_spawn_child(); - return; - } + } -// printf("Spawned process %d\n", pid); - bgpdf.pid = pid; - bgpdf.status = STATUS_RUNNING; - g_child_watch_add(pid, bgpdf_child_handler, NULL); - g_free(ppm_root); + bgpdf.requests = g_list_delete_link(bgpdf.requests, bgpdf.requests); + if (bgpdf.requests != NULL) return TRUE; // remain in the idle loop + bgpdf.pid = 0; + return FALSE; // we're done } /* make a request */ -void add_bgpdf_request(int pageno, double zoom, gboolean printing) +void add_bgpdf_request(int pageno, double zoom) { struct BgPdfRequest *req, *cmp_req; GList *list; - if (bgpdf.status == STATUS_NOT_INIT || bgpdf.status == STATUS_SHUTDOWN) - return; // don't accept requests in those modes... + if (bgpdf.status == STATUS_NOT_INIT) + return; // don't accept requests req = g_new(struct BgPdfRequest, 1); - req->is_printing = printing; - if (printing) req->dpi = PDFTOPPM_PRINTING_DPI; - else req->dpi = (int)floor(72*zoom+0.5); -// printf("Enqueuing request for page %d at %d dpi\n", pageno, req->dpi); - if (pageno >= 1) { - // cancel any request this may supersede - for (list = bgpdf.requests; list != NULL; ) { - cmp_req = (struct BgPdfRequest *)list->data; - list = list->next; - if (!cmp_req->initial_request && cmp_req->pageno == pageno && - cmp_req->is_printing == printing) - cancel_bgpdf_request(cmp_req); - } - req->pageno = pageno; - req->initial_request = FALSE; - } else { - req->pageno = 1; - req->initial_request = TRUE; + req->pageno = pageno; + req->dpi = 72*zoom; +// printf("Enqueuing request for page %d at %f dpi\n", pageno, req->dpi); + + // cancel any request this may supersede + for (list = bgpdf.requests; list != NULL; ) { + cmp_req = (struct BgPdfRequest *)list->data; + list = list->next; + if (cmp_req->pageno == pageno) cancel_bgpdf_request(cmp_req); } + + // make the request bgpdf.requests = g_list_append(bgpdf.requests, req); - if (!bgpdf.pid) bgpdf_spawn_child(); + if (!bgpdf.pid) bgpdf.pid = g_idle_add(bgpdf_scheduler_callback, NULL); } /* shutdown the PDF reader */ @@ -1133,103 +1042,107 @@ void shutdown_bgpdf(void) GList *list; struct BgPdfPage *pdfpg; struct BgPdfRequest *req; + + if (bgpdf.status == STATUS_NOT_INIT) return; - if (bgpdf.status == STATUS_NOT_INIT || bgpdf.status == STATUS_SHUTDOWN) return; + // cancel all requests and free data structures refstring_unref(bgpdf.filename); for (list = bgpdf.pages; list != NULL; list = list->next) { pdfpg = (struct BgPdfPage *)list->data; if (pdfpg->pixbuf!=NULL) gdk_pixbuf_unref(pdfpg->pixbuf); + g_free(pdfpg); } g_list_free(bgpdf.pages); - bgpdf.status = STATUS_SHUTDOWN; - for (list = g_list_last(bgpdf.requests); list != NULL; ) { + for (list = bgpdf.requests; list != NULL; list = list->next) { req = (struct BgPdfRequest *)list->data; - list = list->prev; - cancel_bgpdf_request(req); + g_free(req); + } + g_list_free(bgpdf.requests); + + if (bgpdf.file_contents!=NULL) { + g_free(bgpdf.file_contents); + bgpdf.file_contents = NULL; } - if (!bgpdf.pid) end_bgpdf_shutdown(); - /* The above will ultimately remove all requests and kill the child if needed. - The child will set status to STATUS_NOT_INIT, clear the requests list, - empty tmpdir, ... except if there's no child! */ - /* note: it could look like there's a race condition here - if a child - terminates and a new request is enqueued while we are destroying the - queue - but actually the child handler callback is NOT a signal - callback, so execution of this function is atomic */ + if (bgpdf.document!=NULL) { + g_object_unref(bgpdf.document); + bgpdf.document = NULL; + } + + bgpdf.status = STATUS_NOT_INIT; } + +// initialize PDF background rendering + gboolean init_bgpdf(char *pdfname, gboolean create_pages, int file_domain) { - FILE *f; - gchar *filebuf; - gsize filelen; + int i, n_pages; + struct Background *bg; + struct Page *pg; + PopplerPage *pdfpage; + gdouble width, height; if (bgpdf.status != STATUS_NOT_INIT) return FALSE; - bgpdf.tmpfile_copy = NULL; - bgpdf.tmpdir = mkdtemp(g_strdup(TMPDIR_TEMPLATE)); - if (!bgpdf.tmpdir) return FALSE; - // make a local copy and check if it's a PDF - if (!g_file_get_contents(pdfname, &filebuf, &filelen, NULL)) - { end_bgpdf_shutdown(); return FALSE; } - if (filelen < 4 || strncmp(filebuf, "%PDF", 4)) - { g_free(filebuf); end_bgpdf_shutdown(); return FALSE; } - bgpdf.tmpfile_copy = g_strdup_printf("%s/bg.pdf", bgpdf.tmpdir); - f = fopen(bgpdf.tmpfile_copy, "w"); - if (f == NULL || fwrite(filebuf, 1, filelen, f) != filelen) - { g_free(filebuf); end_bgpdf_shutdown(); return FALSE; } - fclose(f); - g_free(filebuf); - bgpdf.status = STATUS_IDLE; - bgpdf.pid = 0; + + // make a copy of the file in memory and check it's a PDF + if (!g_file_get_contents(pdfname, &(bgpdf.file_contents), &(bgpdf.file_length), NULL)) + return FALSE; + if (bgpdf.file_length < 4 || strncmp(bgpdf.file_contents, "%PDF", 4)) + { g_free(bgpdf.file_contents); bgpdf.file_contents = NULL; return FALSE; } + + // init bgpdf data structures and open poppler document + bgpdf.status = STATUS_READY; bgpdf.filename = new_refstring((file_domain == DOMAIN_ATTACH) ? "bg.pdf" : pdfname); bgpdf.file_domain = file_domain; bgpdf.npages = 0; bgpdf.pages = NULL; bgpdf.requests = NULL; - bgpdf.create_pages = create_pages; + bgpdf.pid = 0; bgpdf.has_failed = FALSE; - add_bgpdf_request(-1, ui.startup_zoom, FALSE); // request all pages - return TRUE; -} -// create page n, resize it, set its bg -void bgpdf_create_page_with_bg(int pageno, struct BgPdfPage *bgpg) -{ - struct Page *pg = NULL; - struct Background *bg; + bgpdf.document = poppler_document_new_from_data(bgpdf.file_contents, bgpdf.file_length, NULL, NULL); + if (bgpdf.document == NULL) shutdown_bgpdf(); - if (journal.npages < pageno) { - bg = g_new(struct Background, 1); - bg->canvas_item = NULL; - } else { - pg = (struct Page *)g_list_nth_data(journal.pages, pageno-1); - bg = pg->bg; - if (bg->type != BG_SOLID) return; - // don't mess with a page the user has modified significantly... - } + if (!create_pages) return TRUE; // we're done - bg->type = BG_PDF; - bg->pixbuf = gdk_pixbuf_ref(bgpg->pixbuf); - bg->filename = refstring_ref(bgpdf.filename); - bg->file_domain = bgpdf.file_domain; - bg->file_page_seq = pageno; - bg->pixbuf_scale = ui.startup_zoom; - bg->pixbuf_dpi = bgpg->dpi; - - if (journal.npages < pageno) { - pg = new_page_with_bg(bg, - gdk_pixbuf_get_width(bg->pixbuf)*72.0/bg->pixbuf_dpi, - gdk_pixbuf_get_height(bg->pixbuf)*72.0/bg->pixbuf_dpi); - journal.pages = g_list_append(journal.pages, pg); - journal.npages++; - } else { - pg->width = gdk_pixbuf_get_width(bgpg->pixbuf)*72.0/bg->pixbuf_dpi; - pg->height = gdk_pixbuf_get_height(bgpg->pixbuf)*72.0/bg->pixbuf_dpi; - make_page_clipbox(pg); - update_canvas_bg(pg); + // create pages with correct sizes if requested + n_pages = poppler_document_get_n_pages(bgpdf.document); + for (i=1; i<=n_pages; i++) { + pdfpage = poppler_document_get_page(bgpdf.document, i-1); + if (!pdfpage) continue; + if (journal.npages < i) { + bg = g_new(struct Background, 1); + bg->canvas_item = NULL; + pg = NULL; + } else { + pg = (struct Page *)g_list_nth_data(journal.pages, i-1); + bg = pg->bg; + } + bg->type = BG_PDF; + bg->filename = refstring_ref(bgpdf.filename); + bg->file_domain = bgpdf.file_domain; + bg->file_page_seq = i; + bg->pixbuf = NULL; + bg->pixbuf_scale = 0; + poppler_page_get_size(pdfpage, &width, &height); + g_object_unref(pdfpage); + if (pg == NULL) { + pg = new_page_with_bg(bg, width, height); + journal.pages = g_list_append(journal.pages, pg); + journal.npages++; + } else { + pg->width = width; + pg->height = height; + make_page_clipbox(pg); + update_canvas_bg(pg); + } } update_page_stuff(); + rescale_bg_pixmaps(); // this actually requests the pages !! + return TRUE; } + // look for all journal pages with given pdf bg, and update their bg pixmaps void bgpdf_update_bg(int pageno, struct BgPdfPage *bgpg) { @@ -1241,7 +1154,8 @@ void bgpdf_update_bg(int pageno, struct BgPdfPage *bgpg) if (pg->bg->type == BG_PDF && pg->bg->file_page_seq == pageno) { if (pg->bg->pixbuf!=NULL) gdk_pixbuf_unref(pg->bg->pixbuf); pg->bg->pixbuf = gdk_pixbuf_ref(bgpg->pixbuf); - pg->bg->pixbuf_dpi = bgpg->dpi; + pg->bg->pixel_width = bgpg->pixel_width; + pg->bg->pixel_height = bgpg->pixel_height; update_canvas_bg(pg); } } @@ -1562,7 +1476,7 @@ void save_config_to_file(void) " antialiased bitmap backgrounds (true/false)", g_strdup(ui.antialias_bg?"true":"false")); update_keyval("paper", "progressive_bg", - " progressive scaling of bitmap backgrounds (true/false)", + " just-in-time update of page backgrounds (true/false)", g_strdup(ui.progressive_bg?"true":"false")); update_keyval("paper", "gs_bitmap_dpi", " bitmap resolution of PS/PDF backgrounds rendered using ghostscript (dpi)", diff --git a/src/xo-file.h b/src/xo-file.h index 5fcf6e8..fab3139 100644 --- a/src/xo-file.h +++ b/src/xo-file.h @@ -23,11 +23,10 @@ GList *attempt_load_gv_bg(char *filename); struct Background *attempt_screenshot_bg(void); void cancel_bgpdf_request(struct BgPdfRequest *req); -void add_bgpdf_request(int pageno, double zoom, gboolean printing); -void bgpdf_spawn_child(void); +void add_bgpdf_request(int pageno, double zoom); +gboolean bgpdf_scheduler_callback(gpointer data); void shutdown_bgpdf(void); gboolean init_bgpdf(char *pdfname, gboolean create_pages, int file_domain); -void end_bgpdf_shutdown(void); void bgpdf_create_page_with_bg(int pageno, struct BgPdfPage *bgpg); void bgpdf_update_bg(int pageno, struct BgPdfPage *bgpg); diff --git a/src/xo-interface.c b/src/xo-interface.c index f10b134..3f761e1 100644 --- a/src/xo-interface.c +++ b/src/xo-interface.c @@ -1911,11 +1911,10 @@ create_winMain (void) gtk_widget_show (labelPage); gtk_box_pack_start (GTK_BOX (hbox1), labelPage, FALSE, FALSE, 0); - spinPageNo_adj = gtk_adjustment_new (1, 1, 1, 1, 1, 1); + spinPageNo_adj = gtk_adjustment_new (1, 1, 1, 1, 0, 0); spinPageNo = gtk_spin_button_new (GTK_ADJUSTMENT (spinPageNo_adj), 1, 0); gtk_widget_show (spinPageNo); gtk_box_pack_start (GTK_BOX (hbox1), spinPageNo, FALSE, TRUE, 0); - gtk_widget_set_size_request (spinPageNo, 39, -1); gtk_tooltips_set_tip (tooltips, spinPageNo, "Set page number", NULL); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinPageNo), TRUE); gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (spinPageNo), TRUE); @@ -1939,6 +1938,7 @@ create_winMain (void) statusbar = gtk_statusbar_new (); gtk_widget_show (statusbar); gtk_box_pack_start (GTK_BOX (hbox1), statusbar, TRUE, TRUE, 0); + gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (statusbar), FALSE); g_signal_connect ((gpointer) winMain, "delete_event", G_CALLBACK (on_winMain_delete_event), @@ -3010,7 +3010,7 @@ create_zoomDialog (void) gtk_radio_button_set_group (GTK_RADIO_BUTTON (radioZoom), radioZoom_group); radioZoom_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radioZoom)); - spinZoom_adj = gtk_adjustment_new (100, 10, 1500, 5, 20, 20); + spinZoom_adj = gtk_adjustment_new (100, 10, 1500, 5, 0, 0); spinZoom = gtk_spin_button_new (GTK_ADJUSTMENT (spinZoom_adj), 1, 0); gtk_widget_show (spinZoom); gtk_box_pack_start (GTK_BOX (hbox4), spinZoom, FALSE, TRUE, 5); diff --git a/src/xo-misc.c b/src/xo-misc.c index 410797c..a5329c3 100644 --- a/src/xo-misc.c +++ b/src/xo-misc.c @@ -351,10 +351,9 @@ void get_pointer_coords(GdkEvent *event, gdouble *ret) void fix_xinput_coords(GdkEvent *event) { -#ifdef ENABLE_XINPUT_BUGFIX double *axes, *px, *py, axis_width; GdkDevice *device; - int wx, wy, sx, sy; + int wx, wy, sx, sy, ix, iy; if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) { axes = event->button.axes; @@ -369,18 +368,26 @@ void fix_xinput_coords(GdkEvent *event) device = event->motion.device; } else return; // nothing we know how to do - + // use canvas window, not event window (else get GTK+ 2.11 bugs!) gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy); gnome_canvas_get_scroll_offsets(canvas, &sx, &sy); - - axis_width = device->axes[0].max - device->axes[0].min; - if (axis_width>EPSILON) - *px = (axes[0]/axis_width)*ui.screen_width + sx - wx; - axis_width = device->axes[1].max - device->axes[1].min; - if (axis_width>EPSILON) - *py = (axes[1]/axis_width)*ui.screen_height + sy - wy; +#ifdef ENABLE_XINPUT_BUGFIX + // fix broken events with the core pointer's location + if (!finite(axes[0]) || !finite(axes[1]) || (axes[0]==0. && axes[1]==0.)) { + gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL); + *px = ix + sx; + *py = iy + sy; + } + else { + axis_width = device->axes[0].max - device->axes[0].min; + if (axis_width>EPSILON) + *px = (axes[0]/axis_width)*ui.screen_width + sx - wx; + axis_width = device->axes[1].max - device->axes[1].min; + if (axis_width>EPSILON) + *py = (axes[1]/axis_width)*ui.screen_height + sy - wy; + } #endif } @@ -512,6 +519,7 @@ void update_canvas_bg(struct Page *pg) double *pt; double x, y; int w, h; + gboolean is_well_scaled; if (pg->bg->canvas_item != NULL) gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item)); @@ -596,15 +604,23 @@ void update_canvas_bg(struct Page *pg) if (pg->bg->type == BG_PDF) { if (pg->bg->pixbuf == NULL) return; - pg->bg->canvas_item = gnome_canvas_item_new(pg->group, - gnome_canvas_pixbuf_get_type(), - "pixbuf", pg->bg->pixbuf, - "width", pg->width, "height", pg->height, - "width-set", TRUE, "height-set", TRUE, - NULL); + is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2. + && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.); + if (is_well_scaled) + pg->bg->canvas_item = gnome_canvas_item_new(pg->group, + gnome_canvas_pixbuf_get_type(), + "pixbuf", pg->bg->pixbuf, + "width-in-pixels", TRUE, "height-in-pixels", TRUE, + NULL); + else + pg->bg->canvas_item = gnome_canvas_item_new(pg->group, + gnome_canvas_pixbuf_get_type(), + "pixbuf", pg->bg->pixbuf, + "width", pg->width, "height", pg->height, + "width-set", TRUE, "height-set", TRUE, + NULL); lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL); } - } gboolean is_visible(struct Page *pg) @@ -624,6 +640,8 @@ void rescale_bg_pixmaps(void) GList *pglist; struct Page *pg; GdkPixbuf *pix; + gboolean is_well_scaled; + gdouble zoom_to_request; for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) { pg = (struct Page *)pglist->data; @@ -649,10 +667,24 @@ void rescale_bg_pixmaps(void) pg->bg->pixbuf_scale = 0; } } - if (pg->bg->type == BG_PDF) { // request an asynchronous update - if (pg->bg->pixbuf_scale == ui.zoom) continue; - add_bgpdf_request(pg->bg->file_page_seq, ui.zoom, FALSE); - pg->bg->pixbuf_scale = ui.zoom; + if (pg->bg->type == BG_PDF) { + // make pixmap scale to correct size if current one is wrong + is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2. + && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.); + if (pg->bg->canvas_item != NULL && !is_well_scaled) { + g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL); + if (is_well_scaled) + gnome_canvas_item_set(pg->bg->canvas_item, + "width", pg->width, "height", pg->height, + "width-in-pixels", FALSE, "height-in-pixels", FALSE, + "width-set", TRUE, "height-set", TRUE, + NULL); + } + // request an asynchronous update to a better pixmap if needed + zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0); + if (pg->bg->pixbuf_scale == zoom_to_request) continue; + add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request); + pg->bg->pixbuf_scale = zoom_to_request; } } } @@ -1591,12 +1623,6 @@ gboolean ok_to_close(void) return TRUE; } -// test if we're still busy loading a PDF background file -gboolean page_ops_forbidden(void) -{ - return (bgpdf.status != STATUS_NOT_INIT && bgpdf.create_pages); -} - // send the focus back to the appropriate widget void reset_focus(void) { @@ -1622,6 +1648,7 @@ void reset_selection(void) update_thickness_buttons(); update_color_buttons(); update_font_button(); + update_cursor(); } void move_journal_items_by(GList *itemlist, double dx, double dy, @@ -1883,6 +1910,8 @@ void allow_all_accels(void) "can-activate-accel", G_CALLBACK(can_accel), NULL); g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"), "can-activate-accel", G_CALLBACK(can_accel), NULL); + g_signal_connect((gpointer) GET_COMPONENT("toolsReco"), + "can-activate-accel", G_CALLBACK(can_accel), NULL); } void add_scroll_bindings(void) diff --git a/src/xo-misc.h b/src/xo-misc.h index 4ade904..d966fa4 100644 --- a/src/xo-misc.h +++ b/src/xo-misc.h @@ -68,7 +68,6 @@ void process_papercolor_activate(GtkMenuItem *menuitem, int color); void process_paperstyle_activate(GtkMenuItem *menuitem, int style); gboolean ok_to_close(void); -gboolean page_ops_forbidden(void); void reset_focus(void); diff --git a/src/xo-paint.c b/src/xo-paint.c index 47fd613..8f6f609 100644 --- a/src/xo-paint.c +++ b/src/xo-paint.c @@ -1077,6 +1077,11 @@ void clipboard_paste(void) gtk_selection_data_free(sel_data); update_copy_paste_enabled(); + update_color_menu(); + update_thickness_buttons(); + update_color_buttons(); + update_font_button(); + update_cursor(); // FIXME: can't know if pointer is within selection! } // modify the color or thickness of pen strokes in a selection diff --git a/src/xo-print.c b/src/xo-print.c index 6a89886..cb480b1 100644 --- a/src/xo-print.c +++ b/src/xo-print.c @@ -711,29 +711,34 @@ int pdf_draw_bitmap_background(struct Page *pg, GString *str, BgPdfPage *pgpdf; GdkPixbuf *pix; GString *zpix; + PopplerPage *pdfpage; char *buf, *p1, *p2; int height, width, stride, x, y, chan; + double pgheight, pgwidth; if (pg->bg->type == BG_PDF) { - pgpdf = (struct BgPdfPage *)g_list_nth_data(bgpdf.pages, pg->bg->file_page_seq-1); - if (pgpdf == NULL) return -1; - if (pgpdf->dpi != PDFTOPPM_PRINTING_DPI) { - add_bgpdf_request(pg->bg->file_page_seq, 0, TRUE); - while (pgpdf->dpi != PDFTOPPM_PRINTING_DPI && bgpdf.status == STATUS_RUNNING) - gtk_main_iteration(); - } - pix = pgpdf->pixbuf; + if (!bgpdf.document) return -1; + pdfpage = poppler_document_get_page(bgpdf.document, pg->bg->file_page_seq-1); + if (!pdfpage) return -1; + poppler_page_get_size(pdfpage, &pgwidth, &pgheight); + width = (int) (PDFTOPPM_PRINTING_DPI * pgwidth/72.0); + height = (int) (PDFTOPPM_PRINTING_DPI * pgheight/72.0); + pix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); + poppler_page_render_to_pixbuf( + pdfpage, 0, 0, width, height, PDFTOPPM_PRINTING_DPI/72.0, 0, pix); + g_object_unref(pdfpage); } - else pix = pg->bg->pixbuf; - - if (gdk_pixbuf_get_bits_per_sample(pix) != 8) return -1; - if (gdk_pixbuf_get_colorspace(pix) != GDK_COLORSPACE_RGB) return -1; + else pix = g_object_ref(pg->bg->pixbuf); + if (gdk_pixbuf_get_bits_per_sample(pix) != 8 || + gdk_pixbuf_get_colorspace(pix) != GDK_COLORSPACE_RGB) + { g_object_unref(pix); return -1; } + width = gdk_pixbuf_get_width(pix); height = gdk_pixbuf_get_height(pix); stride = gdk_pixbuf_get_rowstride(pix); chan = gdk_pixbuf_get_n_channels(pix); - if (chan!=3 && chan!=4) return -1; + if (chan!=3 && chan!=4) { g_object_unref(pix); return -1; } g_string_append_printf(str, "q %.2f 0 0 %.2f 0 %.2f cm /ImBg Do Q ", pg->width, -pg->height, pg->height); @@ -748,6 +753,7 @@ int pdf_draw_bitmap_background(struct Page *pg, GString *str, } zpix = do_deflate(buf, 3*width*height); g_free(buf); + g_object_unref(pix); make_xref(xref, xref->last+1, pdfbuf->len); g_string_append_printf(pdfbuf, @@ -1172,8 +1178,6 @@ gboolean print_to_pdf(char *filename) struct XrefTable xref; GList *pglist; struct Page *pg; - char *buf; - gsize len; gboolean annot, uses_pdf; gboolean use_hiliter; struct PdfInfo pdfinfo; @@ -1195,11 +1199,9 @@ gboolean print_to_pdf(char *filename) } if (uses_pdf && bgpdf.status != STATUS_NOT_INIT && - g_file_get_contents(bgpdf.tmpfile_copy, &buf, &len, NULL) && - !strncmp(buf, "%PDF-1.", 7)) { + bgpdf.file_contents!=NULL && !strncmp(bgpdf.file_contents, "%PDF-1.", 7)) { // parse the existing PDF file - pdfbuf = g_string_new_len(buf, len); - g_free(buf); + pdfbuf = g_string_new_len(bgpdf.file_contents, bgpdf.file_length); if (pdfbuf->str[7]<'4') pdfbuf->str[7] = '4'; // upgrade to 1.4 annot = pdf_parse_info(pdfbuf, &pdfinfo, &xref); if (!annot) { @@ -1407,6 +1409,9 @@ void print_background(GnomePrintContext *gpc, struct Page *pg, gboolean *abort) double x, y; GdkPixbuf *pix; BgPdfPage *pgpdf; + PopplerPage *pdfpage; + int width, height; + double pgwidth, pgheight; if (pg->bg->type == BG_SOLID) { gnome_print_setopacity(gpc, 1.0); @@ -1434,22 +1439,25 @@ void print_background(GnomePrintContext *gpc, struct Page *pg, gboolean *abort) } return; } - else if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) { + else + if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) { if (pg->bg->type == BG_PDF) { - pgpdf = (struct BgPdfPage *)g_list_nth_data(bgpdf.pages, pg->bg->file_page_seq-1); - if (pgpdf == NULL) return; - if (pgpdf->dpi != PDFTOPPM_PRINTING_DPI) { - add_bgpdf_request(pg->bg->file_page_seq, 0, TRUE); - while (pgpdf->dpi != PDFTOPPM_PRINTING_DPI && bgpdf.status == STATUS_RUNNING) { - gtk_main_iteration(); - if (*abort) return; - } - } - pix = pgpdf->pixbuf; + if (!bgpdf.document) return; + pdfpage = poppler_document_get_page(bgpdf.document, pg->bg->file_page_seq-1); + if (!pdfpage) return; + poppler_page_get_size(pdfpage, &pgwidth, &pgheight); + width = (int) (PDFTOPPM_PRINTING_DPI * pgwidth/72.0); + height = (int) (PDFTOPPM_PRINTING_DPI * pgheight/72.0); + pix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); + poppler_page_render_to_pixbuf( + pdfpage, 0, 0, width, height, PDFTOPPM_PRINTING_DPI/72.0, 0, pix); + g_object_unref(pdfpage); } - else pix = pg->bg->pixbuf; - if (gdk_pixbuf_get_bits_per_sample(pix) != 8) return; - if (gdk_pixbuf_get_colorspace(pix) != GDK_COLORSPACE_RGB) return; + else pix = g_object_ref(pg->bg->pixbuf); + + if (gdk_pixbuf_get_bits_per_sample(pix) != 8 || + gdk_pixbuf_get_colorspace(pix) != GDK_COLORSPACE_RGB) + { g_object_unref(pix); return; } gnome_print_gsave(gpc); gnome_print_scale(gpc, pg->width, pg->height); gnome_print_translate(gpc, 0., -1.); @@ -1459,6 +1467,7 @@ void print_background(GnomePrintContext *gpc, struct Page *pg, gboolean *abort) else if (gdk_pixbuf_get_n_channels(pix) == 4) gnome_print_rgbaimage(gpc, gdk_pixbuf_get_pixels(pix), gdk_pixbuf_get_width(pix), gdk_pixbuf_get_height(pix), gdk_pixbuf_get_rowstride(pix)); + g_object_unref(pix); gnome_print_grestore(gpc); return; } diff --git a/src/xournal.h b/src/xournal.h index aadc1ea..f6cc967 100644 --- a/src/xournal.h +++ b/src/xournal.h @@ -1,7 +1,8 @@ #include #include +#include -/* #define INPUT_DEBUG */ +// #define INPUT_DEBUG /* uncomment this line if you experience event-processing problems and want to list the input events received by xournal. Caution, lots of output (redirect to a file). */ @@ -27,6 +28,7 @@ #define DISPLAY_DPI_DEFAULT 96.0 #define MIN_ZOOM 0.2 #define RESIZE_MARGIN 6.0 +#define MAX_SAFE_RENDER_DPI 720 // max dpi at which PDF bg's get rendered #define VBOX_MAIN_NITEMS 5 // number of interface items in vboxMain @@ -53,9 +55,9 @@ typedef struct Background { Refstring *filename; int file_domain; int file_page_seq; - int pixbuf_dpi; // for PDF only - the *current* dpi value double pixbuf_scale; // for PIXMAP, this is the *current* zoom value // for PDF, this is the *requested* zoom value + int pixel_height, pixel_width; // PDF only: pixel size of current pixbuf } Background; #define BG_SOLID 0 @@ -310,35 +312,32 @@ typedef struct UndoItem { typedef struct BgPdfRequest { int pageno; - int dpi; - gboolean initial_request; // if so, loop over page numbers - gboolean is_printing; // this is for printing, not for display + double dpi; } BgPdfRequest; typedef struct BgPdfPage { - int dpi; + double dpi; GdkPixbuf *pixbuf; + int pixel_height, pixel_width; // pixel size of pixbuf } BgPdfPage; typedef struct BgPdf { int status; // the rest only makes sense if this is not STATUS_NOT_INIT - int pid; // PID of the converter process + guint pid; // the identifier of the idle callback Refstring *filename; int file_domain; - gchar *tmpfile_copy; // the temporary work copy of the file (in tmpdir) + gchar *file_contents; // buffer containing a copy of file data + gsize file_length; // size of above buffer int npages; GList *pages; // a list of BgPdfPage structures GList *requests; // a list of BgPdfRequest structures - gchar *tmpdir; // where to look for pages coming from pdf converter - gboolean create_pages; // create journal pages as we find stuff in PDF gboolean has_failed; // has failed in the past... + PopplerDocument *document; // the poppler document } BgPdf; #define STATUS_NOT_INIT 0 -#define STATUS_IDLE 1 -#define STATUS_RUNNING 2 // currently running child process on head(requests) -#define STATUS_ABORTED 3 // child process running, but head(requests) aborted -#define STATUS_SHUTDOWN 4 // waiting for child process to shut down +#define STATUS_READY 1 // things are initialized and can work +// there used to be more possible values, things got streamlined... // UTILITY MACROS diff --git a/xournal.glade b/xournal.glade index 0b01c6e..0476060 100644 --- a/xournal.glade +++ b/xournal.glade @@ -3081,7 +3081,6 @@ - 39 True Set page number True @@ -3091,7 +3090,7 @@ GTK_UPDATE_ALWAYS True False - 1 1 1 1 1 1 + 1 1 1 1 0 0 @@ -3179,7 +3178,7 @@ True - True + False 0 @@ -3681,7 +3680,7 @@ points GTK_UPDATE_ALWAYS False False - 100 10 1500 5 20 20 + 100 10 1500 5 0 0