[gnumeric] Read and write cell comments from/to OpenXML. [#630627]



commit f4b3de799a2e22ed37a0f939410b62c05050ace4
Author: Jean Brefort <jean brefort normalesup org>
Date:   Fri Oct 1 14:17:26 2010 +0200

    Read and write cell comments from/to OpenXML. [#630627]

 NEWS                       |    1 +
 plugins/excel/ChangeLog    |   12 ++
 plugins/excel/xlsx-read.c  |  287 +++++++++++++++++++++++++++++++++++++++++++-
 plugins/excel/xlsx-write.c |  154 +++++++++++++++++++++++-
 4 files changed, 451 insertions(+), 3 deletions(-)
---
diff --git a/NEWS b/NEWS
index e06ab0c..3ab9015 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Andreas:
 
 Jean:
 	* Export/import tick label rotation angle. [#629675]
+	* Read and write cell comments from/to OpenXML. [#630627]
 
 J.H.M. Dassen (Ray):
 	* Revised ssconvert man page.
diff --git a/plugins/excel/ChangeLog b/plugins/excel/ChangeLog
index 047ce1e..ab442d3 100644
--- a/plugins/excel/ChangeLog
+++ b/plugins/excel/ChangeLog
@@ -1,3 +1,15 @@
+2010-10-01  Jean Brefort  <jean brefort normalesup org>
+	* xlsx-read.c (elem_color), (xlsx_run_weight), (xlsx_run_style),
+	(xlsx_run_family), (xlsx_run_size), (xlsx_run_strikethrough),
+	(xlsx_run_underline), (xlsx_run_color), (xlsx_comments_start),
+	(xlsx_comments_end), (xlsx_comment_author_end),
+	(xlsx_comment_start), (xlsx_comment_end), (xlsx_comment_text),
+	(xlsx_comment_rich_text), (xlsx_wb_end), (xlsx_file_open): read cell
+	comments. [#630627]
+	* xlsx-write.c (xlsx_write_rich_text), (write_comment_author),
+	(xlsx_write_comments), (xlsx_write_sheet), (xlsx_file_save): write cell
+	comments.
+
 2010-09-15  Jean Brefort  <jean brefort normalesup org>
 
 	* ms-chart.c (tick), (chart_write_axis): export/import tick label rotation
diff --git a/plugins/excel/xlsx-read.c b/plugins/excel/xlsx-read.c
index 1c478ee..23976a5 100644
--- a/plugins/excel/xlsx-read.c
+++ b/plugins/excel/xlsx-read.c
@@ -48,6 +48,7 @@
 #include "gutils.h"
 #include "graph.h"
 #include "sheet-object-graph.h"
+#include "sheet-object-cell-comment.h"
 #include "gnm-sheet-slicer.h"
 
 #include <goffice/goffice.h>
@@ -124,6 +125,7 @@ typedef struct {
 
 	GArray		*sst;
 	PangoAttrList	*rich_attrs;
+	PangoAttrList	*run_attrs;
 
 	GHashTable	*num_fmts;
 	GOFormat	*date_fmt;
@@ -212,6 +214,11 @@ typedef struct {
 		unsigned int	   field_count, record_count;
 		char		  *cache_record_part_id;
 	} pivot;
+
+	/* Comment state */
+	GPtrArray	*authors;
+	GObject		*comment;
+	GString		*comment_text;
 } XLSXReadState;
 typedef struct {
 	GOString	*str;
@@ -2461,7 +2468,7 @@ elem_color (GsfXMLIn *xin, xmlChar const **attrs)
 {
 	XLSXReadState	*state = (XLSXReadState *)xin->user_state;
 	int indx;
-	GOColor c;
+	GOColor c = GO_COLOR_BLACK;
 	gnm_float tint = 0.;
 	gboolean has_color = FALSE;
 
@@ -4262,12 +4269,272 @@ xlsx_wb_external_ref (GsfXMLIn *xin, xmlChar const **attrs)
 /**************************************************************************************************/
 
 static void
+xlsx_run_weight (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "val")) {
+			PangoAttribute *attr = pango_attr_weight_new (strcmp (attrs[1], "true")? PANGO_WEIGHT_NORMAL: PANGO_WEIGHT_BOLD);
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_run_style (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "val")) {
+			PangoAttribute *attr = pango_attr_style_new (strcmp (attrs[1], "true")? PANGO_STYLE_NORMAL: PANGO_STYLE_ITALIC);
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_run_family (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "val")) {
+			PangoAttribute *attr = pango_attr_family_new (attrs[1]);
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_run_size (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "val")) {
+			PangoAttribute *attr = pango_attr_size_new (atoi (attrs[1]) * PANGO_SCALE);
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_run_strikethrough (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "val")) {
+			PangoAttribute *attr = pango_attr_strikethrough_new (!strcmp (attrs[1], "true"));
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_run_underline (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "val")) {
+			PangoAttribute *attr;
+			if (!strcmp (attrs[1], "single"))
+			    attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+			else if (!strcmp (attrs[1], "singleAccounting"))
+			    attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
+			else if (!strcmp (attrs[1], "double") || !strcmp (attrs[1], "doubleAccounting"))
+			    attr = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE);
+			else
+			    attr = pango_attr_underline_new (PANGO_UNDERLINE_NONE);
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_run_color (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "rgb")) {
+			PangoAttribute *attr;
+			unsigned a, r = 0, g = 0, b = 0;
+			if (4 != sscanf (attrs[1], "%02x%02x%02x%02x", &a, &r, &g, &b)) {
+				xlsx_warning (xin,
+					_("Invalid color '%s' for attribute rgb"),
+					attrs[1]);
+			}
+			attr = pango_attr_foreground_new (r, g, b);
+			if (state->run_attrs == NULL)
+				state->run_attrs = pango_attr_list_new ();
+			pango_attr_list_insert (state->run_attrs, attr);
+			
+		}
+}
+
+static void
+xlsx_comments_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	state->authors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+}
+
+static void
+xlsx_comments_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	g_ptr_array_unref (state->authors);
+	state->authors = NULL;
+}
+
+static void
+xlsx_comment_author_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	int i = strlen (xin->content->str);
+	char *name = xin->content->str;
+	/* remove any trailing white space */
+	/* not sure this is correct, we might be careful about encoding */
+	while (i > 0 && g_ascii_isspace (name[i-1]))
+		i--;
+	name = g_new (char, i + 1);
+	memcpy (name, xin->content->str, i);
+	name[i] = 0;
+	g_ptr_array_add (state->authors, name);
+}
+
+static void
+xlsx_comment_start (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	SheetObject *so;
+	GnmRange anchor_r;
+	SheetObjectAnchor	anchor;
+
+	state->comment = g_object_new (cell_comment_get_type (), NULL);
+	so = SHEET_OBJECT (state->comment);
+	anchor_r = sheet_object_get_anchor (so)->cell_bound;
+
+	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
+		if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "ref"))
+			range_parse (&anchor_r, attrs[1], gnm_sheet_get_size (state->sheet));
+		else if (gsf_xml_in_namecmp (xin, attrs[0], XL_NS_SS, "authorId")) {
+			unsigned id = atoi (attrs[1]);
+			char const *name;
+			if (id < state->authors->len) {
+				name = g_ptr_array_index (state->authors, id);
+				if (*name) /* do not set an empty name */
+					g_object_set (state->comment, "author", name, NULL);
+			}
+		}
+
+	sheet_object_anchor_init (&anchor, &anchor_r, NULL, GOD_ANCHOR_DIR_UNKNOWN);
+	sheet_object_set_anchor (so, &anchor);
+	state->comment_text = g_string_new ("");
+}
+
+static void
+xlsx_comment_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	char *text = g_string_free (state->comment_text, FALSE);
+	state->comment_text = NULL;
+	g_object_set (state->comment, "text", text, NULL);
+	g_free (text);
+	if (state->rich_attrs) {
+		g_object_set (state->comment, "markup", state->rich_attrs, NULL);
+		pango_attr_list_unref (state->rich_attrs);
+		state->rich_attrs = NULL;
+	}
+	sheet_object_set_sheet (SHEET_OBJECT (state->comment), state->sheet);
+	state->comment = NULL;
+	
+}
+
+static void
+xlsx_comment_text (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	g_string_append (state->comment_text, xin->content->str);
+}
+
+static void
+xlsx_comment_rich_text (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+	XLSXReadState *state = (XLSXReadState *)xin->user_state;
+	if (state->run_attrs) {
+		unsigned start, end;
+		start = state->comment_text->len;
+		end = start + strlen (xin->content->str);
+		if (state->rich_attrs == NULL)
+			state->rich_attrs = pango_attr_list_new ();
+		pango_attr_list_splice (state->rich_attrs, state->run_attrs, start, end);
+		pango_attr_list_unref (state->run_attrs);
+		state->run_attrs = NULL;
+	}
+	g_string_append (state->comment_text, xin->content->str);
+}
+
+static GsfXMLInNode const xlsx_comments_dtd[] = {
+GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
+GSF_XML_IN_NODE_FULL (START, COMMENTS, XL_NS_SS, "comments", GSF_XML_NO_CONTENT, TRUE, TRUE, xlsx_comments_start, xlsx_comments_end, 0),
+  GSF_XML_IN_NODE (COMMENTS, AUTHORS, XL_NS_SS, "authors", GSF_XML_NO_CONTENT, NULL, NULL),
+    GSF_XML_IN_NODE (AUTHORS, AUTHOR, XL_NS_SS, "author", GSF_XML_CONTENT, NULL, xlsx_comment_author_end),
+  GSF_XML_IN_NODE (COMMENTS, COMMENTLIST, XL_NS_SS, "commentList", GSF_XML_NO_CONTENT, NULL, NULL),
+    GSF_XML_IN_NODE (COMMENTLIST, COMMENT, XL_NS_SS, "comment", GSF_XML_NO_CONTENT, xlsx_comment_start, xlsx_comment_end),
+      GSF_XML_IN_NODE (COMMENT, TEXTITEM, XL_NS_SS, "text", GSF_XML_NO_CONTENT, NULL, NULL),
+        GSF_XML_IN_NODE (TEXTITEM, TEXT, XL_NS_SS, "t", GSF_XML_CONTENT, NULL, xlsx_comment_text),
+        GSF_XML_IN_NODE (TEXTITEM, RICH, XL_NS_SS, "r", GSF_XML_NO_CONTENT, NULL, NULL),
+          GSF_XML_IN_NODE (RICH, RICH_TEXT, XL_NS_SS, "t", GSF_XML_CONTENT, NULL, xlsx_comment_rich_text),
+          GSF_XML_IN_NODE (RICH, RICH_PROPS, XL_NS_SS, "rPr", GSF_XML_NO_CONTENT, NULL, NULL),
+#if 0
+	GSF_XML_IN_NODE (RICH_PROPS, RICH_FONT, XL_NS_SS, "font", GSF_XML_NO_CONTENT, NULL, NULL),
+	/* docs say 'font' xl is generating rFont */
+#endif
+    	  GSF_XML_IN_NODE (RICH_PROPS, RICH_FONT, XL_NS_SS, "rFont", GSF_XML_NO_CONTENT, NULL, NULL),
+/* Are all these really used by excel? */
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_CHARSET, XL_NS_SS, "charset", GSF_XML_NO_CONTENT, NULL, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_FAMILY, XL_NS_SS, "family", GSF_XML_NO_CONTENT, xlsx_run_family, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_BOLD, XL_NS_SS, "b", GSF_XML_NO_CONTENT, xlsx_run_weight, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_ITALIC, XL_NS_SS, "i", GSF_XML_NO_CONTENT, xlsx_run_style, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_STRIKE, XL_NS_SS, "strike", GSF_XML_NO_CONTENT, xlsx_run_strikethrough, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_OUTLINE, XL_NS_SS, "outline", GSF_XML_NO_CONTENT, NULL, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_SHADOW, XL_NS_SS, "shadow", GSF_XML_NO_CONTENT, NULL, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_CONDENSE, XL_NS_SS, "condense", GSF_XML_NO_CONTENT, NULL, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_EXTEND, XL_NS_SS, "extend", GSF_XML_NO_CONTENT, NULL, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_COLOR, XL_NS_SS, "color", GSF_XML_NO_CONTENT, xlsx_run_color, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_SZ, XL_NS_SS, "sz", GSF_XML_NO_CONTENT, xlsx_run_size, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_ULINE, XL_NS_SS, "u", GSF_XML_NO_CONTENT, xlsx_run_underline, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_VALIGN, XL_NS_SS, "vertAlign", GSF_XML_NO_CONTENT, NULL, NULL),
+	    GSF_XML_IN_NODE (RICH_PROPS, RICH_SCHEME, XL_NS_SS, "scheme", GSF_XML_NO_CONTENT, NULL, NULL),
+          GSF_XML_IN_NODE (RICH, RICH_PROPS, XL_NS_SS, "rPr", GSF_XML_NO_CONTENT, NULL, NULL),
+        GSF_XML_IN_NODE (TEXTITEM, ITEM_PHONETIC_RUN, XL_NS_SS, "rPh", GSF_XML_NO_CONTENT, NULL, NULL),
+          GSF_XML_IN_NODE (ITEM_PHONETIC_RUN, PHONETIC_TEXT, XL_NS_SS, "t", GSF_XML_CONTENT, NULL, NULL),
+        GSF_XML_IN_NODE (TEXTITEM, ITEM_PHONETIC, XL_NS_SS, "phoneticPr", GSF_XML_NO_CONTENT, NULL, NULL),
+	
+GSF_XML_IN_NODE_END
+};
+
+/**************************************************************************************************/
+
+static void
 xlsx_wb_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 {
 	XLSXReadState *state = (XLSXReadState *)xin->user_state;
 	int i, n = workbook_sheet_count (state->wb);
 	char const *part_id;
 	GnmStyle *style;
+	GsfInput *sin, *cin;
+	GError *err = NULL;
 
 	/* Load sheets after setting up the workbooks to give us time to create
 	 * all of them and parse names */
@@ -4288,7 +4555,21 @@ xlsx_wb_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 			sheet_style_set_range (state->sheet, &r, style);
 		}
 
