#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", "selectregion", "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");
}
}
// sanitize a string containing floats, in case it may have , instead of .
+// also replace Windows-produced 1.#J by inf
void cleanup_numeric(char *s)
{
- while (*s!=0) { if (*s==',') *s='.'; s++; }
+ while (*s!=0) {
+ if (*s==',') *s='.';
+ if (*s=='1' && s[1]=='.' && s[2]=='#' && s[3]=='J')
+ { *s='i'; s[1]='n'; s[2]='f'; s[3]=' '; }
+ s++;
+ }
}
// the XML parser functions for open_journal()
}
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,
if (ptr == text) break;
text_len -= (ptr - text);
text = ptr;
- if (!finite(ui.cur_path.coords[n])) {
+ if (!finite_sized(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;
}
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;
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, selectregion, selectrect, vertspace, hand, image)"),
g_strdup(tool_names[ui.startuptool]));
update_keyval("tools", "pen_color",
_(" default pen color"),
_(" 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, selectregion, selectrect, vertspace, hand, image)"),
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 eraser mode (eraser only)"),
g_strdup_printf("%d", ui.brushes[1][TOOL_ERASER].tool_options));
update_keyval("tools", "btn3_tool",
- _(" button 3 tool (pen, eraser, highlighter, text, selectrect, vertspace, hand)"),
+ _(" button 3 tool (pen, eraser, highlighter, text, selectregion, selectrect, vertspace, hand, image)"),
g_strdup(tool_names[ui.toolno[2]]));
update_keyval("tools", "btn3_linked",
_(" button 3 brush linked to primary brush (true/false) (overrides all other settings)"),