Alvaro, Kit Barnes, Eduardo de Barros Lima, Mathieu Bouchard,
Ole Joergen Broenner, Robert Buchholz, Vincenzo Ciancia, Luca de Cicco,
-Michele Codutti, Robert Gerlach, Daniel German, Dirk Gerrits,
+Michele Codutti, Robert Gerlach, Daniel German, Dirk Gerrits, Simon Guest,
Lukasz Kaiser, Timo Kluck, David Kolibac, Danny Kukawka, Stefan Lembach,
Bob McElrath, Andy Neitzke, David Planella, Marco Poletti, Alex Ray,
-Jean-Baptiste Rouquier, Marco Souza, Mike Ter Louw, Uwe Winter, Lu Zhihe.
+Jean-Baptiste Rouquier, Victor Saase, Marco Souza, Mike Ter Louw,
+Uwe Winter, Lu Zhihe.
(Let me know if you are missing from this list or if your name is
mis-spelled)
+This version:
+ - insert image tool (based on patches by Victor Saase and Simon Guest)
+ - renamed "Journal" menu to "Page"
+
Version 0.4.6 (May 22, 2012):
- win32 portability code (contributed by Dirk Gerrits)
- fix bug in PDF export code on 64-bit systems (patch by Robert Buchholz)
# Define the identity of the package.
PACKAGE=xournal
- VERSION=0.4.6
+ VERSION=0.4.6+image
cat >>confdefs.h <<_ACEOF
dnl Process this file with autoconf to produce a configure script.
AC_INIT(configure.in)
-AM_INIT_AUTOMAKE(xournal, 0.4.6)
+AM_INIT_AUTOMAKE(xournal, 0.4.6+image)
AM_CONFIG_HEADER(config.h)
AM_MAINTAINER_MODE
xo-misc.c xo-misc.h \
xo-file.c xo-file.h \
xo-paint.c xo-paint.h \
+ xo-clipboard.c xo-clipboard.h \
+ xo-image.c xo-image.h \
xo-print.c xo-print.h \
xo-support.c xo-support.h \
xo-interface.c xo-interface.h \
if WIN32
xournal_LDFLAGS = -mwindows
- xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS)
+ xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lz
else
- xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11
+ xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11 -lz
endif
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
am_xournal_OBJECTS = main.$(OBJEXT) xo-misc.$(OBJEXT) \
- xo-file.$(OBJEXT) xo-paint.$(OBJEXT) xo-print.$(OBJEXT) \
- xo-support.$(OBJEXT) xo-interface.$(OBJEXT) \
- xo-callbacks.$(OBJEXT) xo-shapes.$(OBJEXT)
+ xo-file.$(OBJEXT) xo-paint.$(OBJEXT) xo-clipboard.$(OBJEXT) \
+ xo-image.$(OBJEXT) xo-print.$(OBJEXT) xo-support.$(OBJEXT) \
+ xo-interface.$(OBJEXT) xo-callbacks.$(OBJEXT) \
+ xo-shapes.$(OBJEXT)
xournal_OBJECTS = $(am_xournal_OBJECTS)
am__DEPENDENCIES_1 =
@WIN32_FALSE@xournal_DEPENDENCIES = ttsubset/libttsubset.a \
xo-misc.c xo-misc.h \
xo-file.c xo-file.h \
xo-paint.c xo-paint.h \
+ xo-clipboard.c xo-clipboard.h \
+ xo-image.c xo-image.h \
xo-print.c xo-print.h \
xo-support.c xo-support.h \
xo-interface.c xo-interface.h \
xo-shapes.c xo-shapes.h
@WIN32_TRUE@xournal_LDFLAGS = -mwindows
-@WIN32_FALSE@xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11
-@WIN32_TRUE@xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS)
+@WIN32_FALSE@xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11 -lz
+@WIN32_TRUE@xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lz
all: all-recursive
.SUFFIXES:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-callbacks.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-clipboard.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-file.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-image.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-interface.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-misc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-paint.Po@am__quote@
xournal). (cf #2944459). Recalibration probably requires talking directly
to X server to query input device geometry and compensate for GTK.
+TODO WITH IMAGE PATCH:
+TODO: paste and selresize don't put rectangle at top
+TODO: select click in image selects it
+TODO: option for image mode to revert to previous tool
+TODO: direct paste text
+
BUGS:
- lingering issues with synaptics touchpads? (#2872667)
(todo) - set device to Absolute mode at startup? (GDK doesn't expose API,
- export to PDF should see cropbox size if smaller, and map the
cropbox -- not the entire page -- to the page area! (2009-11-18 forum)
+BEHAVIOR TODO:
+- option to have "Save Prefs" save current brushes as settings, not defaults
+- method to map more general devices to specific tools or to Ignore
+ (e.g.: X220T ignore trackpoint; map touchscreen to Hand, eraser to Eraser)
+
WIN32:
- test further
- write an installer
- proximity detection: eraser proximity switches mapping?
proximity out removes cursor until next motionnotify?
-
- 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.
+ NO: render using Cairo !!!
(copy-paste: config option to render only current layer or all below?)
- cut-and-paste of selection into other apps (as bitmap; as SVG?)
- add config option to limit total memory usage for PDF bitmaps
- ability to select entire page for copy-paste (as bitmap / reorder xournal)
- copy/paste of an entire page (beware if PDF bg is not compatible!)
-- rewrite printing using GtkPrint + Cairo as GnomePrint replacement
- (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???
- export as SVG, as bitmap (use Cairo for this)
if (undo == NULL) return; // nothing to undo!
reset_selection(); // safer
reset_recognizer(); // safer
- if (undo->type == ITEM_STROKE || undo->type == ITEM_TEXT) {
+ if (undo->type == ITEM_STROKE || undo->type == ITEM_TEXT || undo->type == ITEM_IMAGE) {
// we're keeping the stroke info, but deleting the canvas item
gtk_object_destroy(GTK_OBJECT(undo->item->canvas_item));
undo->item->canvas_item = NULL;
if (redo == NULL) return; // nothing to redo!
reset_selection(); // safer
reset_recognizer(); // safer
- if (redo->type == ITEM_STROKE || redo->type == ITEM_TEXT) {
+ if (redo->type == ITEM_STROKE || redo->type == ITEM_TEXT || redo->type == ITEM_IMAGE) {
// re-create the canvas_item
make_canvas_item_one(redo->layer->group, redo->item);
// reinsert the item on its layer
if (it->type == ITEM_TEXT && it->canvas_item != NULL)
gnome_canvas_item_set(it->canvas_item,
"fill-color-rgba", it->brush.color_rgba, NULL);
+ if (it->type == ITEM_IMAGE && it->canvas_item != NULL) {
+ // remark: a variable-width item might have lost its variable-width
+ group = (GnomeCanvasGroup *) it->canvas_item->parent;
+ gtk_object_destroy(GTK_OBJECT(it->canvas_item));
+ make_canvas_item_one(group, it);
+ }
}
}
else if (redo->type == ITEM_TEXT_EDIT) {
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
do_switch_page(ui.pageno, TRUE, TRUE);
}
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
do_switch_page(ui.pageno, TRUE, TRUE);
}
}
+void
+on_toolsImage_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
+ if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
+ return;
+ } else {
+ if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
+ return;
+ }
+
+ if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
+ if (ui.toolno[ui.cur_mapping] == TOOL_IMAGE) return;
+
+ ui.cur_mapping = 0; // don't use switch_mapping() (refreshes buttons too soon)
+ end_text();
+ reset_selection();
+ ui.toolno[ui.cur_mapping] = TOOL_IMAGE;
+ update_mapping_linkings(-1);
+ update_tool_buttons();
+ update_tool_menu();
+ update_color_menu();
+ update_cursor();
+}
+
+
void
on_toolsSelectRegion_activate (GtkMenuItem *menuitem,
gpointer user_data)
gpointer user_data)
{
double pt[2];
- gboolean page_change;
- struct Page *tmppage;
GtkWidget *dialog;
int mapping;
gboolean is_core;
else mapping = event->button-1;
// check whether we're in a page
- page_change = FALSE;
- tmppage = ui.cur_page;
get_pointer_coords((GdkEvent *)event, pt);
- while (ui.view_continuous && (pt[1] < - VIEW_CONTINUOUS_SKIP)) {
- if (ui.pageno == 0) break;
- page_change = TRUE;
- ui.pageno--;
- tmppage = g_list_nth_data(journal.pages, ui.pageno);
- pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
- }
- while (ui.view_continuous && (pt[1] > tmppage->height + VIEW_CONTINUOUS_SKIP)) {
- if (ui.pageno == journal.npages-1) break;
- pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
- page_change = TRUE;
- ui.pageno++;
- tmppage = g_list_nth_data(journal.pages, ui.pageno);
- }
- if (page_change) do_switch_page(ui.pageno, FALSE, FALSE);
+ set_current_page(pt);
// can't paint on the background...
else if (ui.toolno[mapping] == TOOL_TEXT) {
start_text((GdkEvent *)event, NULL);
}
+ else if (ui.toolno[mapping] == TOOL_IMAGE) {
+ insert_image((GdkEvent *)event);
+ }
return FALSE;
}
or if there's a selection (then we might want to change the mouse
cursor to indicate the possibility of resizing) */
if (ui.cur_item_type == ITEM_NONE && ui.selection==NULL) return FALSE;
- if (ui.cur_item_type == ITEM_TEXT) return FALSE;
+ if (ui.cur_item_type == ITEM_TEXT || ui.cur_item_type == ITEM_IMAGE) return FALSE;
is_core = (event->device == gdk_device_get_core_pointer());
if (!ui.use_xinput && !is_core) return FALSE;
}
+void
+on_button2Image_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ process_mapping_activate(menuitem, 1, TOOL_IMAGE);
+}
+
+
void
on_button2SelectRegion_activate (GtkMenuItem *menuitem,
gpointer user_data)
}
+void
+on_button3Image_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ process_mapping_activate(menuitem, 2, TOOL_IMAGE);
+}
+
+
void
on_button3SelectRegion_activate (GtkMenuItem *menuitem,
gpointer user_data)
gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
rescale_text_items();
rescale_bg_pixmaps();
+ rescale_images();
}
} while (response == GTK_RESPONSE_APPLY);
ui.button_switch_mapping = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem));
}
+
void
on_optionsButtonsSwitchMappings_activate(GtkMenuItem *menuitem,
gpointer user_data);
+
+void
+on_toolsImage_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_button2Image_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
+
+void
+on_button3Image_activate (GtkMenuItem *menuitem,
+ gpointer user_data);
--- /dev/null
+/*
+ * This program 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "xournal.h"
+#include "xo-callbacks.h"
+#include "xo-interface.h"
+#include "xo-support.h"
+#include "xo-misc.h"
+#include "xo-paint.h"
+#include "xo-image.h"
+
+
+void callback_clipboard_get(GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ guint info, gpointer user_data)
+{
+ int length;
+
+ g_memmove(&length, user_data, sizeof(int));
+ gtk_selection_data_set(selection_data,
+ gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
+}
+
+void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
+{
+ g_free(user_data);
+}
+
+void selection_to_clip(void)
+{
+ int bufsz, nitems, val;
+ char *buf, *p;
+ GList *list;
+ struct Item *item;
+ GtkTargetEntry target;
+
+ if (ui.selection == NULL) return;
+ bufsz = 2*sizeof(int) // bufsz, nitems
+ + sizeof(struct BBox); // bbox
+ nitems = 0;
+ for (list = ui.selection->items; list != NULL; list = list->next) {
+ item = (struct Item *)list->data;
+ nitems++;
+ if (item->type == ITEM_STROKE) {
+ bufsz+= sizeof(int) // type
+ + sizeof(struct Brush) // brush
+ + sizeof(int) // num_points
+ + 2*item->path->num_points*sizeof(double); // the points
+ if (item->brush.variable_width)
+ bufsz += (item->path->num_points-1)*sizeof(double); // the widths
+ }
+ else if (item->type == ITEM_TEXT) {
+ bufsz+= sizeof(int) // type
+ + sizeof(struct Brush) // brush
+ + 2*sizeof(double) // bbox upper-left
+ + sizeof(int) // text len
+ + strlen(item->text)+1 // text
+ + sizeof(int) // font_name len
+ + strlen(item->font_name)+1 // font_name
+ + sizeof(double); // font_size
+ }
+ else if (item->type == ITEM_IMAGE) {
+ if (item->image_png == NULL) {
+ set_cursor_busy(TRUE);
+ if (!gdk_pixbuf_save_to_buffer(item->image, &item->image_png, &item->image_png_len, "png", NULL, NULL))
+ item->image_png_len = 0; // failed for some reason, so forget it
+ set_cursor_busy(FALSE);
+ }
+ bufsz+= sizeof(int) // type
+ + sizeof(struct BBox)
+ + sizeof(gsize) // png_buflen
+ + item->image_png_len;
+ }
+ else bufsz+= sizeof(int); // type
+ }
+ p = buf = g_malloc(bufsz);
+ g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
+ g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
+ g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
+ for (list = ui.selection->items; list != NULL; list = list->next) {
+ item = (struct Item *)list->data;
+ g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
+ if (item->type == ITEM_STROKE) {
+ g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
+ g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
+ g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
+ p+= 2*item->path->num_points*sizeof(double);
+ if (item->brush.variable_width) {
+ g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
+ p+= (item->path->num_points-1)*sizeof(double);
+ }
+ }
+ if (item->type == ITEM_TEXT) {
+ g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
+ g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
+ g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
+ val = strlen(item->text);
+ g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
+ g_memmove(p, item->text, val+1); p+= val+1;
+ val = strlen(item->font_name);
+ g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
+ g_memmove(p, item->font_name, val+1); p+= val+1;
+ g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
+ }
+ if (item->type == ITEM_IMAGE) {
+ g_memmove(p, &item->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
+ g_memmove(p, &item->image_png_len, sizeof(gsize)); p+= sizeof(gsize);
+ if (item->image_png_len > 0) {
+ g_memmove(p, item->image_png, item->image_png_len); p+= item->image_png_len;
+ }
+ }
+ }
+
+ target.target = "_XOURNAL";
+ target.flags = 0;
+ target.info = 0;
+
+ gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
+ &target, 1,
+ callback_clipboard_get, callback_clipboard_clear, buf);
+}
+
+// local paste within xournal
+void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
+{
+ unsigned char *p;
+ int nitems, npts, i, len;
+ struct Item *item;
+ double hoffset, voffset, cx, cy;
+ double *pf;
+ int sx, sy, wx, wy;
+
+ reset_selection();
+
+ ui.selection = g_new(struct Selection, 1);
+ p = sel_data->data + sizeof(int);
+ g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
+ ui.selection->type = ITEM_SELECTRECT;
+ ui.selection->layer = ui.cur_layer;
+ g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
+ ui.selection->items = NULL;
+
+ // find by how much we translate the pasted selection
+ gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
+ gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
+ gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
+ cx -= ui.cur_page->hoffset;
+ cy -= ui.cur_page->voffset;
+ if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
+ cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
+ if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
+ cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
+ if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
+ cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
+ if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
+ cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
+ hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
+ voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
+ ui.selection->bbox.left += hoffset;
+ ui.selection->bbox.right += hoffset;
+ ui.selection->bbox.top += voffset;
+ ui.selection->bbox.bottom += voffset;
+
+ ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
+ gnome_canvas_rect_get_type(), "width-pixels", 1,
+ "outline-color-rgba", 0x000000ff,
+ "fill-color-rgba", 0x80808040,
+ "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
+ "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
+ make_dashed(ui.selection->canvas_item);
+
+ while (nitems-- > 0) {
+ item = g_new(struct Item, 1);
+ ui.selection->items = g_list_append(ui.selection->items, item);
+ ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
+ ui.cur_layer->nitems++;
+ g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
+ if (item->type == ITEM_STROKE) {
+ g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
+ g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
+ item->path = gnome_canvas_points_new(npts);
+ pf = (double *)p;
+ for (i=0; i<npts; i++) {
+ item->path->coords[2*i] = pf[2*i] + hoffset;
+ item->path->coords[2*i+1] = pf[2*i+1] + voffset;
+ }
+ p+= 2*item->path->num_points*sizeof(double);
+ if (item->brush.variable_width) {
+ item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
+ p+= (item->path->num_points-1)*sizeof(double);
+ }
+ else item->widths = NULL;
+ update_item_bbox(item);
+ make_canvas_item_one(ui.cur_layer->group, item);
+ }
+ if (item->type == ITEM_TEXT) {
+ g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
+ g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
+ g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
+ item->bbox.left += hoffset;
+ item->bbox.top += voffset;
+ g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
+ item->text = g_malloc(len+1);
+ g_memmove(item->text, p, len+1); p+= len+1;
+ g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
+ item->font_name = g_malloc(len+1);
+ g_memmove(item->font_name, p, len+1); p+= len+1;
+ g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
+ make_canvas_item_one(ui.cur_layer->group, item);
+ }
+ if (item->type == ITEM_IMAGE) {
+ item->canvas_item = NULL;
+ item->image_png = NULL;
+ item->image_png_len = 0;
+ g_memmove(&item->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
+ item->bbox.left += hoffset;
+ item->bbox.right += hoffset;
+ item->bbox.top += voffset;
+ item->bbox.bottom += voffset;
+ g_memmove(&item->image_png_len, p, sizeof(gsize)); p+= sizeof(gsize);
+ if (item->image_png_len > 0) {
+ item->image_png = g_memdup(p, item->image_png_len);
+ item->image = pixbuf_from_buffer(item->image_png, item->image_png_len);
+ p+= item->image_png_len;
+ } else {
+ item->image = NULL;
+ }
+ make_canvas_item_one(ui.cur_layer->group, item);
+ }
+ }
+
+ prepare_new_undo();
+ undo->type = ITEM_PASTE;
+ undo->layer = ui.cur_layer;
+ undo->itemlist = g_list_copy(ui.selection->items);
+
+ 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!
+}
+
+// paste an external image
+void clipboard_paste_image(GdkPixbuf *pixbuf)
+{
+ double pt[2];
+
+ reset_selection();
+
+ get_current_pointer_coords(pt);
+ set_current_page(pt);
+
+ create_image_from_pixbuf(pixbuf, pt);
+}
+
+// work out what format the clipboard data is in, and paste accordingly
+void clipboard_paste(void)
+{
+ GtkSelectionData *sel_data;
+ GtkClipboard *clipboard;
+ GdkPixbuf *pixbuf;
+
+ if (ui.cur_layer == NULL) return;
+
+ ui.cur_item_type = ITEM_PASTE;
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ // try xournal data
+ sel_data = gtk_clipboard_wait_for_contents(
+ clipboard,
+ gdk_atom_intern("_XOURNAL", FALSE));
+ ui.cur_item_type = ITEM_NONE;
+ if (sel_data != NULL) {
+ clipboard_paste_from_xournal(sel_data);
+ return;
+ }
+ // try image data
+ pixbuf = gtk_clipboard_wait_for_image(clipboard);
+ if (pixbuf != NULL) {
+ clipboard_paste_image(pixbuf);
+ return;
+ }
+}
--- /dev/null
+/*
+ * This program 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+void selection_to_clip(void);
+void clipboard_paste(void);
#include "xo-misc.h"
#include "xo-file.h"
#include "xo-paint.h"
+#include "xo-image.h"
-const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "text", "", "selectrect", "vertspace", "hand"};
+const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "text", "", "selectrect", "vertspace", "hand", "image"};
const char *color_names[COLOR_MAX] = {"black", "blue", "red", "green",
"gray", "lightblue", "lightgreen", "magenta", "orange", "yellow", "white"};
const char *bgtype_names[3] = {"solid", "pixmap", "pdf"};
}
}
+/* Write image to file: returns true on success, false on error.
+ The image is written as a base64 encoded PNG. */
+
+gboolean write_image(gzFile f, Item *item)
+{
+ gchar *base64_str;
+
+ if (item->image_png == NULL) {
+ if (!gdk_pixbuf_save_to_buffer(item->image, &item->image_png, &item->image_png_len, "png", NULL, NULL)) {
+ item->image_png_len = 0; // failed for some reason, so forget it
+ return FALSE;
+ }
+ }
+
+ base64_str = g_base64_encode(item->image_png, item->image_png_len);
+ gzputs(f, base64_str);
+ g_free(base64_str);
+ return TRUE;
+}
+
+// create pixbuf from base64 encoded PNG, or return NULL on failure
+
+GdkPixbuf *read_pixbuf(const gchar *base64_str, gsize base64_strlen)
+{
+ gchar *base64_str2;
+ gchar *png_buf;
+ gsize png_buflen;
+ GdkPixbuf *pixbuf;
+
+ // We have to copy the string in order to null terminate it, sigh.
+ base64_str2 = g_memdup(base64_str, base64_strlen+1);
+ base64_str2[base64_strlen] = 0;
+ png_buf = g_base64_decode(base64_str2, &png_buflen);
+
+ pixbuf = pixbuf_from_buffer(png_buf, png_buflen);
+
+ g_free(png_buf);
+ g_free(base64_str2);
+ return pixbuf;
+}
+
// saves the journal to a file: returns true on success, false on error
gboolean save_journal(const char *filename)
gzputs(f, "</text>\n");
g_free(tmpstr);
}
+ if (item->type == ITEM_IMAGE) {
+ gzprintf(f, "<image left=\"%.2f\" top=\"%.2f\" right=\"%.2f\" bottom=\"%.2f\">",
+ item->bbox.left, item->bbox.top, item->bbox.right, item->bbox.bottom);
+ if (!write_image(f, item)) success = FALSE;
+ gzprintf(f, "</image>\n");
+ }
}
gzprintf(f, "</layer>\n");
}
}
if (has_attr!=31) *error = xoj_invalid();
}
+ else if (!strcmp(element_name, "image")) { // start of a image item
+ if (tmpLayer == NULL || tmpItem != NULL) {
+ *error = xoj_invalid();
+ return;
+ }
+ tmpItem = (struct Item *)g_malloc0(sizeof(struct Item));
+ tmpItem->type = ITEM_IMAGE;
+ tmpItem->canvas_item = NULL;
+ tmpItem->image=NULL;
+ tmpItem->image_png = NULL;
+ tmpItem->image_png_len = 0;
+ tmpLayer->items = g_list_append(tmpLayer->items, tmpItem);
+ tmpLayer->nitems++;
+ // scan for x, y
+ has_attr = 0;
+ while (*attribute_names!=NULL) {
+ if (!strcmp(*attribute_names, "left")) {
+ if (has_attr & 1) *error = xoj_invalid();
+ cleanup_numeric((gchar *)*attribute_values);
+ tmpItem->bbox.left = g_ascii_strtod(*attribute_values, &ptr);
+ if (ptr == *attribute_values) *error = xoj_invalid();
+ has_attr |= 1;
+ }
+ else if (!strcmp(*attribute_names, "top")) {
+ if (has_attr & 2) *error = xoj_invalid();
+ cleanup_numeric((gchar *)*attribute_values);
+ tmpItem->bbox.top = g_ascii_strtod(*attribute_values, &ptr);
+ if (ptr == *attribute_values) *error = xoj_invalid();
+ has_attr |= 2;
+ }
+ else if (!strcmp(*attribute_names, "right")) {
+ if (has_attr & 4) *error = xoj_invalid();
+ cleanup_numeric((gchar *)*attribute_values);
+ tmpItem->bbox.right = g_ascii_strtod(*attribute_values, &ptr);
+ if (ptr == *attribute_values) *error = xoj_invalid();
+ has_attr |= 4;
+ }
+ else if (!strcmp(*attribute_names, "bottom")) {
+ if (has_attr & 8) *error = xoj_invalid();
+ cleanup_numeric((gchar *)*attribute_values);
+ tmpItem->bbox.bottom = g_ascii_strtod(*attribute_values, &ptr);
+ if (ptr == *attribute_values) *error = xoj_invalid();
+ has_attr |= 8;
+ }
+ else *error = xoj_invalid();
+ attribute_names++;
+ attribute_values++;
+ }
+ if (has_attr!=15) *error = xoj_invalid();
+ }
}
void xoj_parser_end_element(GMarkupParseContext *context,
}
tmpItem = NULL;
}
+ if (!strcmp(element_name, "image")) {
+ if (tmpItem == NULL) {
+ *error = xoj_invalid();
+ return;
+ }
+ tmpItem = NULL;
+ }
}
void xoj_parser_text(GMarkupParseContext *context,
g_memmove(tmpItem->text, text, text_len);
tmpItem->text[text_len]=0;
}
+ if (!strcmp(element_name, "image")) {
+ tmpItem->image = read_pixbuf(text, text_len);
+ }
}
gboolean user_wants_second_chance(char **filename)
ui.print_ruling = TRUE;
ui.default_unit = UNIT_CM;
ui.default_path = NULL;
+ ui.default_image = NULL;
ui.default_font_name = g_strdup(DEFAULT_FONT);
ui.default_font_size = DEFAULT_FONT_SIZE;
ui.pressure_sensitivity = FALSE;
--- /dev/null
+/*
+ * This program 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "xournal.h"
+#include "xo-support.h"
+#include "xo-image.h"
+
+// create pixbuf from buffer, or return NULL on failure
+GdkPixbuf *pixbuf_from_buffer(const gchar *buf, gsize buflen)
+{
+ GInputStream *istream;
+ GdkPixbuf *pixbuf;
+ GError *error;
+
+ error = NULL;
+
+ istream = g_memory_input_stream_new_from_data (buf, buflen, NULL);
+ pixbuf = gdk_pixbuf_new_from_stream(istream, NULL, &error);
+ g_input_stream_close(istream, NULL, &error);
+ return pixbuf;
+}
+
+void create_image_from_pixbuf(GdkPixbuf *pixbuf, double *pt)
+{
+ double scale;
+ struct Item *item;
+
+ item = g_new(struct Item, 1);
+ item->type = ITEM_IMAGE;
+ item->canvas_item = NULL;
+ item->bbox.left = pt[0];
+ item->bbox.top = pt[1];
+ item->image = pixbuf;
+ item->image_png = NULL;
+ item->image_png_len = 0;
+
+ // Scale at native size, unless that won't fit, in which case we shrink it down.
+ scale = 1 / ui.zoom;
+ if ((scale * gdk_pixbuf_get_width(item->image)) > ui.cur_page->width - item->bbox.left)
+ scale = (ui.cur_page->width - item->bbox.left) / gdk_pixbuf_get_width(item->image);
+ if ((scale * gdk_pixbuf_get_height(item->image)) > ui.cur_page->height - item->bbox.top)
+ scale = (ui.cur_page->height - item->bbox.top) / gdk_pixbuf_get_height(item->image);
+
+ item->bbox.right = item->bbox.left + scale * gdk_pixbuf_get_width(item->image);
+ item->bbox.bottom = item->bbox.top + scale * gdk_pixbuf_get_height(item->image);
+ ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
+ ui.cur_layer->nitems++;
+
+ make_canvas_item_one(ui.cur_layer->group, item);
+
+ // add undo information
+ prepare_new_undo();
+ undo->type = ITEM_IMAGE;
+ undo->item = item;
+ undo->layer = ui.cur_layer;
+ ui.cur_item = NULL;
+ ui.cur_item_type = ITEM_NONE;
+
+ // select image
+ reset_selection();
+ ui.selection = g_new0(struct Selection, 1);
+ ui.selection->type = ITEM_SELECTRECT;
+ ui.selection->layer = ui.cur_layer;
+ ui.selection->bbox = item->bbox;
+ ui.selection->items = g_list_append(ui.selection->items, item);
+ ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
+ gnome_canvas_rect_get_type(), "width-pixels", 1,
+ "outline-color-rgba", 0x000000ff,
+ "fill-color-rgba", 0x80808040,
+ "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
+ "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
+ make_dashed(ui.selection->canvas_item);
+ update_copy_paste_enabled();
+}
+
+void insert_image(GdkEvent *event)
+{
+ GtkTextBuffer *buffer;
+ GnomeCanvasItem *canvas_item;
+ GdkColor color;
+ GtkWidget *dialog;
+ GtkFileFilter *filt_all;
+ GtkFileFilter *filt_gdkimage;
+ char *filename;
+ GdkPixbuf *pixbuf;
+ double scale=1;
+ double pt[2];
+
+ dialog = gtk_file_chooser_dialog_new(_("Insert Image"), GTK_WINDOW (winMain),
+ GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
+#ifdef FILE_DIALOG_SIZE_BUGFIX
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 400);
+#endif
+
+ filt_all = gtk_file_filter_new();
+ gtk_file_filter_set_name(filt_all, _("All files"));
+ gtk_file_filter_add_pattern(filt_all, "*");
+ filt_gdkimage = gtk_file_filter_new();
+ gtk_file_filter_set_name(filt_gdkimage, _("Image files"));
+ gtk_file_filter_add_pixbuf_formats(filt_gdkimage);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_gdkimage);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_all);
+
+ if (ui.default_image != NULL) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER (dialog), ui.default_image);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) {
+ gtk_widget_destroy(dialog);
+ return;
+ }
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
+ gtk_widget_destroy(dialog);
+
+ if (filename == NULL) return; /* nothing selected */
+
+ if (ui.default_image != NULL) g_free(ui.default_image);
+ ui.default_image = g_strdup(filename);
+
+ set_cursor_busy(TRUE);
+ pixbuf=gdk_pixbuf_new_from_file(filename, NULL);
+ set_cursor_busy(FALSE);
+
+ if(pixbuf==NULL) { /* open failed */
+ dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Error opening image '%s'"), filename);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_free(filename);
+ ui.cur_item = NULL;
+ ui.cur_item_type = ITEM_NONE;
+ return;
+ }
+
+ ui.cur_item_type = ITEM_IMAGE;
+
+ get_pointer_coords(event, pt);
+ set_current_page(pt);
+
+ create_image_from_pixbuf(pixbuf, pt);
+}
+
+void rescale_images(void)
+{
+ // nothing needed in this implementation
+}
+
--- /dev/null
+/*
+ * This program 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+GdkPixbuf *pixbuf_from_buffer(const gchar *buf, gsize buflen);
+void create_image_from_pixbuf(GdkPixbuf *pixbuf, double *pt);
+void insert_image(GdkEvent *event);
+void rescale_images(void);
GtkWidget *toolsEraser;
GtkWidget *toolsHighlighter;
GtkWidget *toolsText;
+ GtkWidget *toolsImage;
GtkWidget *separator15;
GtkWidget *toolsReco;
GtkWidget *toolsRuler;
GtkWidget *button2Eraser;
GtkWidget *button2Highlighter;
GtkWidget *button2Text;
+ GtkWidget *button2Image;
GtkWidget *button2SelectRegion;
GtkWidget *button2SelectRectangle;
GtkWidget *button2VerticalSpace;
GtkWidget *button3Eraser;
GtkWidget *button3Highlighter;
GtkWidget *button3Text;
+ GtkWidget *button3Image;
GtkWidget *button3SelectRegion;
GtkWidget *button3SelectRectangle;
GtkWidget *button3VerticalSpace;
GtkWidget *buttonEraser;
GtkWidget *buttonHighlighter;
GtkWidget *buttonText;
+ GtkWidget *buttonImage;
GtkWidget *buttonReco;
GtkWidget *buttonRuler;
GtkWidget *toolitem15;
gtk_widget_show (image631);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (viewHideLayer), image631);
- menuJournal = gtk_menu_item_new_with_mnemonic (_("_Journal"));
+ menuJournal = gtk_menu_item_new_with_mnemonic (_("_Page"));
gtk_widget_show (menuJournal);
gtk_container_add (GTK_CONTAINER (menubar), menuJournal);
GTK_ACCEL_VISIBLE);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (toolsText), TRUE);
+ toolsImage = gtk_radio_menu_item_new_with_mnemonic (toolsPen_group, _("_Image"));
+ toolsPen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (toolsImage));
+ gtk_widget_show (toolsImage);
+ gtk_container_add (GTK_CONTAINER (menuTools_menu), toolsImage);
+ gtk_widget_add_accelerator (toolsImage, "activate", accel_group,
+ GDK_I, (GdkModifierType) GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+ GTK_ACCEL_VISIBLE);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (toolsImage), TRUE);
+
separator15 = gtk_separator_menu_item_new ();
gtk_widget_show (separator15);
gtk_container_add (GTK_CONTAINER (menuTools_menu), separator15);
gtk_container_add (GTK_CONTAINER (button2_mapping_menu), button2Text);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button2Text), TRUE);
+ button2Image = gtk_radio_menu_item_new_with_mnemonic (button2Pen_group, _("_Image"));
+ button2Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button2Image));
+ gtk_widget_show (button2Image);
+ gtk_container_add (GTK_CONTAINER (button2_mapping_menu), button2Image);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button2Image), TRUE);
+
button2SelectRegion = gtk_radio_menu_item_new_with_mnemonic (button2Pen_group, _("Select Re_gion"));
button2Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button2SelectRegion));
gtk_widget_show (button2SelectRegion);
gtk_container_add (GTK_CONTAINER (button3_mapping_menu), button3Text);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button3Text), TRUE);
+ button3Image = gtk_radio_menu_item_new_with_mnemonic (button3Pen_group, _("_Image"));
+ button3Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button3Image));
+ gtk_widget_show (button3Image);
+ gtk_container_add (GTK_CONTAINER (button3_mapping_menu), button3Image);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button3Image), TRUE);
+
button3SelectRegion = gtk_radio_menu_item_new_with_mnemonic (button3Pen_group, _("Select Re_gion"));
button3Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button3SelectRegion));
gtk_widget_show (button3SelectRegion);
gtk_radio_tool_button_set_group (GTK_RADIO_TOOL_BUTTON (buttonText), buttonPen_group);
buttonPen_group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (buttonText));
+ buttonImage = (GtkWidget*) gtk_radio_tool_button_new (NULL);
+ gtk_tool_button_set_label (GTK_TOOL_BUTTON (buttonImage), _("Text"));
+ tmp_image = gtk_image_new_from_stock ("gtk-orientation-portrait", tmp_toolbar_icon_size);
+ gtk_widget_show (tmp_image);
+ gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (buttonImage), tmp_image);
+ gtk_widget_show (buttonImage);
+ gtk_container_add (GTK_CONTAINER (toolbarPen), buttonImage);
+ gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (buttonImage), tooltips, _("Text"), NULL);
+ gtk_radio_tool_button_set_group (GTK_RADIO_TOOL_BUTTON (buttonImage), buttonPen_group);
+ buttonPen_group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (buttonImage));
+
buttonReco = (GtkWidget*) gtk_toggle_tool_button_new ();
gtk_tool_button_set_label (GTK_TOOL_BUTTON (buttonReco), _("Shape Recognizer"));
tmp_image = create_pixmap (winMain, "shapes.png");
g_signal_connect ((gpointer) toolsText, "toggled",
G_CALLBACK (on_toolsText_activate),
NULL);
+ g_signal_connect ((gpointer) toolsImage, "toggled",
+ G_CALLBACK (on_toolsImage_activate),
+ NULL);
g_signal_connect ((gpointer) toolsReco, "toggled",
G_CALLBACK (on_toolsReco_activate),
NULL);
g_signal_connect ((gpointer) button2Text, "activate",
G_CALLBACK (on_button2Text_activate),
NULL);
+ g_signal_connect ((gpointer) button2Image, "activate",
+ G_CALLBACK (on_button2Image_activate),
+ NULL);
g_signal_connect ((gpointer) button2SelectRegion, "activate",
G_CALLBACK (on_button2SelectRegion_activate),
NULL);
g_signal_connect ((gpointer) button3Text, "activate",
G_CALLBACK (on_button3Text_activate),
NULL);
+ g_signal_connect ((gpointer) button3Image, "activate",
+ G_CALLBACK (on_button3Image_activate),
+ NULL);
g_signal_connect ((gpointer) button3SelectRegion, "activate",
G_CALLBACK (on_button3SelectRegion_activate),
NULL);
g_signal_connect ((gpointer) buttonText, "toggled",
G_CALLBACK (on_toolsText_activate),
NULL);
+ g_signal_connect ((gpointer) buttonImage, "toggled",
+ G_CALLBACK (on_toolsImage_activate),
+ NULL);
g_signal_connect ((gpointer) buttonReco, "toggled",
G_CALLBACK (on_toolsReco_activate),
NULL);
GLADE_HOOKUP_OBJECT (winMain, toolsEraser, "toolsEraser");
GLADE_HOOKUP_OBJECT (winMain, toolsHighlighter, "toolsHighlighter");
GLADE_HOOKUP_OBJECT (winMain, toolsText, "toolsText");
+ GLADE_HOOKUP_OBJECT (winMain, toolsImage, "toolsImage");
GLADE_HOOKUP_OBJECT (winMain, separator15, "separator15");
GLADE_HOOKUP_OBJECT (winMain, toolsReco, "toolsReco");
GLADE_HOOKUP_OBJECT (winMain, toolsRuler, "toolsRuler");
GLADE_HOOKUP_OBJECT (winMain, button2Eraser, "button2Eraser");
GLADE_HOOKUP_OBJECT (winMain, button2Highlighter, "button2Highlighter");
GLADE_HOOKUP_OBJECT (winMain, button2Text, "button2Text");
+ GLADE_HOOKUP_OBJECT (winMain, button2Image, "button2Image");
GLADE_HOOKUP_OBJECT (winMain, button2SelectRegion, "button2SelectRegion");
GLADE_HOOKUP_OBJECT (winMain, button2SelectRectangle, "button2SelectRectangle");
GLADE_HOOKUP_OBJECT (winMain, button2VerticalSpace, "button2VerticalSpace");
GLADE_HOOKUP_OBJECT (winMain, button3Eraser, "button3Eraser");
GLADE_HOOKUP_OBJECT (winMain, button3Highlighter, "button3Highlighter");
GLADE_HOOKUP_OBJECT (winMain, button3Text, "button3Text");
+ GLADE_HOOKUP_OBJECT (winMain, button3Image, "button3Image");
GLADE_HOOKUP_OBJECT (winMain, button3SelectRegion, "button3SelectRegion");
GLADE_HOOKUP_OBJECT (winMain, button3SelectRectangle, "button3SelectRectangle");
GLADE_HOOKUP_OBJECT (winMain, button3VerticalSpace, "button3VerticalSpace");
GLADE_HOOKUP_OBJECT (winMain, buttonEraser, "buttonEraser");
GLADE_HOOKUP_OBJECT (winMain, buttonHighlighter, "buttonHighlighter");
GLADE_HOOKUP_OBJECT (winMain, buttonText, "buttonText");
+ GLADE_HOOKUP_OBJECT (winMain, buttonImage, "buttonImage");
GLADE_HOOKUP_OBJECT (winMain, buttonReco, "buttonReco");
GLADE_HOOKUP_OBJECT (winMain, buttonRuler, "buttonRuler");
GLADE_HOOKUP_OBJECT (winMain, toolitem15, "toolitem15");
#include "xo-file.h"
#include "xo-paint.h"
#include "xo-shapes.h"
+#include "xo-image.h"
// some global constants
return pg;
}
+// change the current page if necessary for pointer at pt
+void set_current_page(gdouble *pt)
+{
+ gboolean page_change;
+ struct Page *tmppage;
+
+ page_change = FALSE;
+ tmppage = ui.cur_page;
+ while (ui.view_continuous && (pt[1] < - VIEW_CONTINUOUS_SKIP)) {
+ if (ui.pageno == 0) break;
+ page_change = TRUE;
+ ui.pageno--;
+ tmppage = g_list_nth_data(journal.pages, ui.pageno);
+ pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
+ }
+ while (ui.view_continuous && (pt[1] > tmppage->height + VIEW_CONTINUOUS_SKIP)) {
+ if (ui.pageno == journal.npages-1) break;
+ pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
+ page_change = TRUE;
+ ui.pageno++;
+ tmppage = g_list_nth_data(journal.pages, ui.pageno);
+ }
+ if (page_change) do_switch_page(ui.pageno, FALSE, FALSE);
+}
+
void realloc_cur_path(int n)
{
if (n <= ui.cur_path_storage_alloc) return;
g_free(redo->item->font_name);
g_free(redo->item);
}
+ else if (redo->type == ITEM_IMAGE) {
+ g_object_unref(redo->item->image);
+ g_free(redo->item->image_png);
+ g_free(redo->item);
+ }
else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
for (list = redo->erasurelist; list!=NULL; list=list->next) {
erasure = (struct UndoErasureData *)list->data;
}
if (erasure->item->type == ITEM_TEXT)
{ g_free(erasure->item->text); g_free(erasure->item->font_name); }
+ if (erasure->item->type == ITEM_IMAGE) {
+ g_object_unref(erasure->item->image);
+ g_free(erasure->item->image_png);
+ }
g_free(erasure->item);
g_list_free(erasure->replacement_items);
g_free(erasure);
if (item->type == ITEM_TEXT) {
g_free(item->font_name); g_free(item->text);
}
+ if (item->type == ITEM_IMAGE) {
+ g_object_unref(item->image);
+ g_free(item->image_png);
+ }
// don't need to delete the canvas_item, as it's part of the group destroyed below
g_free(item);
l->items = g_list_delete_link(l->items, l->items);
ret[1] -= ui.cur_page->voffset;
}
+void get_current_pointer_coords(gdouble *ret)
+{
+ gint wx, wy, sx, sy;
+
+ gtk_widget_get_pointer((GtkWidget *)canvas, &wx, &wy);
+ gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
+ gnome_canvas_window_to_world(canvas, (double)(wx + sx), (double)(wy + sy), ret, ret+1);
+ ret[0] -= ui.cur_page->hoffset;
+ ret[1] -= ui.cur_page->voffset;
+}
+
void fix_xinput_coords(GdkEvent *event)
{
double *axes, *px, *py, axis_width;
"text", item->text, NULL);
update_item_bbox(item);
}
+ if (item->type == ITEM_IMAGE) {
+ item->canvas_item = gnome_canvas_item_new(group,
+ gnome_canvas_pixbuf_get_type(),
+ "pixbuf", item->image,
+ "x", item->bbox.left, "y", item->bbox.top,
+ "width", item->bbox.right - item->bbox.left,
+ "height", item->bbox.bottom - item->bbox.top,
+ "width-set", TRUE, "height-set", TRUE,
+ NULL);
+ }
}
void make_canvas_items(void)
gtk_toggle_tool_button_set_active(
GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
break;
+ case TOOL_IMAGE:
+ gtk_toggle_tool_button_set_active(
+ GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonImage")), TRUE);
+ break;
case TOOL_SELECTREGION:
gtk_toggle_tool_button_set_active(
GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
break;
+ case TOOL_IMAGE:
+ gtk_check_menu_item_set_active(
+ GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsImage")), TRUE);
+ break;
case TOOL_SELECTREGION:
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
break;
+ case TOOL_IMAGE:
+ gtk_check_menu_item_set_active(
+ GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Image")), TRUE);
+ break;
case TOOL_SELECTREGION:
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
break;
+ case TOOL_IMAGE:
+ gtk_check_menu_item_set_active(
+ GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Image")), TRUE);
+ break;
case TOOL_SELECTREGION:
gtk_check_menu_item_set_active(
GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
if (item->type == ITEM_STROKE)
for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
{ pt[0] += dx; pt[1] += dy; }
- if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
+ if (item->type == ITEM_STROKE || item->type == ITEM_TEXT ||
+ item->type == ITEM_TEMP_TEXT || item->type == ITEM_IMAGE) {
item->bbox.left += dx;
item->bbox.right += dx;
item->bbox.top += dy;
item->bbox.left = item->bbox.left*scaling_x + offset_x;
item->bbox.top = item->bbox.top*scaling_y + offset_y;
}
+ if (item->type == ITEM_IMAGE) {
+ item->bbox.left = item->bbox.left*scaling_x + offset_x;
+ item->bbox.right = item->bbox.right*scaling_x + offset_x;
+ item->bbox.top = item->bbox.top*scaling_y + offset_y;
+ item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
+ if (item->bbox.left > item->bbox.right) {
+ temp = item->bbox.left;
+ item->bbox.left = item->bbox.right;
+ item->bbox.right = temp;
+ }
+ if (item->bbox.top > item->bbox.bottom) {
+ temp = item->bbox.top;
+ item->bbox.top = item->bbox.bottom;
+ item->bbox.bottom = temp;
+ }
+ }
// redraw the item
if (item->canvas_item!=NULL) {
group = (GnomeCanvasGroup *) item->canvas_item->parent;
struct Page *new_page(struct Page *template);
struct Page *new_page_with_bg(struct Background *bg, double width, double height);
+void set_current_page(gdouble *pt);
void realloc_cur_path(int n);
void realloc_cur_widths(int n);
void clear_redo_stack(void);
int finite_sized(double x);
void get_pointer_coords(GdkEvent *event, double *ret);
+void get_current_pointer_coords(double *ret);
double get_pressure_multiplier(GdkEvent *event);
void fix_xinput_coords(GdkEvent *event);
void update_item_bbox(struct Item *item);
the forward direction */
}
-void callback_clipboard_get(GtkClipboard *clipboard,
- GtkSelectionData *selection_data,
- guint info, gpointer user_data)
-{
- int length;
-
- g_memmove(&length, user_data, sizeof(int));
- gtk_selection_data_set(selection_data,
- gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
-}
-
-void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
-{
- g_free(user_data);
-}
-
-void selection_to_clip(void)
-{
- int bufsz, nitems, val;
- char *buf, *p;
- GList *list;
- struct Item *item;
- GtkTargetEntry target;
-
- if (ui.selection == NULL) return;
- bufsz = 2*sizeof(int) // bufsz, nitems
- + sizeof(struct BBox); // bbox
- nitems = 0;
- for (list = ui.selection->items; list != NULL; list = list->next) {
- item = (struct Item *)list->data;
- nitems++;
- if (item->type == ITEM_STROKE) {
- bufsz+= sizeof(int) // type
- + sizeof(struct Brush) // brush
- + sizeof(int) // num_points
- + 2*item->path->num_points*sizeof(double); // the points
- if (item->brush.variable_width)
- bufsz += (item->path->num_points-1)*sizeof(double); // the widths
- }
- else if (item->type == ITEM_TEXT) {
- bufsz+= sizeof(int) // type
- + sizeof(struct Brush) // brush
- + 2*sizeof(double) // bbox upper-left
- + sizeof(int) // text len
- + strlen(item->text)+1 // text
- + sizeof(int) // font_name len
- + strlen(item->font_name)+1 // font_name
- + sizeof(double); // font_size
- }
- else bufsz+= sizeof(int); // type
- }
- p = buf = g_malloc(bufsz);
- g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
- g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
- g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
- for (list = ui.selection->items; list != NULL; list = list->next) {
- item = (struct Item *)list->data;
- g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
- if (item->type == ITEM_STROKE) {
- g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
- g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
- g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
- p+= 2*item->path->num_points*sizeof(double);
- if (item->brush.variable_width) {
- g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
- p+= (item->path->num_points-1)*sizeof(double);
- }
- }
- if (item->type == ITEM_TEXT) {
- g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
- g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
- g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
- val = strlen(item->text);
- g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
- g_memmove(p, item->text, val+1); p+= val+1;
- val = strlen(item->font_name);
- g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
- g_memmove(p, item->font_name, val+1); p+= val+1;
- g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
- }
- }
-
- target.target = "_XOURNAL";
- target.flags = 0;
- target.info = 0;
-
- gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
- &target, 1,
- callback_clipboard_get, callback_clipboard_clear, buf);
-}
-
-
-void clipboard_paste(void)
-{
- GtkSelectionData *sel_data;
- unsigned char *p;
- int nitems, npts, i, len;
- struct Item *item;
- double hoffset, voffset, cx, cy;
- double *pf;
- int sx, sy, wx, wy;
-
- if (ui.cur_layer == NULL) return;
-
- ui.cur_item_type = ITEM_PASTE;
- sel_data = gtk_clipboard_wait_for_contents(
- gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
- gdk_atom_intern("_XOURNAL", FALSE));
- ui.cur_item_type = ITEM_NONE;
- if (sel_data == NULL) return; // paste failed
-
- reset_selection();
-
- ui.selection = g_new(struct Selection, 1);
- p = sel_data->data + sizeof(int);
- g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
- ui.selection->type = ITEM_SELECTRECT;
- ui.selection->layer = ui.cur_layer;
- g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
- ui.selection->items = NULL;
-
- // find by how much we translate the pasted selection
- gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
- gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
- gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
- cx -= ui.cur_page->hoffset;
- cy -= ui.cur_page->voffset;
- if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
- cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
- if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
- cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
- if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
- cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
- if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
- cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
- hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
- voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
- ui.selection->bbox.left += hoffset;
- ui.selection->bbox.right += hoffset;
- ui.selection->bbox.top += voffset;
- ui.selection->bbox.bottom += voffset;
-
- ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
- gnome_canvas_rect_get_type(), "width-pixels", 1,
- "outline-color-rgba", 0x000000ff,
- "fill-color-rgba", 0x80808040,
- "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
- "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
- make_dashed(ui.selection->canvas_item);
-
- while (nitems-- > 0) {
- item = g_new(struct Item, 1);
- ui.selection->items = g_list_append(ui.selection->items, item);
- ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
- ui.cur_layer->nitems++;
- g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
- if (item->type == ITEM_STROKE) {
- g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
- g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
- item->path = gnome_canvas_points_new(npts);
- pf = (double *)p;
- for (i=0; i<npts; i++) {
- item->path->coords[2*i] = pf[2*i] + hoffset;
- item->path->coords[2*i+1] = pf[2*i+1] + voffset;
- }
- p+= 2*item->path->num_points*sizeof(double);
- if (item->brush.variable_width) {
- item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
- p+= (item->path->num_points-1)*sizeof(double);
- }
- else item->widths = NULL;
- update_item_bbox(item);
- make_canvas_item_one(ui.cur_layer->group, item);
- }
- if (item->type == ITEM_TEXT) {
- g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
- g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
- g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
- item->bbox.left += hoffset;
- item->bbox.top += voffset;
- g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
- item->text = g_malloc(len+1);
- g_memmove(item->text, p, len+1); p+= len+1;
- g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
- item->font_name = g_malloc(len+1);
- g_memmove(item->font_name, p, len+1); p+= len+1;
- g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
- make_canvas_item_one(ui.cur_layer->group, item);
- }
- }
-
- prepare_new_undo();
- undo->type = ITEM_PASTE;
- undo->layer = ui.cur_layer;
- undo->itemlist = g_list_copy(ui.selection->items);
-
- 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
void recolor_selection(int color_no, guint color_rgba)
void finalize_resizesel(void);
void selection_delete(void);
-void selection_to_clip(void);
-void clipboard_paste(void);
void recolor_selection(int color_no, guint color_rgba);
void rethicken_selection(int val);
return xref->last;
}
+gboolean pdf_draw_image(PdfImage *image, struct XrefTable *xref, GString *pdfbuf)
+{
+ char *buf, *p1, *p2;
+ int height, width, stride, x, y, chan;
+ GString *zpix;
+
+ if (gdk_pixbuf_get_bits_per_sample(image->pixbuf) != 8 ||
+ gdk_pixbuf_get_colorspace(image->pixbuf) != GDK_COLORSPACE_RGB) {
+ return FALSE;
+ }
+
+ width = gdk_pixbuf_get_width(image->pixbuf);
+ height = gdk_pixbuf_get_height(image->pixbuf);
+ stride = gdk_pixbuf_get_rowstride(image->pixbuf);
+ chan = gdk_pixbuf_get_n_channels(image->pixbuf);
+ if (!((chan==3 && !image->has_alpha) || (chan==4 && image->has_alpha))) {
+ return FALSE;
+ }
+
+ p2 = buf = (char *)g_malloc(3*width*height);
+ for (y=0; y<height; y++) {
+ p1 = (char *)gdk_pixbuf_get_pixels(image->pixbuf)+stride*y;
+ for (x=0; x<width; x++) {
+ *(p2++)=*(p1++); *(p2++)=*(p1++); *(p2++)=*(p1++);
+ if (chan==4) p1++;
+ }
+ }
+ zpix = do_deflate(buf, 3*width*height);
+ g_free(buf);
+
+ xref->data[image->n_obj] = pdfbuf->len;
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Length %d /Filter /FlateDecode /Type /Xobject "
+ "/Subtype /Image /Width %d /Height %d /ColorSpace /DeviceRGB "
+ "/BitsPerComponent 8 ",
+ image->n_obj, zpix->len, width, height);
+ if (image->has_alpha) {
+ g_string_append_printf(pdfbuf,
+ "/SMask %d 0 R ",
+ image->n_obj_smask);
+ }
+ g_string_append_printf(pdfbuf, " >> stream\n");
+
+ g_string_append_len(pdfbuf, zpix->str, zpix->len);
+ g_string_free(zpix, TRUE);
+ g_string_append(pdfbuf, "endstream\nendobj\n");
+
+ if (image->has_alpha) {
+ p2 = buf = (char *)g_malloc(width*height);
+ for (y=0; y<height; y++) {
+ p1 = (char *)gdk_pixbuf_get_pixels(image->pixbuf)+stride*y;
+ for (x=0; x<width; x++) {
+ p1+=3; /* skip the RGB */
+ *(p2++)=*(p1++); /* just copy the alpha */
+ }
+ }
+ zpix = do_deflate(buf, width*height);
+ g_free(buf);
+
+ xref->data[image->n_obj_smask] = pdfbuf->len;
+ g_string_append_printf(pdfbuf,
+ "%d 0 obj\n<< /Length %d /Filter /FlateDecode /Type /Xobject "
+ "/Subtype /Image /Width %d /Height %d /ColorSpace /DeviceGray "
+ "/BitsPerComponent 8 >> stream\n",
+ image->n_obj_smask, zpix->len, width, height);
+
+ g_string_append_len(pdfbuf, zpix->str, zpix->len);
+ g_string_free(zpix, TRUE);
+ g_string_append(pdfbuf, "endstream\nendobj\n");
+ }
+
+ return TRUE;
+}
+
+
// manipulate Pdf fonts
struct PdfFont *new_pdffont(struct XrefTable *xref, GList **fonts,
g_string_append(pdfbuf, ">> endobj\n");
}
+// Pdf images
+
+struct PdfImage *new_pdfimage(struct XrefTable *xref, GList **images, GdkPixbuf *pixbuf)
+{
+ GList *list;
+ struct PdfImage *image;
+
+ image = g_malloc(sizeof(struct PdfImage));
+ *images = g_list_append(*images, image);
+ image->n_obj = xref->last+1;
+ make_xref(xref, xref->last+1, 0); // will give it a value later
+ image->has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
+ if (image->has_alpha) {
+ image->n_obj_smask = xref->last+1;
+ make_xref(xref, xref->last+1, 0); // will give it a value later
+ }
+ image->pixbuf = pixbuf;
+
+ return image;
+}
+
// draw a page's graphics
void pdf_draw_page(struct Page *pg, GString *str, gboolean *use_hiliter,
- struct XrefTable *xref, GList **pdffonts)
+ struct XrefTable *xref, GList **pdffonts, GList **pdfimages)
{
GList *layerlist, *itemlist, *tmplist;
struct Layer *l;
int font_id;
FT_Face ftface;
struct PdfFont *cur_font;
+ struct PdfImage *cur_image;
gboolean in_string;
old_rgba = old_text_rgba = 0x12345678; // not any values we use, so we'll reset them
cur_font = (struct PdfFont *)tmplist->data;
cur_font->used_in_this_page = FALSE;
}
+ for (tmplist = *pdfimages; tmplist!=NULL; tmplist = tmplist->next) {
+ cur_image = (struct PdfImage *)tmplist->data;
+ cur_image->used_in_this_page = FALSE;
+ }
for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
l = (struct Layer *)layerlist->data;
pango_layout_iter_free(iter);
g_object_unref(layout);
}
+ else if (item->type == ITEM_IMAGE) {
+ cur_image = new_pdfimage(xref, pdfimages, item->image);
+ cur_image->used_in_this_page = TRUE;
+ g_string_append_printf(str, "\nq 1 0 0 1 %.2f %.2f cm %.2f 0 0 %.2f 0 %.2f cm /Im%d Do Q ",
+ item->bbox.left, item->bbox.top, // translation
+ item->bbox.right-item->bbox.left, item->bbox.top-item->bbox.bottom, item->bbox.bottom-item->bbox.top, // scaling
+ cur_image->n_obj);
+ }
}
}
}
gboolean use_hiliter;
struct PdfInfo pdfinfo;
struct PdfObj *obj;
- GList *pdffonts, *list;
+ GList *pdffonts, *pdfimages, *list;
struct PdfFont *font;
+ struct PdfImage *image;
char *tmpbuf;
f = fopen(filename, "wb");
xref.data = NULL;
uses_pdf = FALSE;
pdffonts = NULL;
+ pdfimages = NULL;
for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
pg = (struct Page *)pglist->data;
if (pg->bg->type == BG_PDF) uses_pdf = TRUE;
n_obj_bgpix = pdf_draw_bitmap_background(pg, pgstrm, &xref, pdfbuf);
// draw the page contents
use_hiliter = FALSE;
- pdf_draw_page(pg, pgstrm, &use_hiliter, &xref, &pdffonts);
+ pdf_draw_page(pg, pgstrm, &use_hiliter, &xref, &pdffonts, &pdfimages);
g_string_append_printf(pgstrm, "Q\n");
// deflate pgstrm and write it
}
add_dict_subentry(pdfbuf, &xref,
obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/PDF"));
- if (n_obj_bgpix>0)
+ if (n_obj_bgpix>0 || pdfimages!=NULL)
add_dict_subentry(pdfbuf, &xref,
obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/ImageC"));
if (use_hiliter)
g_free(tmpbuf);
}
}
+ for (list=pdfimages; list!=NULL; list = list->next) {
+ image = (struct PdfImage *)list->data;
+ if (image->used_in_this_page) {
+ tmpbuf = g_strdup_printf("/Im%d", image->n_obj);
+ add_dict_subentry(pdfbuf, &xref,
+ obj, "/XObject", PDFTYPE_DICT, tmpbuf, mk_pdfref(image->n_obj));
+ g_free(tmpbuf);
+ }
+ }
show_pdfobj(obj, pdfbuf);
free_pdfobj(obj);
g_string_append(pdfbuf, " >> endobj\n");
}
- // after the pages, we insert fonts
+ // after the pages, we insert fonts and images
for (list = pdffonts; list!=NULL; list = list->next) {
font = (struct PdfFont *)list->data;
embed_pdffont(pdfbuf, &xref, font);
g_free(font);
}
g_list_free(pdffonts);
+ for (list = pdfimages; list!=NULL; list = list->next) {
+ image = (struct PdfImage *)list->data;
+ if (!pdf_draw_image(image, &xref, pdfbuf)) {
+ return FALSE;
+ }
+ g_free(image);
+ }
+ g_list_free(pdfimages);
// PDF trailer
startxref = pdfbuf->len;
pango_cairo_show_layout(cr, layout);
g_object_unref(layout);
}
+ if (item->type == ITEM_IMAGE) {
+ double scalex = (item->bbox.right-item->bbox.left)/gdk_pixbuf_get_width(item->image);
+ double scaley = (item->bbox.bottom-item->bbox.top)/gdk_pixbuf_get_height(item->image);
+ cairo_scale(cr, scalex, scaley);
+ gdk_cairo_set_source_pixbuf(cr,item->image, item->bbox.left/scalex, item->bbox.top/scaley);
+ cairo_scale(cr, 1/scalex, 1/scaley);
+ cairo_paint(cr);
+ old_rgba = predef_colors_rgba[COLOR_BLACK];
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ }
}
}
}
int flags;
} PdfFont;
+typedef struct PdfImage {
+ int n_obj;
+ gboolean has_alpha;
+ int n_obj_smask; /* only if has_alpha */
+ GdkPixbuf *pixbuf;
+ gboolean used_in_this_page;
+} PdfImage;
+
+
#define PDFTYPE_CST 0 // intval: true=1, false=0, null=-1
#define PDFTYPE_INT 1 // intval
#define PDFTYPE_REAL 2 // realval
#define TOOL_SELECTRECT 5
#define TOOL_VERTSPACE 6
#define TOOL_HAND 7
+#define TOOL_IMAGE 8
#define NUM_STROKE_TOOLS 3
-#define NUM_TOOLS 8
+#define NUM_TOOLS 9
#define NUM_BUTTONS 3
#define TOOLOPT_ERASER_STANDARD 0
gchar *font_name;
gdouble font_size;
GtkWidget *widget; // the widget while text is being edited (ITEM_TEMP_TEXT)
+ // the following fields for ITEM_IMAGE:
+ GdkPixbuf *image; // the image
+ gchar *image_png; // PNG of original image, for save and clipboard
+ gsize image_png_len;
} Item;
// item type values for Item.type, UndoItem.type, ui.cur_item_type ...
#define ITEM_TEXT_ATTRIB 21
#define ITEM_RESIZESEL 22
#define ITEM_RECOGNIZER 23
+#define ITEM_IMAGE 24
typedef struct Layer {
GList *items; // the items on the layer, from bottom to top
gboolean hand_scrollto_pending;
char *filename;
gchar *default_path; // default path for new notes
+ gchar *default_image; // path for previous image
gboolean view_continuous, fullscreen, maximize_at_start;
gboolean in_update_page_stuff; // semaphore to avoid scrollbar retroaction
struct Selection *selection;
typedef struct UndoItem {
int type;
- struct Item *item; // for ITEM_STROKE, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_TEXT_ATTRIB
- struct Layer *layer; // for ITEM_STROKE, ITEM_ERASURE, ITEM_PASTE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_MOVESEL, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_RECOGNIZER
+ struct Item *item; // for ITEM_STROKE, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_TEXT_ATTRIB, ITEM_IMAGE
+ struct Layer *layer; // for ITEM_STROKE, ITEM_ERASURE, ITEM_PASTE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_MOVESEL, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_RECOGNIZER, ITEM_IMAGE
struct Layer *layer2; // for ITEM_DELETE_LAYER with val=-1, ITEM_MOVESEL
struct Page *page; // for ITEM_NEW_BG_ONE/RESIZE, ITEM_NEW_PAGE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_DELETE_PAGE
GList *erasurelist; // for ITEM_ERASURE, ITEM_RECOGNIZER
<child>
<widget class="GtkMenuItem" id="menuJournal">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Journal</property>
+ <property name="label" translatable="yes">_Page</property>
<property name="use_underline">True</property>
<child>
</widget>
</child>
+ <child>
+ <widget class="GtkRadioMenuItem" id="toolsImage">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Image</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="group">toolsPen</property>
+ <signal name="toggled" handler="on_toolsImage_activate" last_modification_time="Wed, 27 Jun 2012 20:54:08 GMT"/>
+ <accelerator key="I" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+ </widget>
+ </child>
+
<child>
<widget class="GtkSeparatorMenuItem" id="separator15">
<property name="visible">True</property>
</widget>
</child>
+ <child>
+ <widget class="GtkRadioMenuItem" id="button2Image">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Image</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="group">button2Pen</property>
+ <signal name="activate" handler="on_button2Image_activate" last_modification_time="Wed, 27 Jun 2012 14:33:10 GMT"/>
+ </widget>
+ </child>
+
<child>
<widget class="GtkRadioMenuItem" id="button2SelectRegion">
<property name="visible">True</property>
</widget>
</child>
+ <child>
+ <widget class="GtkRadioMenuItem" id="button3Image">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Image</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="group">button3Pen</property>
+ <signal name="activate" handler="on_button3Image_activate" last_modification_time="Wed, 27 Jun 2012 14:33:10 GMT"/>
+ </widget>
+ </child>
+
<child>
<widget class="GtkRadioMenuItem" id="button3SelectRegion">
<property name="visible">True</property>
</packing>
</child>
+ <child>
+ <widget class="GtkRadioToolButton" id="buttonImage">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Image</property>
+ <property name="label" translatable="yes">Image</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-orientation-portrait</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <property name="active">False</property>
+ <property name="group">buttonPen</property>
+ <signal name="toggled" handler="on_toolsImage_activate" last_modification_time="Wed, 27 Jun 2012 20:49:10 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
<child>
<widget class="GtkToggleToolButton" id="buttonReco">
<property name="visible">True</property>