-		xlsx_parse_rel_by_id (xin, part_id, xlsx_sheet_dtd, xlsx_ns);
+		sin = gsf_open_pkg_open_rel_by_id (gsf_xml_in_get_input (xin), part_id, &err);
+		if (NULL != err) {
+			XLSXReadState *state = (XLSXReadState *)xin->user_state;
+			go_io_warning (state->context, "%s", err->message);
+			g_error_free (err);
+			err = NULL;
+			continue;
+		}
+		/* load comments */
+		
+		cin = gsf_open_pkg_open_rel_by_type (sin,
+			"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";, NULL);
+		xlsx_parse_stream (state, sin, xlsx_sheet_dtd);
+		if (cin != NULL)
+			xlsx_parse_stream (state, cin, xlsx_comments_dtd);
 
 		/* Flag a respan here in case nothing else does */
 		sheet_flag_recompute_spans (state->sheet);
@@ -5169,6 +5450,8 @@ xlsx_file_open (GOFileOpener const *fo, GOIOContext *context,
 	state.wb_view	= wb_view;
 	state.wb	= wb_view_get_workbook (wb_view);
 	state.sheet	= NULL;
+	state.run_attrs	= NULL;
+	state.rich_attrs = NULL;
 	state.sst = g_array_new (FALSE, TRUE, sizeof (XLSXStr));
 	state.shared_exprs = g_hash_table_new_full (g_str_hash, g_str_equal,
 		(GDestroyNotify)g_free, (GDestroyNotify) gnm_expr_top_unref);
diff --git a/plugins/excel/xlsx-write.c b/plugins/excel/xlsx-write.c
index f6b371c..1b566dc 100644
--- a/plugins/excel/xlsx-write.c
+++ b/plugins/excel/xlsx-write.c
@@ -46,6 +46,7 @@
 #include "print-info.h"
 #include "gutils.h"
 #include "sheet-object.h"
+#include "sheet-object-cell-comment.h"
 #include "sheet-object-graph.h"
 #include "graph.h"
 
@@ -71,6 +72,7 @@ static char const *ns_rel	 = "http://schemas.openxmlformats.org/officeDocument/2
 static char const *ns_rel_hlink	 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";;
 static char const *ns_rel_draw	 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";;
 static char const *ns_rel_chart	 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";;
+static char const *ns_rel_com	 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";;
 
 typedef struct {
 	XLExportBase base;
@@ -86,6 +88,7 @@ typedef struct {
 		unsigned int	count;
 		GsfOutfile	*dir;
 	} chart, drawing, pivotCache, pivotTable;
+	unsigned comment;
 	GOFormat *date_fmt;
 } XLSXWriteState;
 
@@ -1242,6 +1245,145 @@ xlsx_write_objects (XLSXWriteState *state, GsfOutput *sheet_part, GSList *object
 	return rId;
 }
 
+static void
+xlsx_write_rich_text (GsfXMLOut *xml, char const *text, PangoAttrList *attrs)
+{
+	PangoAttrIterator *iter =  pango_attr_list_get_iterator (attrs);
+	PangoAttribute *attr;
+	int start, end, max = strlen (text);
+	char *buf;
+	do {
+		gsf_xml_out_start_element (xml, "r");
+		gsf_xml_out_start_element (xml, "rPr");
+		gsf_xml_out_start_element (xml, "rFont");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
+		gsf_xml_out_add_cstr_unchecked (xml, "val", (attr)? ((PangoAttrString *) attr)->value: "Calibri");
+		gsf_xml_out_end_element (xml); /* </rFont> */
+		gsf_xml_out_start_element (xml, "b");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT);
+		gsf_xml_out_add_cstr_unchecked (xml, "val", (attr && ((PangoAttrInt *) attr)->value > PANGO_WEIGHT_NORMAL)? "true": "false");
+		gsf_xml_out_end_element (xml); /* </b> */
+		gsf_xml_out_start_element (xml, "i");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_STYLE);
+		gsf_xml_out_add_cstr_unchecked (xml, "val", (attr && ((PangoAttrInt *) attr)->value != PANGO_STYLE_NORMAL)? "true": "false");
+		gsf_xml_out_end_element (xml); /* </i> */
+		gsf_xml_out_start_element (xml, "strike");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH);
+		gsf_xml_out_add_cstr_unchecked (xml, "val", (attr && ((PangoAttrInt *) attr)->value)? "true": "false");
+		gsf_xml_out_end_element (xml); /* </strike> */
+		gsf_xml_out_start_element (xml, "color");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
+		if (attr) {
+			PangoColor *color = &((PangoAttrColor *) attr)->color;
+			g_strdup_printf("FF%2x%2x%2x", color->red >> 8, color->green >> 8, color->blue >> 8);
+			gsf_xml_out_add_cstr_unchecked (xml, "rgb", buf);
+			g_free (buf);
+		} else
+			gsf_xml_out_add_cstr_unchecked (xml, "rgb", "FF000000");
+		gsf_xml_out_end_element (xml); /* </color> */
+		gsf_xml_out_start_element (xml, "sz");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
+		gsf_xml_out_add_uint (xml, "val", (attr)? ((PangoAttrInt *) attr)->value / PANGO_SCALE: 8);
+		gsf_xml_out_end_element (xml); /* </sz> */
+		gsf_xml_out_start_element (xml, "u");
+		attr = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
+		if (attr) {
+			PangoUnderline u = ((PangoAttrInt *) attr)->value;
+			switch (u) {
+			case PANGO_UNDERLINE_NONE:
+			default:
+				gsf_xml_out_add_cstr_unchecked (xml, "val", "none");
+				break;
+			case PANGO_UNDERLINE_ERROR: /* not supported by OpenXML */
+			case PANGO_UNDERLINE_SINGLE:
+				gsf_xml_out_add_cstr_unchecked (xml, "val", "single");
+			case PANGO_UNDERLINE_DOUBLE:
+				gsf_xml_out_add_cstr_unchecked (xml, "val", "double");
+			case PANGO_UNDERLINE_LOW:
+				gsf_xml_out_add_cstr_unchecked (xml, "val", "singleAccounting");
+			}
+		} else
+			gsf_xml_out_add_cstr_unchecked (xml, "val", "none");
+		gsf_xml_out_end_element (xml); /* </u> */
+		gsf_xml_out_end_element (xml); /* </rPr> */
+		gsf_xml_out_start_element (xml, "t");
+		gsf_xml_out_add_cstr_unchecked (xml, "xml:space", "preserve");
+		pango_attr_iterator_range (iter, &start, &end);
+		if (end > max)
+		    end = max;
+		buf = g_strndup (text + start, end - start);
+		gsf_xml_out_add_cstr_unchecked (xml, NULL, buf);
+		g_free (buf);
+		gsf_xml_out_end_element (xml); /* </t> */
+		gsf_xml_out_end_element (xml); /* </r> */
+	} while (pango_attr_iterator_next (iter));
+	pango_attr_iterator_destroy (iter);
+}
+
+static void
+write_comment_author (gpointer key, G_GNUC_UNUSED gpointer value, GsfXMLOut *xml)
+{
+	gsf_xml_out_start_element (xml, "author");
+	gsf_xml_out_add_cstr_unchecked (xml, NULL, (char const *) key);
+	gsf_xml_out_end_element (xml);
+}
+
+static void
+xlsx_write_comments (XLSXWriteState *state, GsfOutput *sheet_part, GSList *objects)
+{
+	GsfXMLOut *xml;
+	GHashTable *authors;
+	unsigned author = 0;
+	char const *authorname;
+	GSList *ptr;
+	SheetObjectAnchor const *anchor;
+	PangoAttrList *attrs;
+	char *name = g_strdup_printf ("comments%u.xml", ++state->comment);
+	GsfOutput *comments_part = gsf_outfile_new_child_full (state->xl_dir, name, FALSE,
+		"content-type", "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
+		NULL);
+	g_free (name);
+	gsf_outfile_open_pkg_relate (GSF_OUTFILE_OPEN_PKG (comments_part),
+		GSF_OUTFILE_OPEN_PKG (sheet_part), ns_rel_com);
+	xml = gsf_xml_out_new (comments_part);
+	gsf_xml_out_start_element (xml, "comments");
+	gsf_xml_out_add_cstr_unchecked (xml, "xmlns", ns_ss);
+	/* search for comments authors */
+	authors = g_hash_table_new (g_str_hash, g_str_equal);
+	for (ptr = objects; ptr; ptr = ptr->next) {
+		authorname = cell_comment_author_get (CELL_COMMENT (ptr->data));
+		if (!g_hash_table_lookup_extended (authors, authorname, NULL, NULL))
+			g_hash_table_insert (authors, (gpointer) authorname, GUINT_TO_POINTER (author++));
+	}
+	/* save authors */
+	gsf_xml_out_start_element (xml, "authors");
+	g_hash_table_foreach (authors, (GHFunc) write_comment_author, xml);
+	gsf_xml_out_end_element (xml); /* </authors> */
+	/* save comments */
+	gsf_xml_out_start_element (xml, "commentList");
+	for (ptr = objects; ptr; ptr = ptr->next) {
+		gsf_xml_out_start_element (xml, "comment");
+		anchor = sheet_object_get_anchor (ptr->data);
+		gsf_xml_out_add_cstr_unchecked (xml, "ref", range_as_string (&anchor->cell_bound));
+		gsf_xml_out_add_uint (xml, "authorId", GPOINTER_TO_UINT (g_hash_table_lookup (authors, cell_comment_author_get (CELL_COMMENT (ptr->data)))));
+		gsf_xml_out_start_element (xml, "text");
+		/* Save text as rich text */
+		g_object_get (ptr->data, "text", &name, "markup", &attrs, NULL);
+		if (name && *name)
+			xlsx_write_rich_text (xml, name, attrs);
+		g_free (name);
+		pango_attr_list_unref (attrs);
+		gsf_xml_out_end_element (xml); /* </text> */
+		gsf_xml_out_end_element (xml); /* </comment> */
+	}
+	gsf_xml_out_end_element (xml); /* </commentList> */
+	g_hash_table_destroy (authors);
+	gsf_xml_out_end_element (xml); /* </comments> */
+	g_object_unref (xml);
+	gsf_output_close (comments_part);
+	g_object_unref (comments_part);
+}
+
 static char const *
 xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, unsigned i)
 {
@@ -1265,6 +1407,14 @@ xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, u
 		MIN (XLSX_MAX_COLS, gnm_sheet_get_max_cols (state->sheet)),
 		MIN (XLSX_MAX_ROWS, gnm_sheet_get_max_rows (state->sheet)), state->io_context);
 
