diff --git a/plugins/openoffice/openoffice-read.c b/plugins/openoffice/openoffice-read.c
index 399c3bf..9f02fe3 100644
--- a/plugins/openoffice/openoffice-read.c
+++ b/plugins/openoffice/openoffice-read.c
@@ -1179,6 +1179,32 @@ oo_expr_rangeref_parse (GnmRangeRef *ref, char const *start, GnmParsePos const *
 	return start;
 }
 
+static char const *
+odf_strunescape (char const *string, GString *target,
+		   G_GNUC_UNUSED GnmConventions const *convs)
+{
+	char quote = *string++;
+	size_t oldlen = target->len;
+
+	/* This should be UTF-8 safe as long as quote is ASCII.  */
+	do {
+		while (*string != quote) {
+			if (*string == '\0')
+				goto error;
+			g_string_append_c (target, *string);
+			string++;
+		}
+		string++;
+		if (*string == quote)
+			g_string_append_c (target, quote);
+	} while (*string++ == quote);
+	return --string;
+
+ error:
+	g_string_truncate (target, oldlen);
+	return NULL;	
+}
+
 static GnmExpr const *
 oo_func_map_in (GnmConventions const *convs, Workbook *scope,
 		char const *name, GnmExprList *args);
@@ -1197,6 +1223,7 @@ oo_conventions_new (void)
 	conv->arg_sep		= ';';
 	conv->array_col_sep	= ';';
 	conv->array_row_sep	= '|';
+	conv->input.string	= odf_strunescape;
 	conv->input.func	= oo_func_map_in;
 	conv->input.range_ref	= oo_expr_rangeref_parse;
 	conv->sheet_name_sep	= '.';
diff --git a/src/parse-util.c b/src/parse-util.c
index 0af15fb..5a704d2 100644
--- a/src/parse-util.c
+++ b/src/parse-util.c
@@ -1332,6 +1332,12 @@ std_external_wb (G_GNUC_UNUSED GnmConventions const *convs,
 	return gnm_app_workbook_get_by_name (wb_name, ref_uri);
 }
 
+static char const *
+std_string_parser (char const *in, GString *target,
+		   G_GNUC_UNUSED GnmConventions const *convs)
+{
+	return go_strunescape (target, in);
+}
 
 /**
  * gnm_conventions_new_full :
@@ -1359,6 +1365,7 @@ gnm_conventions_new_full (unsigned size)
 	convs->intersection_char	= ' ';
 	convs->exp_is_left_associative  = FALSE;
 	convs->input.range_ref		= rangeref_parse;
+	convs->input.string		= std_string_parser;
 	convs->input.name		= std_name_parser;
 	convs->input.func		= std_func_map;
 	convs->input.external_wb	= std_external_wb;
diff --git a/src/parse-util.h b/src/parse-util.h
index 7270e07..cbe4b05 100644
--- a/src/parse-util.h
+++ b/src/parse-util.h
@@ -151,12 +151,15 @@ struct _GnmConventions {
 					  GnmConventions const *convs);
 					/* GError **err); */
 
+		/* Called to unescape strings */
+		char const *(*string) (char const *in, GString *target,
+				       GnmConventions const *convs);
+
 		/* Called a lot for anything that might be a function name or
 		 * defined name.  */
 		char const *(*name) (char const *in,
 				     GnmConventions const *convs);
 
-
 		/* Must return non-NULL, and absorb the args, including the list. */
 		GnmExpr const *(*func) (GnmConventions const *convs,
 				        /* make scope more useful, eg a
diff --git a/src/parser.y b/src/parser.y
index f599b47..1944056 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -1357,7 +1357,7 @@ yylex (void)
 	case '\'':
 	case '"': {
 		GString *s = g_string_new (NULL);
-		char const *end = go_strunescape (s, start);
+		char const *end = state->convs->input.string (start, s, state->convs);
 
 		if (end == NULL) {
 			size_t len = strlen (start);
