X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=src%2Fxo-file.c;h=7d2786036f38feb0e4a1231e176ba97d7f2dcca4;hb=e648cece76b671b5b0e165acc6d14178e8de1000;hp=2ac09fd9964966b59871bbb80c12e0a02ea88bce;hpb=5b10083203ad4e798c25ed35110ab7e7440656b8;p=xournal.git diff --git a/src/xo-file.c b/src/xo-file.c index 2ac09fd..7d27860 100644 --- a/src/xo-file.c +++ b/src/xo-file.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include "xournal.h" #include "xo-interface.h" @@ -75,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; @@ -118,14 +119,16 @@ gboolean save_journal(const char *filename) if (!gdk_pixbuf_save(pg->bg->pixbuf, tmpfn, "png", NULL, NULL)) { dialog = gtk_message_dialog_new(GTK_WINDOW(winMain), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, - "Could not write background '%s'. Continuing anyway.", tmpfn); + _("Could not write background '%s'. Continuing anyway."), tmpfn); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } g_free(tmpfn); } + tmpstr = g_markup_escape_text(pg->bg->filename->s, -1); gzprintf(f, "domain=\"%s\" filename=\"%s\" ", - file_domain_names[pg->bg->file_domain], pg->bg->filename->s); + file_domain_names[pg->bg->file_domain], tmpstr); + g_free(tmpstr); } } else if (pg->bg->type == BG_PDF) { @@ -138,26 +141,26 @@ 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) { dialog = gtk_message_dialog_new(GTK_WINDOW(winMain), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, - "Could not write background '%s'. Continuing anyway.", tmpfn); + _("Could not write background '%s'. Continuing anyway."), tmpfn); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } g_free(tmpfn); } + tmpstr = g_markup_escape_text(pg->bg->filename->s, -1); gzprintf(f, "domain=\"%s\" filename=\"%s\" ", - file_domain_names[pg->bg->file_domain], pg->bg->filename->s); + file_domain_names[pg->bg->file_domain], tmpstr); + g_free(tmpstr); } gzprintf(f, "pageno=\"%d\" ", pg->bg->file_page_seq); } @@ -174,14 +177,20 @@ gboolean save_journal(const char *filename) gzputs(f, color_names[item->brush.color_no]); else gzprintf(f, "#%08x", item->brush.color_rgba); - gzprintf(f, "\" width=\"%.2f\">\n", item->brush.thickness); + gzprintf(f, "\" width=\"%.2f", item->brush.thickness); + if (item->brush.variable_width) + for (i=0;ipath->num_points-1;i++) + gzprintf(f, " %.2f", item->widths[i]); + gzprintf(f, "\">\n"); for (i=0;i<2*item->path->num_points;i++) gzprintf(f, "%.2f ", item->path->coords[i]); gzprintf(f, "\n\n"); } if (item->type == ITEM_TEXT) { + tmpstr = g_markup_escape_text(item->font_name, -1); gzprintf(f, "font_name, item->font_size, item->bbox.left, item->bbox.top); + tmpstr, item->font_size, item->bbox.left, item->bbox.top); + g_free(tmpstr); if (item->brush.color_no >= 0) gzputs(f, color_names[item->brush.color_no]); else @@ -189,7 +198,6 @@ gboolean save_journal(const char *filename) tmpstr = g_markup_escape_text(item->text, -1); gzprintf(f, "\">%s\n", tmpstr); g_free(tmpstr); - } } gzprintf(f, "\n"); @@ -240,7 +248,7 @@ struct Background *tmpBg_pdf; GError *xoj_invalid(void) { - return g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid file contents"); + return g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("Invalid file contents")); } void xoj_parser_start_element(GMarkupParseContext *context, @@ -248,9 +256,10 @@ void xoj_parser_start_element(GMarkupParseContext *context, const gchar **attribute_values, gpointer user_data, GError **error) { int has_attr, i; - char *ptr; + char *ptr, *tmpptr; struct Background *tmpbg; char *tmpbg_filename; + gdouble val; GtkWidget *dialog; if (!strcmp(element_name, "title") || !strcmp(element_name, "xournal")) { @@ -389,7 +398,7 @@ void xoj_parser_start_element(GMarkupParseContext *context, if (tmpPage->bg->pixbuf == NULL) { dialog = gtk_message_dialog_new(GTK_WINDOW(winMain), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, - "Could not open background '%s'. Setting background to white.", + _("Could not open background '%s'. Setting background to white."), tmpbg_filename); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); @@ -438,6 +447,7 @@ void xoj_parser_start_element(GMarkupParseContext *context, tmpItem->type = ITEM_STROKE; tmpItem->path = NULL; tmpItem->canvas_item = NULL; + tmpItem->widths = NULL; tmpLayer->items = g_list_append(tmpLayer->items, tmpItem); tmpLayer->nitems++; // scan for tool, color, and width attributes @@ -448,6 +458,20 @@ void xoj_parser_start_element(GMarkupParseContext *context, cleanup_numeric((gchar *)*attribute_values); tmpItem->brush.thickness = g_ascii_strtod(*attribute_values, &ptr); if (ptr == *attribute_values) *error = xoj_invalid(); + i = 0; + while (*ptr!=0) { + realloc_cur_widths(i+1); + ui.cur_widths[i] = g_ascii_strtod(ptr, &tmpptr); + if (tmpptr == ptr) break; + ptr = tmpptr; + i++; + } + tmpItem->brush.variable_width = (i>0); + if (i>0) { + tmpItem->brush.variable_width = TRUE; + tmpItem->widths = (gdouble *) g_memdup(ui.cur_widths, i*sizeof(gdouble)); + ui.cur_path.num_points = i+1; + } has_attr |= 1; } else if (!strcmp(*attribute_names, "color")) { @@ -483,6 +507,8 @@ void xoj_parser_start_element(GMarkupParseContext *context, // finish filling the brush info tmpItem->brush.thickness_no = 0; // who cares ? tmpItem->brush.tool_options = 0; // who cares ? + tmpItem->brush.ruler = FALSE; + tmpItem->brush.recognizer = FALSE; if (tmpItem->brush.tool_type == TOOL_HIGHLIGHTER) { if (tmpItem->brush.color_no >= 0) tmpItem->brush.color_rgba &= ui.hiliter_alpha_mask; @@ -603,9 +629,15 @@ void xoj_parser_text(GMarkupParseContext *context, if (ptr == text) break; text_len -= (ptr - text); text = ptr; + if (!finite(ui.cur_path.coords[n])) { + if (n>=2) ui.cur_path.coords[n] = ui.cur_path.coords[n-2]; + else ui.cur_path.coords[n] = 0; + } n++; } - if (n<4 || n&1) { *error = xoj_invalid(); return; } + if (n<4 || n&1 || + (tmpItem->brush.variable_width && (n!=2*ui.cur_path.num_points))) + { *error = xoj_invalid(); return; } // wrong number of points tmpItem->path = gnome_canvas_points_new(n/2); g_memmove(tmpItem->path->coords, ui.cur_path.coords, n*sizeof(double)); } @@ -624,20 +656,20 @@ gboolean user_wants_second_chance(char **filename) dialog = gtk_message_dialog_new(GTK_WINDOW(winMain), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_YES_NO, - "Could not open background '%s'.\nSelect another file?", + _("Could not open background '%s'.\nSelect another file?"), *filename); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if (response != GTK_RESPONSE_YES) return FALSE; - dialog = gtk_file_chooser_dialog_new("Open PDF", GTK_WINDOW (winMain), + dialog = gtk_file_chooser_dialog_new(_("Open PDF"), GTK_WINDOW (winMain), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); filt_all = gtk_file_filter_new(); - gtk_file_filter_set_name(filt_all, "All files"); + gtk_file_filter_set_name(filt_all, _("All files")); gtk_file_filter_add_pattern(filt_all, "*"); filt_pdf = gtk_file_filter_new(); - gtk_file_filter_set_name(filt_pdf, "PDF files"); + gtk_file_filter_set_name(filt_pdf, _("PDF files")); gtk_file_filter_add_pattern(filt_pdf, "*.pdf"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_pdf); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_all); @@ -737,7 +769,7 @@ gboolean open_journal(char *filename) bgpdf.filename = refstring_ref(tmpBg_pdf->filename); } else { dialog = gtk_message_dialog_new(GTK_WINDOW(winMain), GTK_DIALOG_MODAL, - GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not open background '%s'.", + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not open background '%s'."), tmpfn); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); @@ -755,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; } @@ -858,10 +891,8 @@ struct Background *attempt_screenshot_bg(void) struct Background *bg; GdkPixbuf *pix; XEvent x_event; - GError *error = NULL; GdkWindow *window; - int x,y,w,h, status; - unsigned int tmp; + int x,y,w,h; Window x_root, x_win; x_root = gdk_x11_get_default_root_xwindow(); @@ -897,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) @@ -922,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("DEBUG: 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("DEBUG: 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; @@ -972,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("DEBUG: 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 */ @@ -1100,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); } - 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 */ + g_list_free(bgpdf.requests); + + if (bgpdf.file_contents!=NULL) { + g_free(bgpdf.file_contents); + bgpdf.file_contents = NULL; + } + 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; - 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) { @@ -1208,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); } } @@ -1323,6 +1270,10 @@ void init_config_default(void) ui.view_continuous = TRUE; ui.allow_xinput = TRUE; ui.discard_corepointer = FALSE; + ui.left_handed = FALSE; + ui.shorten_menus = FALSE; + ui.shorten_menu_items = g_strdup(DEFAULT_SHORTEN_MENUS); + ui.auto_save_prefs = FALSE; ui.bg_apply_all_pages = FALSE; ui.use_erasertip = FALSE; ui.window_default_width = 720; @@ -1339,6 +1290,9 @@ void init_config_default(void) ui.default_path = NULL; ui.default_font_name = g_strdup(DEFAULT_FONT); ui.default_font_size = DEFAULT_FONT_SIZE; + ui.pressure_sensitivity = FALSE; + ui.width_minimum_multiplier = 0.0; + ui.width_maximum_multiplier = 1.25; // the default UI vertical order ui.vertical_order[0][0] = 1; @@ -1352,10 +1306,8 @@ void init_config_default(void) ui.vertical_order[1][3] = ui.vertical_order[1][4] = -1; ui.toolno[0] = ui.startuptool = TOOL_PEN; - ui.ruler[0] = ui.startupruler = FALSE; for (i=1; i<=NUM_BUTTONS; i++) { ui.toolno[i] = TOOL_ERASER; - ui.ruler[i] = FALSE; } for (i=0; i<=NUM_BUTTONS; i++) ui.linked_brush[i] = BRUSH_LINKED; @@ -1365,6 +1317,9 @@ void init_config_default(void) for (i=0; i < NUM_STROKE_TOOLS; i++) { ui.brushes[0][i].thickness_no = THICKNESS_MEDIUM; ui.brushes[0][i].tool_options = 0; + ui.brushes[0][i].ruler = FALSE; + ui.brushes[0][i].recognizer = FALSE; + ui.brushes[0][i].variable_width = FALSE; } for (i=0; i< NUM_STROKE_TOOLS; i++) for (j=1; j<=NUM_BUTTONS; j++) @@ -1424,177 +1379,217 @@ void save_config_to_file(void) &ui.window_default_width, &ui.window_default_height); update_keyval("general", "display_dpi", - " the display resolution, in pixels per inch", + _(" the display resolution, in pixels per inch"), g_strdup_printf("%.2f", DEFAULT_ZOOM*72)); update_keyval("general", "initial_zoom", - " the initial zoom level, in percent", + _(" the initial zoom level, in percent"), g_strdup_printf("%.2f", 100*ui.zoom/DEFAULT_ZOOM)); update_keyval("general", "window_maximize", - " maximize the window at startup (true/false)", + _(" maximize the window at startup (true/false)"), g_strdup(ui.maximize_at_start?"true":"false")); update_keyval("general", "window_fullscreen", - " start in full screen mode (true/false)", + _(" start in full screen mode (true/false)"), g_strdup(ui.fullscreen?"true":"false")); update_keyval("general", "window_width", - " the window width in pixels (when not maximized)", + _(" the window width in pixels (when not maximized)"), g_strdup_printf("%d", ui.window_default_width)); update_keyval("general", "window_height", - " the window height in pixels", + _(" the window height in pixels"), g_strdup_printf("%d", ui.window_default_height)); update_keyval("general", "scrollbar_speed", - " scrollbar step increment (in pixels)", + _(" scrollbar step increment (in pixels)"), g_strdup_printf("%d", ui.scrollbar_step_increment)); update_keyval("general", "zoom_dialog_increment", - " the step increment in the zoom dialog box", + _(" the step increment in the zoom dialog box"), g_strdup_printf("%d", ui.zoom_step_increment)); update_keyval("general", "zoom_step_factor", - " the multiplicative factor for zoom in/out", + _(" the multiplicative factor for zoom in/out"), g_strdup_printf("%.3f", ui.zoom_step_factor)); update_keyval("general", "view_continuous", - " document view (true = continuous, false = single page)", + _(" document view (true = continuous, false = single page)"), g_strdup(ui.view_continuous?"true":"false")); update_keyval("general", "use_xinput", - " use XInput extensions (true/false)", + _(" use XInput extensions (true/false)"), g_strdup(ui.allow_xinput?"true":"false")); update_keyval("general", "discard_corepointer", - " discard Core Pointer events in XInput mode (true/false)", + _(" discard Core Pointer events in XInput mode (true/false)"), g_strdup(ui.discard_corepointer?"true":"false")); update_keyval("general", "use_erasertip", - " always map eraser tip to eraser (true/false)", + _(" always map eraser tip to eraser (true/false)"), g_strdup(ui.use_erasertip?"true":"false")); update_keyval("general", "default_path", - " default path for open/save (leave blank for current directory)", + _(" default path for open/save (leave blank for current directory)"), g_strdup((ui.default_path!=NULL)?ui.default_path:"")); + update_keyval("general", "pressure_sensitivity", + _(" use pressure sensitivity to control pen stroke width (true/false)"), + g_strdup(ui.pressure_sensitivity?"true":"false")); + update_keyval("general", "width_minimum_multiplier", + _(" minimum width multiplier"), + g_strdup_printf("%.2f", ui.width_minimum_multiplier)); + update_keyval("general", "width_maximum_multiplier", + _(" maximum width multiplier"), + g_strdup_printf("%.2f", ui.width_maximum_multiplier)); update_keyval("general", "interface_order", - " interface components from top to bottom\n valid values: drawarea menu main_toolbar pen_toolbar statusbar", + _(" interface components from top to bottom\n valid values: drawarea menu main_toolbar pen_toolbar statusbar"), verbose_vertical_order(ui.vertical_order[0])); update_keyval("general", "interface_fullscreen", - " interface components in fullscreen mode, from top to bottom", + _(" interface components in fullscreen mode, from top to bottom"), verbose_vertical_order(ui.vertical_order[1])); + update_keyval("general", "interface_lefthanded", + _(" interface has left-handed scrollbar (true/false)"), + g_strdup(ui.left_handed?"true":"false")); + update_keyval("general", "shorten_menus", + _(" hide some unwanted menu or toolbar items (true/false)"), + g_strdup(ui.shorten_menus?"true":"false")); + update_keyval("general", "shorten_menu_items", + _(" interface items to hide (customize at your own risk!)\n see source file xo-interface.c for a list of item names"), + g_strdup(ui.shorten_menu_items)); update_keyval("general", "highlighter_opacity", - " highlighter opacity (0 to 1, default 0.5)\n warning: opacity level is not saved in xoj files!", + _(" highlighter opacity (0 to 1, default 0.5)\n warning: opacity level is not saved in xoj files!"), g_strdup_printf("%.2f", ui.hiliter_opacity)); + update_keyval("general", "autosave_prefs", + _(" auto-save preferences on exit (true/false)"), + g_strdup(ui.auto_save_prefs?"true":"false")); update_keyval("paper", "width", - " the default page width, in points (1/72 in)", + _(" the default page width, in points (1/72 in)"), g_strdup_printf("%.2f", ui.default_page.width)); update_keyval("paper", "height", - " the default page height, in points (1/72 in)", + _(" the default page height, in points (1/72 in)"), g_strdup_printf("%.2f", ui.default_page.height)); update_keyval("paper", "color", - " the default paper color", + _(" the default paper color"), g_strdup(bgcolor_names[ui.default_page.bg->color_no])); update_keyval("paper", "style", - " the default paper style (plain, lined, ruled, or graph)", + _(" the default paper style (plain, lined, ruled, or graph)"), g_strdup(bgstyle_names[ui.default_page.bg->ruling])); update_keyval("paper", "apply_all", - " apply paper style changes to all pages (true/false)", + _(" apply paper style changes to all pages (true/false)"), g_strdup(ui.bg_apply_all_pages?"true":"false")); update_keyval("paper", "default_unit", - " preferred unit (cm, in, px, pt)", + _(" preferred unit (cm, in, px, pt)"), g_strdup(unit_names[ui.default_unit])); update_keyval("paper", "print_ruling", - " include paper ruling when printing or exporting to PDF (true/false)", + _(" include paper ruling when printing or exporting to PDF (true/false)"), g_strdup(ui.print_ruling?"true":"false")); update_keyval("paper", "antialias_bg", - " antialiased bitmap backgrounds (true/false)", + _(" 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)", + _(" bitmap resolution of PS/PDF backgrounds rendered using ghostscript (dpi)"), g_strdup_printf("%d", GS_BITMAP_DPI)); update_keyval("paper", "pdftoppm_printing_dpi", - " bitmap resolution of PDF backgrounds when printing with libgnomeprint (dpi)", + _(" bitmap resolution of PDF backgrounds when printing with libgnomeprint (dpi)"), g_strdup_printf("%d", PDFTOPPM_PRINTING_DPI)); update_keyval("tools", "startup_tool", - " selected tool at startup (pen, eraser, highlighter, selectrect, vertspace, hand)", + _(" selected tool at startup (pen, eraser, highlighter, selectrect, vertspace, hand)"), g_strdup(tool_names[ui.startuptool])); - update_keyval("tools", "startup_ruler", - " ruler mode at startup (true/false) (for pen or highlighter only)", - g_strdup(ui.startupruler?"true":"false")); update_keyval("tools", "pen_color", - " default pen color", + _(" default pen color"), g_strdup(color_names[ui.default_brushes[TOOL_PEN].color_no])); update_keyval("tools", "pen_thickness", - " default pen thickness (fine = 1, medium = 2, thick = 3)", + _(" default pen thickness (fine = 1, medium = 2, thick = 3)"), g_strdup_printf("%d", ui.default_brushes[TOOL_PEN].thickness_no)); + update_keyval("tools", "pen_ruler", + _(" default pen is in ruler mode (true/false)"), + g_strdup(ui.default_brushes[TOOL_PEN].ruler?"true":"false")); + update_keyval("tools", "pen_recognizer", + _(" default pen is in shape recognizer mode (true/false)"), + g_strdup(ui.default_brushes[TOOL_PEN].recognizer?"true":"false")); update_keyval("tools", "eraser_thickness", - " default eraser thickness (fine = 1, medium = 2, thick = 3)", + _(" default eraser thickness (fine = 1, medium = 2, thick = 3)"), g_strdup_printf("%d", ui.default_brushes[TOOL_ERASER].thickness_no)); update_keyval("tools", "eraser_mode", - " default eraser mode (standard = 0, whiteout = 1, strokes = 2)", + _(" default eraser mode (standard = 0, whiteout = 1, strokes = 2)"), g_strdup_printf("%d", ui.default_brushes[TOOL_ERASER].tool_options)); update_keyval("tools", "highlighter_color", - " default highlighter color", + _(" default highlighter color"), g_strdup(color_names[ui.default_brushes[TOOL_HIGHLIGHTER].color_no])); update_keyval("tools", "highlighter_thickness", - " default highlighter thickness (fine = 1, medium = 2, thick = 3)", + _(" default highlighter thickness (fine = 1, medium = 2, thick = 3)"), g_strdup_printf("%d", ui.default_brushes[TOOL_HIGHLIGHTER].thickness_no)); + update_keyval("tools", "highlighter_ruler", + _(" default highlighter is in ruler mode (true/false)"), + g_strdup(ui.default_brushes[TOOL_HIGHLIGHTER].ruler?"true":"false")); + update_keyval("tools", "highlighter_recognizer", + _(" default highlighter is in shape recognizer mode (true/false)"), + g_strdup(ui.default_brushes[TOOL_HIGHLIGHTER].recognizer?"true":"false")); update_keyval("tools", "btn2_tool", - " button 2 tool (pen, eraser, highlighter, text, selectrect, vertspace, hand)", + _(" button 2 tool (pen, eraser, highlighter, text, selectrect, vertspace, hand)"), g_strdup(tool_names[ui.toolno[1]])); update_keyval("tools", "btn2_linked", - " button 2 brush linked to primary brush (true/false) (overrides all other settings)", + _(" button 2 brush linked to primary brush (true/false) (overrides all other settings)"), g_strdup((ui.linked_brush[1]==BRUSH_LINKED)?"true":"false")); - update_keyval("tools", "btn2_ruler", - " button 2 ruler mode (true/false) (for pen or highlighter only)", - g_strdup(ui.ruler[1]?"true":"false")); update_keyval("tools", "btn2_color", - " button 2 brush color (for pen or highlighter only)", + _(" button 2 brush color (for pen or highlighter only)"), g_strdup((ui.toolno[1]