+/*   comments   */
+	charts = sheet_objects_get (state->sheet, NULL, CELL_COMMENT_TYPE);
+	if (NULL != charts) {
+		xlsx_write_comments (state, sheet_part, charts);
+		g_slist_free (charts);
+	}
+	                                               
+/*   charts   */
 	charts = sheet_objects_get (state->sheet, NULL, SHEET_OBJECT_GRAPH_TYPE);
 	if (NULL != charts) {
 		chart_drawing_rel_id = xlsx_write_objects (state, sheet_part, charts);
@@ -1357,13 +1507,14 @@ xlsx_write_sheet (XLSXWriteState *state, GsfOutfile *dir, GsfOutfile *wb_part, u
 /*   element extLst { CT_ExtensionList }?     */
 	gsf_xml_out_end_element (xml); /* </worksheet> */
 
-	state->sheet = NULL;
 	g_object_unref (xml);
 	gsf_output_close (sheet_part);
 	g_object_unref (sheet_part);
 	g_free (name);
 	g_free (col_styles);
 
+	state->sheet = NULL;
+
 	return rId;
 }
 
@@ -1508,6 +1659,7 @@ xlsx_file_save (GOFileSaver const *fs, GOIOContext *io_context,
 
 	state.io_context	= io_context;
 	state.base.wb		= wb_view_get_workbook (wb_view);
+	state.comment		= 0;
 	root_part = gsf_outfile_open_pkg_new (
 		gsf_outfile_zip_new (output, NULL));
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]