diff --git a/ChangeLog b/ChangeLog
index d4c6760..58518ca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2012-11-03  Morten Welinder  <terra@gnome.org>
+
+	* src/sheet-style.c (sheet_style_get_row2): New function.
+
 2012-11-02  Jean Brefort  <jean.brefort@normalesup.org>
 
 	* src/sheet-object-image.c (gnm_soi_write_xml_sax): fixed crash when
diff --git a/NEWS b/NEWS
index f43b0f4..0572c25 100644
--- a/NEWS
+++ b/NEWS
@@ -12,7 +12,7 @@ Jean:
 
 Morten:
 	* Fix xlsx save performance problems.  [#662058]  [#685530]
-	* Fix ods save performance problems.  [#662057]
+	* Fix ods save performance problems.  [#662057, #687243]
 	* Make .gnumeric use fewer style rectangles for the same style.
 	* Short-circuit column resize for large stf imports.  [#686858]
 	* Enhance POWER to do root of negative numbers.  [#687269]
diff --git a/plugins/openoffice/ChangeLog b/plugins/openoffice/ChangeLog
index d00bc62..c2abcc4 100644
--- a/plugins/openoffice/ChangeLog
+++ b/plugins/openoffice/ChangeLog
@@ -1,3 +1,11 @@
+2012-11-03  Morten Welinder  <terra@gnome.org>
+
+	* openoffice-write.c (row_info_equal): Rename from row_style_eq.
+	(compare_row_styles): New function.
+	(odf_write_content_rows): Handle repeated rows that have
+	non-default style too.  Avoid single-cell style lookups in favour
+	of getting the whole row in one go.  Fixes #687243.
+
 2012-10-30  Andreas J. Guelzow <aguelzow@pyrshep.ca>
 
 	* openoffice-write.c (finder): deleted
diff --git a/plugins/openoffice/openoffice-write.c b/plugins/openoffice/openoffice-write.c
index 33386c0..51897a0 100644
--- a/plugins/openoffice/openoffice-write.c
+++ b/plugins/openoffice/openoffice-write.c
@@ -3416,8 +3416,8 @@ write_row_style (GnmOOExport *state, ColRowInfo const *ci,
 }
 
 static gboolean
-row_style_eq (GnmOOExport *state, Sheet const *sheet,
-	      ColRowInfo const *ci1, ColRowInfo const *ci2)
+row_info_equal (GnmOOExport *state, Sheet const *sheet,
+		ColRowInfo const *ci1, ColRowInfo const *ci2)
 {
 	if (ci1 == ci2)
 		return TRUE;
@@ -3434,6 +3434,20 @@ row_style_eq (GnmOOExport *state, Sheet const *sheet,
 	}
 }
 
+static gboolean
+compare_row_styles (const Sheet *sheet, GnmStyle **styles, int orow)
+{
+	GnmStyle **ostyles = sheet_style_get_row2 (sheet, orow);
+	gboolean res;
+
+	res = !memcmp (styles, ostyles,
+		       gnm_sheet_get_max_cols (sheet) * sizeof (GnmStyle *));
+
+	g_free (ostyles);
+
+	return res;
+}
+
 static GSList *
 odf_sheet_objects_get (Sheet const *sheet, GnmCellPos const *pos)
 {
@@ -3512,16 +3526,21 @@ odf_write_content_rows (GnmOOExport *state, Sheet const *sheet, int from, int to
 		g_free (non_defaults_rows);
 	}
 
-	for (row = from; row < to; row++) {
+	for (row = from; row < to; /* nothing here */) {
 		ColRowInfo const *ci = sheet_row_get (sheet, row);
 		GnmStyle const *null_style = NULL;
 		int null_cell = 0;
 		int covered_cell = 0;
 		GnmCellPos pos;
+		int repeat_count = 1;
+		guint8 rf = row_flags[row];
+		GnmStyle **row_styles =	(rf & RF_STYLE)
+			? sheet_style_get_row2 (sheet, row)
+			: NULL;
 
 		pos.row = row;
 
-		if (row_flags[row] & RF_PAGEBREAK)
+		if (rf & RF_PAGEBREAK)
 			gsf_xml_out_simple_element (state->xml,
 						    TEXT "soft-page-break",
 						    NULL);
@@ -3529,26 +3548,34 @@ odf_write_content_rows (GnmOOExport *state, Sheet const *sheet, int from, int to
 		gsf_xml_out_start_element (state->xml, TABLE "table-row");
 		write_row_style (state, ci, sheet);
 
-		if (row_flags[row] == 0) {
-			int count = 1;
-			while (row + count < to &&
-			       row_flags[row + count] == 0 &&
-			       row_style_eq (state, sheet, ci, sheet_row_get (sheet, row + count)))
-				count++;
-			if (count > 1) {
+		if ((rf & ~RF_STYLE) == 0) {
+			/*
+			 * We have nothing but style (possibly default) in this
+			 * row, so see if some rows following this one are
+			 * identical.
+			 */
+			int row2;
+			while ((row2 = row + repeat_count) < to &&
+			       row_flags[row2] == rf &&
+			       row_info_equal (state, sheet, ci, sheet_row_get (sheet, row2)) &&
+			       (rf == 0 || compare_row_styles (sheet, row_styles, row2)))
+				repeat_count++;
+
+			if (repeat_count > 1)
 				gsf_xml_out_add_int (state->xml, TABLE "number-rows-repeated",
-						     count);
-				row += (count - 1);
-			}
+						     repeat_count);
+		}
 
-		} else {
+		if (rf) {
 			int col;
 
 			for (col = 0; col < row_length; col++) {
 				GnmCell *current_cell;
 				GnmRange const	*merge_range;
 				GSList *objects;
-				GnmStyle const *this_style;
+				GnmStyle const *this_style = row_styles
+					? row_styles[col]
+					: col_styles[col];
 
 				current_cell = g_ptr_array_index (all_cells, cno);
 				if (current_cell &&
@@ -3559,6 +3586,7 @@ odf_write_content_rows (GnmOOExport *state, Sheet const *sheet, int from, int to
 					current_cell = NULL;
 
 				pos.col = col;
+
 				merge_range = gnm_sheet_merge_is_corner (sheet, &pos);
 
 				if (odf_cell_is_covered (sheet, current_cell, col, row,
@@ -3569,15 +3597,14 @@ odf_write_content_rows (GnmOOExport *state, Sheet const *sheet, int from, int to
 					continue;
 				}
 
-				objects = (row_flags[row] & RF_OBJECT)
+				objects = (rf & RF_OBJECT)
 					? odf_sheet_objects_get (sheet, &pos)
 					: NULL;
 
 				if ((!(current_cell && gnm_cell_has_expr(current_cell))) &&
 				    (merge_range == NULL) && (objects == NULL) &&
 				    gnm_cell_is_empty (current_cell) &&
-				    NULL == gnm_style_get_hlink
-				    ((this_style = sheet_style_get (sheet, pos.col, pos.row)))) {
+				    !gnm_style_get_hlink (this_style)) {
 					if ((null_cell == 0) || (null_style == this_style)) {
 						null_style = this_style;
 						if (covered_cell > 0)
@@ -3607,6 +3634,9 @@ odf_write_content_rows (GnmOOExport *state, Sheet const *sheet, int from, int to
 			odf_write_covered_cell (state, &covered_cell);
 
 		gsf_xml_out_end_element (state->xml);   /* table-row */
+
+		row += repeat_count;
+		g_free (row_styles);
 	}
 
 	g_ptr_array_free (all_cells, TRUE);
diff --git a/src/sheet-style.c b/src/sheet-style.c
index 5bea872..335e69d 100644
--- a/src/sheet-style.c
+++ b/src/sheet-style.c
@@ -1552,6 +1552,38 @@ sheet_style_get_row (Sheet const *sheet, GnmStyleRow *sr)
 	get_style_row (sheet->style_data->styles, sheet->tile_top_level, 0, 0, sr);
 }
 
+static void
+cb_get_row (GnmStyle *style,
+	    int corner_col, G_GNUC_UNUSED int corner_row,
+	    int width, G_GNUC_UNUSED int height,
+	    GnmRange const *apply_to, gpointer user_)
+{
+	GnmStyle **res = user_;
+	int i;
+
+	/* The given dimensions refer to the tile, not the area. */
+	width = MIN (width, apply_to->end.col - corner_col + 1);
+
+	for (i = 0; i < width; i++)
+		res[corner_col + i] = style;
+}
+
+GnmStyle **
+sheet_style_get_row2 (Sheet const *sheet, int row)
+{
+	GnmRange r;
+	GnmStyle **res = g_new (GnmStyle *, gnm_sheet_get_max_cols (sheet));
+
+	range_init_rows (&r, sheet, row, row);
+
+	foreach_tile (sheet->style_data->styles,
+		      sheet->tile_top_level, 0, 0, &r,
+		      cb_get_row, res);
+
+	return res;
+}
+
+
 /**
  * style_row_init :
  *
diff --git a/src/sheet-style.h b/src/sheet-style.h
index c7923a3..a9c1cb2 100644
--- a/src/sheet-style.h
+++ b/src/sheet-style.h
@@ -25,6 +25,7 @@ GnmStyle *sheet_style_default		(Sheet const *sheet);
 GnmStyle const *sheet_style_get		(Sheet const *sheet, int col, int row);
 GnmStyle *sheet_style_find  		(Sheet const *sheet, GnmStyle *st);
 void	 sheet_style_get_row		(Sheet const *sheet, GnmStyleRow *sr);
+GnmStyle **sheet_style_get_row2		(Sheet const *sheet, int row);
 void	 sheet_style_apply_border	(Sheet *sheet, GnmRange const *r,
 					 GnmBorder **borders);
 void	 sheet_style_apply_range	(Sheet *sheet, GnmRange const *r,
