changeset 10406:1600469c640f octave-forge

Support for odfdom-0.8.8-incubator; tabs replaced by double space
author prnienhuis
date Fri, 08 Jun 2012 15:28:27 +0000
parents 8467fcf135a3
children 7a142d50999f
files main/io/inst/getusedrange.m main/io/inst/oct2ods.m main/io/inst/ods2oct.m main/io/inst/odsclose.m main/io/inst/odsfinfo.m main/io/inst/odsopen.m main/io/inst/odsread.m main/io/inst/odswrite.m
diffstat 8 files changed, 1547 insertions(+), 1539 deletions(-) [+]
line wrap: on
line diff
--- a/main/io/inst/getusedrange.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/getusedrange.m	Fri Jun 08 15:28:27 2012 +0000
@@ -66,8 +66,10 @@
 ## 2011-06-13 OpenXLS support added
 ## 2011-09-08 Style & layout updates
 ## 2012-01-26 Fixed "seealso" help string
+## 2012-06-08 Replaced tabs by double space
+##     ''     Added COM and OXS to message about supported interfaces
 ##
-## Last subfunc update: 2012-04-18 (JOD)
+## Last subfunc update: 2012-06-08 (OTK)
 
 function [ trow, lrow, lcol, rcol ] = getusedrange (spptr, ii)
 
@@ -89,7 +91,7 @@
   elseif (strcmp (spptr.xtype, 'OXS'))
     [ trow, lrow, lcol, rcol ] = getusedrange_oxs (spptr, ii);
   else
-    error ('Only OTK, JOD, POI and JXL interface implemented');
+    error ("Only OTK, JOD, COM, POI, JXL and OXS interface implemented");
   endif
 
 endfunction
@@ -124,10 +126,11 @@
 ##            Currently this fix is just commented.
 ## 2011-06-06 Fixed wrong if clause for finding last filler cells (L.160 & L.176)
 ## 2011-09-12 Support for odfdom-0.8.7 added (API change for XPATH)
+## 2012-06-08 Support for odsfdom-0.8.8-incubator
 
 function [ trow, lrow, lcol, rcol ] = getusedrange_otk (ods, ii)
 
-  odfcont = ods.workbook;		# Local copy just in case
+  odfcont = ods.workbook;  # Local copy just in case
 
   if (isfield (ods, 'odfvsn'))
     if (strcmp (ods.odfvsn, '0.8.6') || strcmp (ods.odfvsn, '0.7.5'))
@@ -166,11 +169,11 @@
         reprows = 0;
       endif
 
-      # Get leftmost cell column number
+    # Get leftmost cell column number
       lcell = row.getFirstChild ();
       cl_char = char (lcell);
-      # Swap the following lines into comment to catch a jOpenDocument bug which foobars OTK
-      # (JOD doesn't set <office:value-type='string'> attribute when writing strings
+    # Swap the following lines into comment to catch a jOpenDocument bug which foobars OTK
+    # (JOD doesn't set <office:value-type='string'> attribute when writing strings
       #if (isempty (findstr ('office:value-type', cl_char)) || isempty (findstr ('<text:', cl_char)))
       if (isempty (findstr ('office:value-type', cl_char)))
         lcol = min (lcol, lcell.getTableNumberColumnsRepeatedAttribute () + 1);
@@ -232,7 +235,7 @@
 ## Created: 2010-05-25
 ## Last updates:
 ## 2010-05-31 Fixed ignoring table-covered-cells; fixed count of sheets comprising just A1:A1
-##     ''     Added option for wsh being a string argument 
+##            Added option for wsh being a string argument 
 ## 2010-08-12 Little textual adaptations
 ## 2010-11-13 Catched jOpenDocument bug (1.2bx) where string cells have no office:value-type
 ##     ''     attrb set (by JOD). Somehow OTK is more robust as it catches these cells
@@ -241,15 +244,15 @@
 
 function [ trow, brow, lcol, rcol ] = getusedrange_jod (ods, wsh)
 
-    # This function works by virtue of sheets in JOD actually being a Java string.
-    # It works outside of the Java memory/heap space which is an added benefit...
-    # (Read: this is one big dirty hack...... prone to crash Java on BIG spreadsheets)
+  # This function works by virtue of sheets in JOD actually being a Java string.
+  # It works outside of the Java memory/heap space which is an added benefit...
+  # (Read: this is one big dirty hack...... prone to crash Java on BIG spreadsheets)
 
-    if (isnumeric (wsh))
-    	sh = ods.workbook.getSheet (wsh - 1);
-    else
-    	sh = ods.workbook.getSheet (wsh);
-    endif
+  if (isnumeric (wsh))
+    sh = char (ods.workbook.getSheet (wsh - 1));
+  else
+    sh = char (ods.workbook.getSheet (wsh));
+  endif
 
   try
     # Let's see if we have JOD v. 1.3x. If not, next call fails & we'll fall back to the old hack
@@ -266,16 +269,12 @@
       brow = trow + nr - 1;
       rcol = lcol + nc - 1;
     endif
-    return
   
   catch
-    # This function works by virtue of sheets in JOD actually being a Java string.
-    # It works outside of the Java memory/heap space which is an added benefit...
-    # (Read: this is one big dirty hack...... prone to crash Java on BIG spreadsheets)
-
+    # Fall back to the old hack :-(
     sh = char (sh);
 
-    # Get table-row pointers
+    # 1. Get table-row pointers
     id_trow = strfind (sh, '<table:table-row');
     id = strfind (sh, '</table:table>') - 1;
     id_trow = [id_trow id];
@@ -283,96 +282,96 @@
     trow = rcol = 0;
     lcol = 1024; brow = 0;
     if (~isempty (id))
-    	# Loop over all table-rows
-    	rowrepcnt = 0;
-    	for irow = 1:length (id_trow)-1
-    		# Isolate single table-row
-    		tablerow = sh(id_trow(irow):id_trow(irow+1)-1);
-    		# Search table-cells. table-c covers both table-cell & table-covered-cell
-    		id_tcell = strfind (tablerow, '<table:table-c'); 
-    		id_tcell = [id_tcell id];
-    		rowl = length (tablerow);
-    		if (isempty (id_tcell(1:end-1)))
-    			rowend = rowl;
-    		else
-    			rowend = id_tcell(1);
-    		endif
-    		# Add in table-number-rows-repeated attribute values
-    		rowrept = strfind (tablerow(1:rowend), 'number-rows-repeated');
-    		if (~isempty (rowrept))
-    			[st, en] = regexp (tablerow(rowrept:min (rowend, rowrept+30)), '\d+');
-    			rowrepcnt += str2num (tablerow(rowrept+st-1:min (rowend, rowrept+en-1))) - 1;
-    		endif
+      # 2. Loop over all table-rows
+      rowrepcnt = 0;
+      for irow = 1:length (id_trow)-1
+        # Isolate single table-row
+        tablerow = sh(id_trow(irow):id_trow(irow+1)-1);
+        # Search table-cells. table-c covers both table-cell & table-covered-cell
+        id_tcell = strfind (tablerow, '<table:table-c'); 
+        id_tcell = [id_tcell id];
+        rowl = length (tablerow);
+        if (isempty (id_tcell(1:end-1)))
+          rowend = rowl;
+        else
+          rowend = id_tcell(1);
+        endif
+        # Add in table-number-rows-repeated attribute values
+        rowrept = strfind (tablerow(1:rowend), 'number-rows-repeated');
+        if (~isempty (rowrept))
+          [st, en] = regexp (tablerow(rowrept:min (rowend, rowrept+30)), '\d+');
+          rowrepcnt += str2num (tablerow(rowrept+st-1:min (rowend, rowrept+en-1))) - 1;
+        endif
 
-    		# Search table-cells. table-c is a table-covered-cell that is considered empty
-    		id_tcell = strfind (tablerow, '<table:table-c');
-    		if (~isempty (id_tcell))
-    			# OK, this row has a value cell. Now table-covered-cells must be included.
-    			id_tcell2 = strfind (tablerow, '<table:covered-t');
-    			if (~isempty (id_tcell2)) id_tcell = sort ([id_tcell id_tcell2]); endif
-    			id_tcell = [id_tcell rowl];
-    			# Search for non-empty cells (i.e., with an office:value-type attribute). But:
-    			# jOpenDocument 1.2b3 has a bug: it often doesn't set this attr for string cells
-    			id_valtcell = strfind (tablerow, 'office:value-type=');
-    			id_textonlycell = strfind (tablerow, '<text:');
-    			id_valtcell = sort ([id_valtcell id_textonlycell]);
-    			id_valtcell = [id_valtcell rowl];
-    			if (~isempty (id_valtcell(1:end-1)))
-    				brow = irow + rowrepcnt;
-    				# First set trow if it hadn't already been found
-    				if (~trow) trow = irow; endif
-    				# Search for repeated table-cells
-    				id_reptcell = strfind (tablerow, 'number-columns-repeated');
-    				id_reptcell = [id_reptcell rowl];
-    				# Search for leftmost non-empty table-cell. llcol = counter for this table-row
-    				llcol = 1;
-    				while (id_tcell (llcol) < id_valtcell(1) && llcol <= length (id_tcell) - 1)
-    					++llcol;
-    				endwhile
-    				--llcol;
-    				# Compensate for repeated cells. First count all repeats left of llcol
-    				ii = 1;
-    				repcnt = 0;
-    				if (~isempty (id_reptcell(1:end-1)))
-    					# First try lcol
-    					while (ii <= length (id_reptcell) - 1 && id_reptcell(ii) < id_valtcell(1))
-    						# Add all repeat counts left of leftmost data tcell minus 1 for each
-    						[st, en] = regexp (tablerow(id_reptcell(ii):id_reptcell(ii)+30), '\d+');
-    						repcnt += str2num (tablerow(id_reptcell(ii)+st-1:id_reptcell(ii)+en-1)) - 1;
-    						++ii;
-    					endwhile
-    					# Next, add current repcnt value to llcol and update lcol
-    					lcol = min (lcol, llcol + repcnt);
-    					# Get last value table-cell in table-cell idx
-    					jj = 1;
-    					while (id_tcell (jj) < id_valtcell(length (id_valtcell)-1))
-    						++jj;
-    					endwhile
+        # 3. Search table-cells. table-c is a table-covered-cell that is considered empty
+        id_tcell = strfind (tablerow, '<table:table-c');
+        if (~isempty (id_tcell))
+          # OK, this row has a value cell. Now table-covered-cells must be included.
+          id_tcell2 = strfind (tablerow, '<table:covered-t');
+          if (~isempty (id_tcell2)) id_tcell = sort ([id_tcell id_tcell2]); endif
+          id_tcell = [id_tcell rowl];
+          # Search for non-empty cells (i.e., with an office:value-type attribute). But:
+          # jOpenDocument 1.2b3 has a bug: it often doesn't set this attr for string cells
+          id_valtcell = strfind (tablerow, 'office:value-type=');
+          id_textonlycell = strfind (tablerow, '<text:');
+          id_valtcell = sort ([id_valtcell id_textonlycell]);
+          id_valtcell = [id_valtcell rowl];
+          if (~isempty (id_valtcell(1:end-1)))
+            brow = irow + rowrepcnt;
+            # First set trow if it hadn't already been found
+            if (~trow) trow = irow; endif
+            # Search for repeated table-cells
+            id_reptcell = strfind (tablerow, 'number-columns-repeated');
+            id_reptcell = [id_reptcell rowl];
+            # Search for leftmost non-empty table-cell. llcol = counter for this table-row
+            llcol = 1;
+            while (id_tcell (llcol) < id_valtcell(1) && llcol <= length (id_tcell) - 1)
+              ++llcol;
+            endwhile
+            --llcol;
+            # Compensate for repeated cells. First count all repeats left of llcol
+            ii = 1;
+            repcnt = 0;
+            if (~isempty (id_reptcell(1:end-1)))
+              # First try lcol
+              while (ii <= length (id_reptcell) - 1 && id_reptcell(ii) < id_valtcell(1))
+                # Add all repeat counts left of leftmost data tcell minus 1 for each
+                [st, en] = regexp (tablerow(id_reptcell(ii):id_reptcell(ii)+30), '\d+');
+                repcnt += str2num (tablerow(id_reptcell(ii)+st-1:id_reptcell(ii)+en-1)) - 1;
+                ++ii;
+              endwhile
+              # Next, add current repcnt value to llcol and update lcol
+              lcol = min (lcol, llcol + repcnt);
+              # Get last value table-cell in table-cell idx
+              jj = 1;
+              while (id_tcell (jj) < id_valtcell(length (id_valtcell)-1))
+                ++jj;
+              endwhile
 
-    					# Get rest of column repeat counts in value table-cell range
-    					while (ii < length (id_reptcell) && id_reptcell(ii) < id_tcell(jj))
-    						# Add all repeat counts minus 1 for each tcell in value tcell range
-    						[st, en] = regexp (tablerow(id_reptcell(ii):id_reptcell(ii)+30), '\d+');
-    						repcnt += str2num (tablerow(id_reptcell(ii)+st-1:id_reptcell(ii)+en-1)) - 1;
-    						++ii;
-    					endwhile
-    				else
-    					# In case left column = A
-    					lcol = min (lcol, llcol);
-    				endif
-    				# Count all table-cells in value table-cell-range
-    				ii = 1; 			# Indexes cannot be negative
-    				while (ii < length (id_tcell) && id_tcell(ii) < id_valtcell(length (id_valtcell) - 1))
-    					++ii;
-    				endwhile
-    				--ii;
-    				rcol = max (rcol, ii + repcnt);
-    			endif
-    		endif
-    	endfor
+              # Get rest of column repeat counts in value table-cell range
+              while (ii < length (id_reptcell) && id_reptcell(ii) < id_tcell(jj))
+                # Add all repeat counts minus 1 for each tcell in value tcell range
+                [st, en] = regexp (tablerow(id_reptcell(ii):id_reptcell(ii)+30), '\d+');
+                repcnt += str2num (tablerow(id_reptcell(ii)+st-1:id_reptcell(ii)+en-1)) - 1;
+                ++ii;
+              endwhile
+            else
+              # In case left column = A
+              lcol = min (lcol, llcol);
+            endif
+            # Count all table-cells in value table-cell-range
+            ii = 1;       # Indexes cannot be negative
+            while (ii < length (id_tcell) && id_tcell(ii) < id_valtcell(length (id_valtcell) - 1))
+              ++ii;
+            endwhile
+            --ii;
+            rcol = max (rcol, ii + repcnt);
+          endif
+        endif
+      endfor
     else
-    	# No data found, empty sheet
-    	lcol = rcol = brow = trow = 0;
+      # No data found, empty sheet
+      lcol = rcol = brow = trow = 0;
     endif
 
   end_try_catch
@@ -482,30 +481,30 @@
 
 function [ trow, brow, lcol, rcol ] = getusedrange_com (xls, ii)
 
-    sh = xls.workbook.Worksheets (ii);
-    
-    # Decipher used range. Beware, UsedRange() returns *cached* rectangle of
-    # all spreadsheet cells containing *anything*, including just formatting
-    # (i.e., empty cells are included too). ==> This is an approximation only
-    allcells = sh.UsedRange;
-    
-    # Get top left cell as a Range object
-    toplftcl = allcells.Columns(1).Rows(1);
-    
-    # Count number of rows & cols in virtual range from A1 to top left cell
-    lcol = sh.Range ("A1", toplftcl).columns.Count;
-    trow = sh.Range ("A1", toplftcl).rows.Count;
-    
-    # Add real occupied rows & cols to obtain end row & col
-    brow = trow + allcells.rows.Count() - 1;
-    rcol = lcol + allcells.columns.Count() - 1;
-    
-    # Check if there are real data
-    if ((lcol == rcol) && (trow = brow))
-    	if (isempty (toplftcl.Value))
-    		trow = brow = lcol = rcol = 0;
-    	endif
+  sh = xls.workbook.Worksheets (ii);
+  
+  # Decipher used range. Beware, UsedRange() returns *cached* rectangle of
+  # all spreadsheet cells containing *anything*, including just formatting
+  # (i.e., empty cells are included too). ==> This is an approximation only
+  allcells = sh.UsedRange;
+  
+  # Get top left cell as a Range object
+  toplftcl = allcells.Columns(1).Rows(1);
+  
+  # Count number of rows & cols in virtual range from A1 to top left cell
+  lcol = sh.Range ("A1", toplftcl).columns.Count;
+  trow = sh.Range ("A1", toplftcl).rows.Count;
+  
+  # Add real occupied rows & cols to obtain end row & col
+  brow = trow + allcells.rows.Count() - 1;
+  rcol = lcol + allcells.columns.Count() - 1;
+  
+  # Check if there are real data
+  if ((lcol == rcol) && (trow = brow))
+    if (isempty (toplftcl.Value))
+      trow = brow = lcol = rcol = 0;
     endif
+  endif
 
 endfunction
 
@@ -532,35 +531,35 @@
 
 function [ trow, brow, lcol, rcol ] = getusedrange_poi (xls, ii)
 
-    persistent cblnk; cblnk = java_get ('org.apache.poi.ss.usermodel.Cell', 'CELL_TYPE_BLANK');
+  persistent cblnk; cblnk = java_get ('org.apache.poi.ss.usermodel.Cell', 'CELL_TYPE_BLANK');
 
-    sh = xls.workbook.getSheetAt (ii-1);         # Java POI starts counting at 0 
+  sh = xls.workbook.getSheetAt (ii-1);         # Java POI starts counting at 0 
 
-    trow = sh.getFirstRowNum ();                 # 0-based
-    brow = sh.getLastRowNum ();                  # 0-based
-    # Get column range
-    lcol = 1048577;  # OOXML (xlsx) max. + 1
-    rcol = 0;
-    botrow = brow;
-    for jj=trow:brow
-    	irow = sh.getRow (jj);
-    	if (~isempty (irow))
-    		scol = (irow.getFirstCellNum).intValue ();
-    		lcol = min (lcol, scol);
-    		ecol = (irow.getLastCellNum).intValue () - 1;
-    		rcol = max (rcol, ecol);
-    		# Keep track of lowermost non-empty row as getLastRowNum() is unreliable
-    		if ~(irow.getCell(scol).getCellType () == cblnk && irow.getCell(ecol).getCellType () == cblnk)
-    			botrow = jj;
-    		endif
-    	endif
-    endfor
-    if (lcol > 1048576)
-    	# Empty sheet
-    	trow = 0; brow = 0; lcol = 0; rcol = 0;
-    else
-    	brow = min (brow, botrow) + 1; ++trow; ++lcol; ++rcol;    # 1-based return values
+  trow = sh.getFirstRowNum ();                 # 0-based
+  brow = sh.getLastRowNum ();                  # 0-based
+  # Get column range
+  lcol = 1048577;  # OOXML (xlsx) max. + 1
+  rcol = 0;
+  botrow = brow;
+  for jj=trow:brow
+    irow = sh.getRow (jj);
+    if (~isempty (irow))
+      scol = (irow.getFirstCellNum).intValue ();
+      lcol = min (lcol, scol);
+      ecol = (irow.getLastCellNum).intValue () - 1;
+      rcol = max (rcol, ecol);
+      # Keep track of lowermost non-empty row as getLastRowNum() is unreliable
+      if ~(irow.getCell(scol).getCellType () == cblnk && irow.getCell(ecol).getCellType () == cblnk)
+        botrow = jj;
+      endif
     endif
+  endfor
+  if (lcol > 1048576)
+    # Empty sheet
+    trow = 0; brow = 0; lcol = 0; rcol = 0;
+  else
+    brow = min (brow, botrow) + 1; ++trow; ++lcol; ++rcol;    # 1-based return values
+  endif
 
 endfunction
 
@@ -588,33 +587,33 @@
 
 function [ trow, brow, lcol, rcol ] = getusedrange_jxl (xls, wsh)
 
-    persistent emptycell = (java_get ('jxl.CellType', 'EMPTY')).toString ();
+  persistent emptycell = (java_get ('jxl.CellType', 'EMPTY')).toString ();
 
-    sh = xls.workbook.getSheet (wsh - 1);			# JXL sheet count 0-based
+  sh = xls.workbook.getSheet (wsh - 1);      # JXL sheet count 0-based
 
-    brow = sh.getRows ();
-    rcol = sh.getColumns ();
-    
-    if (brow == 0 || rcol == 0)
-    	# Empty sheet
-    	trow = 0; lcol = 0; brow = 0; rcol = 0;
-    else
-    	trow = brow + 1;
-    	lcol = rcol + 1;
-    	for ii=0:brow-1		# For loop coz we must check ALL rows for leftmost column
-    		emptyrow = 1;
-    		jj = 0;
-    		while (jj < rcol && emptyrow)   # While loop => only til first non-empty cell
-    			cell = sh.getCell (jj, ii);
-    			if ~(strcmp (char (cell.getType ()), emptycell))
-    				lcol = min (lcol, jj + 1);
-    				emptyrow = 0;
-    			endif
-    			++jj;
-    		endwhile
-    		if ~(emptyrow) trow = min (trow, ii + 1); endif
-    	endfor
-    endif
+  brow = sh.getRows ();
+  rcol = sh.getColumns ();
+  
+  if (brow == 0 || rcol == 0)
+    # Empty sheet
+    trow = 0; lcol = 0; brow = 0; rcol = 0;
+  else
+    trow = brow + 1;
+    lcol = rcol + 1;
+    for ii=0:brow-1    # For loop coz we must check ALL rows for leftmost column
+      emptyrow = 1;
+      jj = 0;
+      while (jj < rcol && emptyrow)   # While loop => only til first non-empty cell
+        cell = sh.getCell (jj, ii);
+        if ~(strcmp (char (cell.getType ()), emptycell))
+          lcol = min (lcol, jj + 1);
+          emptyrow = 0;
+        endif
+        ++jj;
+      endwhile
+      if ~(emptyrow) trow = min (trow, ii + 1); endif
+    endfor
+  endif
 
 endfunction
 
@@ -643,18 +642,18 @@
 
 function [ trow, brow, lcol, rcol ] = getusedrange_oxs (xls, wsh)
 
-    sh = xls.workbook.getWorkSheet (wsh - 1);
-    try
-    	# Intriguing:  sh.getFirst<> is off by one, sh.getLast<> = OK.... 8-Z 
-    	trow = sh.getFirstRow () + 1;
-    	brow = sh.getLastRow ();
-    	lcol = sh.getFirstCol () + 1;
-    	rcol = sh.getLastCol ();
-    catch
-    	# Might be an empty sheet
-    	trow = brow = lcol = rcol = 0;
-    end_try_catch
-    # Check for empty sheet
-    if ((trow > brow) || (lcol > rcol)), trow = brow = lcol = rcol = 0; endif
+  sh = xls.workbook.getWorkSheet (wsh - 1);
+  try
+    # Intriguing:  sh.getFirst<> is off by one, sh.getLast<> = OK.... 8-Z 
+    trow = sh.getFirstRow () + 1;
+    brow = sh.getLastRow ();
+    lcol = sh.getFirstCol () + 1;
+    rcol = sh.getLastCol ();
+  catch
+    # Might be an empty sheet
+    trow = brow = lcol = rcol = 0;
+  end_try_catch
+  # Check for empty sheet
+  if ((trow > brow) || (lcol > rcol)), trow = brow = lcol = rcol = 0; endif
 
 endfunction
--- a/main/io/inst/oct2ods.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/oct2ods.m	Fri Jun 08 15:28:27 2012 +0000
@@ -115,98 +115,96 @@
 ## 2012-02-20 Fixed range parameter to be default empty string rather than empty numeral
 ## 2012-02-27 More range arg fixes
 ## 2012-03-07 Updated texinfo help text
-## 2012-05-22 Cast all numeric data in input array to double
+## 2012-06-08 Support for odfdom-incubator-0.8.8
+##     ''     Tabs replaced by double space
 ##
-## Last update of subfunctions below: 2012-02-26
+## Last update of subfunctions below: 2012-06-08
 
 function [ ods, rstatus ] = oct2ods (c_arr, ods, wsh=1, crange='', spsh_opts=[])
 
-	if (nargin < 2) error ("oct2xls needs a minimum of 2 arguments."); endif
+  if (nargin < 2) error ("oct2xls needs a minimum of 2 arguments."); endif
   
-	# Check if input array is cell
-	if (isempty (c_arr))
-		warning ("Request to write empty matrix - ignored."); 
-		rstatus = 1;
-		return;
-	elseif (isnumeric (c_arr))
-		c_arr = num2cell (c_arr);
-	elseif (ischar(c_arr))
-		c_arr = {c_arr};
-		printf ("(oct2ods: input character array converted to 1x1 cell)\n");
-	elseif (~iscell (c_arr))
-		error ("oct2ods: input array neither cell nor numeric array");
-	endif
-	if (ndims (c_arr) > 2), error ("Only 2-dimensional arrays can be written to spreadsheet"); endif
-  # Cast all numerical values to double as spreadsheets only have double/boolean/text type
-  idx = cellfun (@isnumeric, obj, "UniformOutput", true);
-  obj(idx) = cellfun (@double, obj(idx), "UniformOutput", false);
+  # Check if input array is cell
+  if (isempty (c_arr))
+    warning ("Request to write empty matrix - ignored."); 
+    rstatus = 1;
+    return;
+  elseif (isnumeric (c_arr))
+    c_arr = num2cell (c_arr);
+  elseif (ischar(c_arr))
+    c_arr = {c_arr};
+    printf ("(oct2ods: input character array converted to 1x1 cell)\n");
+  elseif (~iscell (c_arr))
+    error ("oct2ods: input array neither cell nor numeric array");
+  endif
+  if (ndims (c_arr) > 2), error ("Only 2-dimensional arrays can be written to spreadsheet"); endif
 
   # Check ods file pointer struct
-	test1 = ~isfield (ods, "xtype");
-	test1 = test1 || ~isfield (ods, "workbook");
-	test1 = test1 || isempty (ods.workbook);
-	test1 = test1 || isempty (ods.app);
-	if test1
-		error ("Arg #2: Invalid ods file pointer struct");
-	endif
+  test1 = ~isfield (ods, "xtype");
+  test1 = test1 || ~isfield (ods, "workbook");
+  test1 = test1 || isempty (ods.workbook);
+  test1 = test1 || isempty (ods.app);
+  if test1
+    error ("Arg #2: Invalid ods file pointer struct");
+  endif
 
-	# Check worksheet ptr
-	if (~(ischar (wsh) || isnumeric (wsh))), error ("Integer (index) or text (wsh name) expected for arg # 3"); endif
+  # Check worksheet ptr
+  if (~(ischar (wsh) || isnumeric (wsh))), error ("Integer (index) or text (wsh name) expected for arg # 3"); endif
 
-	# Check range
-	if (~isempty (crange) && ~ischar (crange))
+  # Check range
+  if (~isempty (crange) && ~ischar (crange))
     error ("Character string (range) expected for arg # 4");
   elseif (isempty (crange))
     crange = '';
   endif
 
-	# Various options 
-	if (isempty (spsh_opts))
-		spsh_opts.formulas_as_text = 0;
-		# other options to be implemented here
-	elseif (isstruct (spsh_opts))
-		if (~isfield (spsh_opts, 'formulas_as_text')), spsh_opts.formulas_as_text = 0; endif
-		# other options to be implemented here
-	else
-		error ("Structure expected for arg # 5");
-	endif
-	
-	if (nargout < 1) printf ("Warning: no output spreadsheet file pointer specified.\n"); endif
+  # Various options 
+  if (isempty (spsh_opts))
+    spsh_opts.formulas_as_text = 0;
+    # other options to be implemented here
+  elseif (isstruct (spsh_opts))
+    if (~isfield (spsh_opts, 'formulas_as_text')), spsh_opts.formulas_as_text = 0; endif
+    # other options to be implemented here
+  else
+    error ("Structure expected for arg # 5");
+  endif
+  
+  if (nargout < 1) printf ("Warning: no output spreadsheet file pointer specified.\n"); endif
 
-	if (strcmp (ods.xtype, 'OTK'))
-		# Write ods file tru Java & ODF toolkit.
-		switch ods.odfvsn
-			case "0.7.5"
-				[ ods, rstatus ] = oct2jotk2ods (c_arr, ods, wsh, crange, spsh_opts);
-			case {"0.8.6", "0.8.7"}
-				[ ods, rstatus ] = oct3jotk2ods (c_arr, ods, wsh, crange, spsh_opts);
-			otherwise
-				error ("Unsupported odfdom version");
-		endswitch
+  if (strcmp (ods.xtype, 'OTK'))
+    # Write ods file tru Java & ODF toolkit.
+    switch ods.odfvsn
+      case "0.7.5"
+        [ ods, rstatus ] = oct2jotk2ods (c_arr, ods, wsh, crange, spsh_opts);
+      case {"0.8.6", "0.8.7", "0.8.8"}
+        [ ods, rstatus ] = oct3jotk2ods (c_arr, ods, wsh, crange, spsh_opts);
+      otherwise
+        error ("Unsupported odfdom version");
+    endswitch
 
-	elseif (strcmp (ods.xtype, 'JOD'))
-		# Write ods file tru Java & jOpenDocument. API still leaves lots to be wished...
-		[ ods, rstatus ] = oct2jod2ods (c_arr, ods, wsh, crange);
+  elseif (strcmp (ods.xtype, 'JOD'))
+    # Write ods file tru Java & jOpenDocument. API still leaves lots to be wished...
+    [ ods, rstatus ] = oct2jod2ods (c_arr, ods, wsh, crange);
 
-	elseif (strcmp (ods.xtype, 'UNO'))
-		# Write ods file tru Java & UNO bridge (OpenOffice.org & clones)
-		[ ods, rstatus ] = oct2uno2ods (c_arr, ods, wsh, crange, spsh_opts);
+  elseif (strcmp (ods.xtype, 'UNO'))
+    # Write ods file tru Java & UNO bridge (OpenOffice.org & clones)
+    [ ods, rstatus ] = oct2uno2ods (c_arr, ods, wsh, crange, spsh_opts);
 
-#	elseif 
-		# ---- < Other interfaces here >
+#  elseif 
+    # ---- < Other interfaces here >
 
-	else
-		error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype));
-	endif
+  else
+    error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype));
+  endif
 
-	if (rstatus), ods.limits = []; endif
+  if (rstatus), ods.limits = []; endif
 
 endfunction
 
 
 #=============================================================================
 
-## Copyright (C) 2010,2011 Philip Nienhuis <prnienhuis@users.sf.net>
+## Copyright (C) 2010,2011,2012 Philip Nienhuis <prnienhuis@users.sf.net>
 ##
 ## 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
@@ -253,401 +251,401 @@
 
 function [ ods, rstatus ] = oct2jotk2ods (c_arr, ods, wsh, crange, spsh_opts)
 
-	persistent ctype;
-	if (isempty (ctype))
-		# Number, Boolean, String, Formula, Empty, Date, Time (last 2 are ignored)
-		ctype = [1, 2, 3, 4, 5, 6, 7];
-	endif
+  persistent ctype;
+  if (isempty (ctype))
+    # Number, Boolean, String, Formula, Empty, Date, Time (last 2 are ignored)
+    ctype = [1, 2, 3, 4, 5, 6, 7];
+  endif
 
-	rstatus = 0; f_errs = 0;
+  rstatus = 0; f_errs = 0;
 
-	# Get some basic spreadsheet data from the pointer using ODFtoolkit
-	odfcont = ods.workbook;
-	xpath = ods.app.getXPath ();
-	offsprdsh = ods.app.getContentRoot();
-	autostyles = odfcont.getOrCreateAutomaticStyles();
-	officestyles = ods.app.getOrCreateDocumentStyles();
+  # Get some basic spreadsheet data from the pointer using ODFtoolkit
+  odfcont = ods.workbook;
+  xpath = ods.app.getXPath ();
+  offsprdsh = ods.app.getContentRoot();
+  autostyles = odfcont.getOrCreateAutomaticStyles();
+  officestyles = ods.app.getOrCreateDocumentStyles();
 
-	# Create an instance of type NODESET for use in subsequent statements
-	NODESET = java_get ('javax.xml.xpath.XPathConstants', 'NODESET');
+  # Create an instance of type NODESET for use in subsequent statements
+  NODESET = java_get ('javax.xml.xpath.XPathConstants', 'NODESET');
 
-	# Parse sheets ("tables") from ODS file
-	sheets = xpath.evaluate ("//table:table", odfcont, NODESET);
-	nr_of_sheets = sheets.getLength ();
-	newsh = 0;								# Assume existing sheet
-	if isempty (wsh) wsh = 1; endif
-	if (~isnumeric (wsh))					# Sheet name specified
-		# Search in sheet names, match sheet name to sheet number.
-		# Beware, 0-based index, 1-based count!
-		ii = 0;
-		while (++ii <= nr_of_sheets && ischar (wsh))	
-			# Look in first part of the sheet nodeset
-			sh_name = sheets.item(ii-1).getTableNameAttribute ();
-			if (strcmp (sh_name, wsh))
-				# Convert local copy of wsh into a number (pointer)
-				wsh = ii - 1;
-			endif
-		endwhile
-		if (ischar (wsh) && nr_of_sheets < 256) newsh = 1; endif
-	else										# Sheet index specified
-		if ((ods.changed > 2) || (wsh > nr_of_sheets && wsh < 256))	# Max nr of sheets = 256
-			# Create a new sheet
-			newsh = 1;
-		elseif (wsh <=nr_of_sheets && wsh > 0)
-			# Existing sheet. Count = 1-based, index = 0-based
-			--wsh; sh = sheets.item(wsh);
-			printf ("Writing to sheet %s\n", sh.getTableNameAttribute());
-		else
-			error ("oct2ods: illegal sheet number.");
-		endif
-	endif
+  # Parse sheets ("tables") from ODS file
+  sheets = xpath.evaluate ("//table:table", odfcont, NODESET);
+  nr_of_sheets = sheets.getLength ();
+  newsh = 0;                # Assume existing sheet
+  if isempty (wsh) wsh = 1; endif
+  if (~isnumeric (wsh))          # Sheet name specified
+    # Search in sheet names, match sheet name to sheet number.
+    # Beware, 0-based index, 1-based count!
+    ii = 0;
+    while (++ii <= nr_of_sheets && ischar (wsh))  
+      # Look in first part of the sheet nodeset
+      sh_name = sheets.item(ii-1).getTableNameAttribute ();
+      if (strcmp (sh_name, wsh))
+        # Convert local copy of wsh into a number (pointer)
+        wsh = ii - 1;
+      endif
+    endwhile
+    if (ischar (wsh) && nr_of_sheets < 256) newsh = 1; endif
+  else                    # Sheet index specified
+    if ((ods.changed > 2) || (wsh > nr_of_sheets && wsh < 256))  # Max nr of sheets = 256
+      # Create a new sheet
+      newsh = 1;
+    elseif (wsh <=nr_of_sheets && wsh > 0)
+      # Existing sheet. Count = 1-based, index = 0-based
+      --wsh; sh = sheets.item(wsh);
+      printf ("Writing to sheet %s\n", sh.getTableNameAttribute());
+    else
+      error ("oct2ods: illegal sheet number.");
+    endif
+  endif
 
 # Check size of data array & range / capacity of worksheet & prepare vars
-	[nr, nc] = size (c_arr);
-	[topleft, nrows, ncols, trow, lcol] = spsh_chkrange (crange, nr, nc, ods.xtype, ods.filename);
-	--trow; --lcol;									# Zero-based row # & col #
-	if (nrows < nr || ncols < nc)
-		warning ("Array truncated to fit in range");
-		c_arr = c_arr(1:nrows, 1:ncols);
-	endif
-	
+  [nr, nc] = size (c_arr);
+  [topleft, nrows, ncols, trow, lcol] = spsh_chkrange (crange, nr, nc, ods.xtype, ods.filename);
+  --trow; --lcol;                  # Zero-based row # & col #
+  if (nrows < nr || ncols < nc)
+    warning ("Array truncated to fit in range");
+    c_arr = c_arr(1:nrows, 1:ncols);
+  endif
+  
 # Parse data array, setup typarr and throw out NaNs  to speed up writing;
-	typearr = spsh_prstype (c_arr, nrows, ncols, ctype, spsh_opts, 0);
-	if ~(spsh_opts.formulas_as_text)
-		# Find formulas (designated by a string starting with "=" and ending in ")")
-		fptr = cellfun (@(x) ischar (x) && strncmp (x, "=", 1) && strncmp (x(end:end), ")", 1), c_arr);
-		typearr(fptr) = ctype(4);					# FORMULA
-	endif
+  typearr = spsh_prstype (c_arr, nrows, ncols, ctype, spsh_opts, 0);
+  if ~(spsh_opts.formulas_as_text)
+    # Find formulas (designated by a string starting with "=" and ending in ")")
+    fptr = cellfun (@(x) ischar (x) && strncmp (x, "=", 1) && strncmp (x(end:end), ")", 1), c_arr);
+    typearr(fptr) = ctype(4);          # FORMULA
+  endif
 
 # Prepare worksheet for writing. If needed create new sheet
-	if (newsh)
-		if (ods.changed > 2)
-			# New spreadsheet. Prepare to use the default 1x1 first sheet.
-			sh = sheets.item(0);
-		else
-			# Other sheets exist, create a new sheet. First the basics
-			sh = java_new ('org.odftoolkit.odfdom.doc.table.OdfTable', odfcont);
-			# Append sheet to spreadsheet ( contentRoot)
-			offsprdsh.appendChild (sh);
-			# Rebuild sheets nodes
-			sheets = xpath.evaluate ("//table:table", odfcont, NODESET);
-		endif 
+  if (newsh)
+    if (ods.changed > 2)
+      # New spreadsheet. Prepare to use the default 1x1 first sheet.
+      sh = sheets.item(0);
+    else
+      # Other sheets exist, create a new sheet. First the basics
+      sh = java_new ('org.odftoolkit.odfdom.doc.table.OdfTable', odfcont);
+      # Append sheet to spreadsheet ( contentRoot)
+      offsprdsh.appendChild (sh);
+      # Rebuild sheets nodes
+      sheets = xpath.evaluate ("//table:table", odfcont, NODESET);
+    endif 
 
-		# Sheet name
-		if (isnumeric (wsh))
-			# Give sheet a name
-			str = sprintf ("Sheet%d", wsh);
-			sh.setTableNameAttribute (str);
-		else
-			# Assign name to sheet and change wsh into numeric pointer
-			sh.setTableNameAttribute (wsh);
-			wsh = sheets.getLength () - 1;
-		endif
-		# Fixup wsh pointer in case of new spreadsheet
-		if (ods.changed > 2) wsh = 0; endif
+    # Sheet name
+    if (isnumeric (wsh))
+      # Give sheet a name
+      str = sprintf ("Sheet%d", wsh);
+      sh.setTableNameAttribute (str);
+    else
+      # Assign name to sheet and change wsh into numeric pointer
+      sh.setTableNameAttribute (wsh);
+      wsh = sheets.getLength () - 1;
+    endif
+    # Fixup wsh pointer in case of new spreadsheet
+    if (ods.changed > 2) wsh = 0; endif
 
-		# Add table-column entry for style etc
-		col = sh.addTableColumn ();
-		col.setTableDefaultCellStyleNameAttribute ("Default");
-		col.setTableNumberColumnsRepeatedAttribute (lcol + ncols + 1);
-		col.setTableStyleNameAttribute ("co1");
+    # Add table-column entry for style etc
+    col = sh.addTableColumn ();
+    col.setTableDefaultCellStyleNameAttribute ("Default");
+    col.setTableNumberColumnsRepeatedAttribute (lcol + ncols + 1);
+    col.setTableStyleNameAttribute ("co1");
 
-	# Build up the complete row & cell structure to cover the data array.
-	# This will speed up processing later
+  # Build up the complete row & cell structure to cover the data array.
+  # This will speed up processing later
 
-		# 1. Build empty table row template
-		row = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
-		# Create an empty tablecell & append it to the row
-		scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-		scell = row.appendCell (scell);
-		scell.setTableNumberColumnsRepeatedAttribute (1024);
-		# 2. If needed add empty filler row above the data rows & if needed add repeat count
-		if (trow > 0)				
-			sh.appendRow (row);
-			if (trow > 1) row.setTableNumberRowsRepeatedAttribute (trow); endif
-		endif
-		# 3. Add data rows; first one serves as a template
-		drow = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
-		if (lcol > 0) 
-			scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-			drow.appendCell (scell);
-			if (lcol > 1) scell.setTableNumberColumnsRepeatedAttribute (lcol); endif
-		endif
-		# 4. Add data cell placeholders
-		scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-		drow.appendCell (scell);
-		for jj=2:ncols
-			dcell = scell.cloneNode (1);		# Deep copy
-			drow.appendCell (dcell);
-		endfor
-		# 5. Last cell is remaining column counter
-		rest = max (1024 - lcol - ncols);
-		if (rest)
-			dcell = scell.cloneNode (1);		# Deep copy
-			drow.appendCell (dcell);
-			if (rest > 1) dcell.setTableNumberColumnsRepeatedAttribute (rest); endif
-		endif
-		# Only now add drow as otherwise for each cell an empty table-column is
-		# inserted above the rows (odftoolkit bug?)
-		sh.appendRow (drow);
-		if (ods.changed > 2)
-			# In case of a completely new spreadsheet, delete the first initial 1-cell row
-			# But check if it *is* a row...
-			try
-				sh.removeChild (drow.getPreviousRow ());
-			catch
-				# Nothing. Apparently there was only the just appended row.
-			end_try_catch
-		endif
-		# 6. Row template ready. Copy row template down to cover future array
-		for ii=2:nrows
-			nrow = drow.cloneNode (1);	# Deep copy
-			sh.appendRow (nrow);
-		endfor
-		ods.changed = min (ods.changed, 2);		# Keep 2 for new spshsht, 1 for existing + changed
+    # 1. Build empty table row template
+    row = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
+    # Create an empty tablecell & append it to the row
+    scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+    scell = row.appendCell (scell);
+    scell.setTableNumberColumnsRepeatedAttribute (1024);
+    # 2. If needed add empty filler row above the data rows & if needed add repeat count
+    if (trow > 0)        
+      sh.appendRow (row);
+      if (trow > 1) row.setTableNumberRowsRepeatedAttribute (trow); endif
+    endif
+    # 3. Add data rows; first one serves as a template
+    drow = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
+    if (lcol > 0) 
+      scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+      drow.appendCell (scell);
+      if (lcol > 1) scell.setTableNumberColumnsRepeatedAttribute (lcol); endif
+    endif
+    # 4. Add data cell placeholders
+    scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+    drow.appendCell (scell);
+    for jj=2:ncols
+      dcell = scell.cloneNode (1);    # Deep copy
+      drow.appendCell (dcell);
+    endfor
+    # 5. Last cell is remaining column counter
+    rest = max (1024 - lcol - ncols);
+    if (rest)
+      dcell = scell.cloneNode (1);    # Deep copy
+      drow.appendCell (dcell);
+      if (rest > 1) dcell.setTableNumberColumnsRepeatedAttribute (rest); endif
+    endif
+    # Only now add drow as otherwise for each cell an empty table-column is
+    # inserted above the rows (odftoolkit bug?)
+    sh.appendRow (drow);
+    if (ods.changed > 2)
+      # In case of a completely new spreadsheet, delete the first initial 1-cell row
+      # But check if it *is* a row...
+      try
+        sh.removeChild (drow.getPreviousRow ());
+      catch
+        # Nothing. Apparently there was only the just appended row.
+      end_try_catch
+    endif
+    # 6. Row template ready. Copy row template down to cover future array
+    for ii=2:nrows
+      nrow = drow.cloneNode (1);  # Deep copy
+      sh.appendRow (nrow);
+    endfor
+    ods.changed = min (ods.changed, 2);    # Keep 2 for new spshsht, 1 for existing + changed
 
-	else
-		# Existing sheet. We must be prepared for all situations, incomplete rows,
-		# number-rows/columns-repeated, merged (spanning) cells, you name it.
-		# First explore row buildup of existing sheet using an XPath
-		sh = sheets.item(wsh);											# 0 - based
-		str = sprintf ("//table:table[%d]/table:table-row", wsh + 1);	# 1 - based 
-		trows = xpath.evaluate (str, odfcont, NODESET);
-		nr_of_trows = trows.getLength(); 	# Nr. of existing table-rows, not data rows!
+  else
+    # Existing sheet. We must be prepared for all situations, incomplete rows,
+    # number-rows/columns-repeated, merged (spanning) cells, you name it.
+    # First explore row buildup of existing sheet using an XPath
+    sh = sheets.item(wsh);                      # 0 - based
+    str = sprintf ("//table:table[%d]/table:table-row", wsh + 1);  # 1 - based 
+    trows = xpath.evaluate (str, odfcont, NODESET);
+    nr_of_trows = trows.getLength();   # Nr. of existing table-rows, not data rows!
 
-		# For the first rows we do some preprocessing here. Similar stuff for cells
-		# i.e. table-cells (columns) is done in the loops below.
-		# Make sure the upper data array row doesn't end up in a nr-rows-repeated row
+    # For the first rows we do some preprocessing here. Similar stuff for cells
+    # i.e. table-cells (columns) is done in the loops below.
+    # Make sure the upper data array row doesn't end up in a nr-rows-repeated row
 
-		# Provisionally! set start table-row in case "while" & "if" (split) are skipped
-		drow = trows.item(0);	
-		rowcnt = 0; trowcnt = 0;					# Spreadsheet/ table-rows, resp;
-		while (rowcnt < trow && trowcnt < nr_of_trows)
-			# Count rows & table-rows UNTIL we reach trow
-			++trowcnt;								# Nr of table-rows
-			row = drow;
-			drow = row.getNextSibling ();
-			repcnt = row.getTableNumberRowsRepeatedAttribute();
-			rowcnt = rowcnt + repcnt;				# Nr of spreadsheet rows
-		endwhile
-		rsplit = rowcnt - trow;
-		if (rsplit > 0)
-			# Apparently a nr-rows-repeated top table-row must be split, as the
-			# first data row seems to be projected in it (1st while condition above!)
-			row.removeAttribute ('table:number-rows-repeated');
-			row.getCellAt (0).removeAttribute ('table:number-columns-repeated');
-			nrow = row.cloneNode (1);
-			drow = nrow;							# Future upper data array row
-			if (repcnt > 1)
-				row.setTableNumberRowsRepeatedAttribute (repcnt - rsplit);
-			else
-				row.removeAttribute ('table:number-rows-repeated');
-			endif
-			rrow = row.getNextSibling ();
-			sh.insertBefore (nrow, rrow);
-			for jj=2:rsplit
-				nrow = nrow.cloneNode (1);
-				sh.insertBefore (nrow, rrow);
-			endfor
-		elseif (rsplit < 0)
-			# New data rows to be added below existing data & table(!) rows, i.e.
-			# beyond lower end of the current sheet. Add filler row and 1st data row
-			row = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
-			drow = row.cloneNode (1);								# First data row
-			row.setTableNumberRowsRepeatedAttribute (-rsplit);		# Filler row
-			scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-			dcell = scell.cloneNode (1);
-			scell.setTableNumberColumnsRepeatedAttribute (COL_CAP);	# Filler cell
-			row.appendCell (scell);
-			sh.appendRow (row);
-			drow.appendCell (dcell);
-			sh.appendRow (drow);
-		endif
-	endif
+    # Provisionally! set start table-row in case "while" & "if" (split) are skipped
+    drow = trows.item(0);  
+    rowcnt = 0; trowcnt = 0;          # Spreadsheet/ table-rows, resp;
+    while (rowcnt < trow && trowcnt < nr_of_trows)
+      # Count rows & table-rows UNTIL we reach trow
+      ++trowcnt;                # Nr of table-rows
+      row = drow;
+      drow = row.getNextSibling ();
+      repcnt = row.getTableNumberRowsRepeatedAttribute();
+      rowcnt = rowcnt + repcnt;        # Nr of spreadsheet rows
+    endwhile
+    rsplit = rowcnt - trow;
+    if (rsplit > 0)
+      # Apparently a nr-rows-repeated top table-row must be split, as the
+      # first data row seems to be projected in it (1st while condition above!)
+      row.removeAttribute ('table:number-rows-repeated');
+      row.getCellAt (0).removeAttribute ('table:number-columns-repeated');
+      nrow = row.cloneNode (1);
+      drow = nrow;              # Future upper data array row
+      if (repcnt > 1)
+        row.setTableNumberRowsRepeatedAttribute (repcnt - rsplit);
+      else
+        row.removeAttribute ('table:number-rows-repeated');
+      endif
+      rrow = row.getNextSibling ();
+      sh.insertBefore (nrow, rrow);
+      for jj=2:rsplit
+        nrow = nrow.cloneNode (1);
+        sh.insertBefore (nrow, rrow);
+      endfor
+    elseif (rsplit < 0)
+      # New data rows to be added below existing data & table(!) rows, i.e.
+      # beyond lower end of the current sheet. Add filler row and 1st data row
+      row = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
+      drow = row.cloneNode (1);                # First data row
+      row.setTableNumberRowsRepeatedAttribute (-rsplit);    # Filler row
+      scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+      dcell = scell.cloneNode (1);
+      scell.setTableNumberColumnsRepeatedAttribute (COL_CAP);  # Filler cell
+      row.appendCell (scell);
+      sh.appendRow (row);
+      drow.appendCell (dcell);
+      sh.appendRow (drow);
+    endif
+  endif
 
 # For each row, for each cell, add the data. Expand row/column-repeated nodes
 
-	row = drow;			# Start row; pointer still exists from above stanzas
-	for ii=1:nrows
-		if (~newsh)		# Only for existing sheets the next checks should be made
-			# While processing next data rows, fix table-rows if needed
-			if (isempty (row) || (row.getLength () < 1))
-				# Append an empty row with just one empty cell
-				row = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
-				scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-				scell.setTableNumberColumnsRepeatedAttribute (lcol + 1);
-				row.appendCell (scell);
-				sh.appendRow (row);
-			else
-				# If needed expand nr-rows-repeated
-				repcnt = row.getTableNumberRowsRepeatedAttribute ();
-				if (repcnt > 1)
-					row.removeAttribute ('table:number-rows-repeated');
-					# Insert new table-rows above row until our new data space is complete.
-					# Keep handle of upper new table-row as that's where data are added 1st
-					drow = row.cloneNode (1);
-					sh.insertBefore (drow, row);
-					for kk=1:min (repcnt, nrows-ii)
-						nrow = row.cloneNode (1);
-						sh.insertBefore (nrow, row);
-					endfor
-					if (repcnt > nrows-ii+1)
-						row.setTableNumberRowsRepeatedAttribute (repcnt - nrows +ii - 1);
-					endif
-					row = drow;
-				endif
-			endif
+  row = drow;      # Start row; pointer still exists from above stanzas
+  for ii=1:nrows
+    if (~newsh)    # Only for existing sheets the next checks should be made
+      # While processing next data rows, fix table-rows if needed
+      if (isempty (row) || (row.getLength () < 1))
+        # Append an empty row with just one empty cell
+        row = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableRow', odfcont);
+        scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+        scell.setTableNumberColumnsRepeatedAttribute (lcol + 1);
+        row.appendCell (scell);
+        sh.appendRow (row);
+      else
+        # If needed expand nr-rows-repeated
+        repcnt = row.getTableNumberRowsRepeatedAttribute ();
+        if (repcnt > 1)
+          row.removeAttribute ('table:number-rows-repeated');
+          # Insert new table-rows above row until our new data space is complete.
+          # Keep handle of upper new table-row as that's where data are added 1st
+          drow = row.cloneNode (1);
+          sh.insertBefore (drow, row);
+          for kk=1:min (repcnt, nrows-ii)
+            nrow = row.cloneNode (1);
+            sh.insertBefore (nrow, row);
+          endfor
+          if (repcnt > nrows-ii+1)
+            row.setTableNumberRowsRepeatedAttribute (repcnt - nrows +ii - 1);
+          endif
+          row = drow;
+        endif
+      endif
 
-			# Check if leftmost cell ends up in nr-cols-repeated cell
-			colcnt = 0; tcellcnt = 0; rcellcnt = row.getLength();
-			dcell = row.getCellAt (0);
-			while (colcnt < lcol && tcellcnt < rcellcnt)
-				# Count columns UNTIL we hit lcol
-				++tcellcnt;						# Nr of table-cells counted
-				scell = dcell;
-				dcell = scell.getNextSibling ();
-				repcnt = scell.getTableNumberColumnsRepeatedAttribute ();
-				colcnt = colcnt + repcnt;		# Nr of spreadsheet cell counted
-			endwhile
-			csplit = colcnt - lcol;
-			if (csplit > 0)
-				# Apparently a nr-columns-repeated cell must be split
-				scell.removeAttribute ('table:number-columns-repeated');
-				ncell = scell.cloneNode (1);
-				if (repcnt > 1)
-					scell.setTableNumberColumnsRepeatedAttribute (repcnt - csplit);
-				else
-					scell.removeAttribute ('table:number-columns-repeated');
-				endif
-				rcell = scell.getNextSibling ();
-				row.insertBefore (ncell, rcell);
-				for jj=2:csplit
-					ncell = ncell.cloneNode (1);
-					row.insertBefore (ncell, rcell);
-				endfor
-			elseif (csplit < 0)
-				# New cells to be added beyond current last cell & table-cell in row
-				dcell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-				scell = dcell.cloneNode (1);
-				dcell.setTableNumberColumnsRepeatedAttribute (-csplit);
-				row.appendCell (dcell);
-				row.appendCell (scell);
-			endif
-		endif
+      # Check if leftmost cell ends up in nr-cols-repeated cell
+      colcnt = 0; tcellcnt = 0; rcellcnt = row.getLength();
+      dcell = row.getCellAt (0);
+      while (colcnt < lcol && tcellcnt < rcellcnt)
+        # Count columns UNTIL we hit lcol
+        ++tcellcnt;            # Nr of table-cells counted
+        scell = dcell;
+        dcell = scell.getNextSibling ();
+        repcnt = scell.getTableNumberColumnsRepeatedAttribute ();
+        colcnt = colcnt + repcnt;    # Nr of spreadsheet cell counted
+      endwhile
+      csplit = colcnt - lcol;
+      if (csplit > 0)
+        # Apparently a nr-columns-repeated cell must be split
+        scell.removeAttribute ('table:number-columns-repeated');
+        ncell = scell.cloneNode (1);
+        if (repcnt > 1)
+          scell.setTableNumberColumnsRepeatedAttribute (repcnt - csplit);
+        else
+          scell.removeAttribute ('table:number-columns-repeated');
+        endif
+        rcell = scell.getNextSibling ();
+        row.insertBefore (ncell, rcell);
+        for jj=2:csplit
+          ncell = ncell.cloneNode (1);
+          row.insertBefore (ncell, rcell);
+        endfor
+      elseif (csplit < 0)
+        # New cells to be added beyond current last cell & table-cell in row
+        dcell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+        scell = dcell.cloneNode (1);
+        dcell.setTableNumberColumnsRepeatedAttribute (-csplit);
+        row.appendCell (dcell);
+        row.appendCell (scell);
+      endif
+    endif
 
-	# Write a row of data from data array, column by column
-	
-		for jj=1:ncols
-			scell = row.getCellAt (lcol + jj - 1);
-			if (~newsh)
-				if (isempty (scell))
-					# Apparently end of row encountered. Add cell
-					scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
-					scell = row.appendCell (scell);
-				else
-					# If needed expand nr-cols-repeated
-					repcnt = scell.getTableNumberColumnsRepeatedAttribute ();
-					if (repcnt > 1)
-						scell.removeAttribute ('table:number-columns-repeated');
-						for kk=2:repcnt
-							ncell = scell.cloneNode (1);
-							row.insertBefore (ncell, scell.getNextSibling ());
-						endfor
-					endif
-				endif
-				# Clear text contents
-				while (scell.hasChildNodes ())
-					tmp = scell.getFirstChild ();
-					scell.removeChild (tmp);
-				endwhile
-				scell.removeAttribute ('table:formula');
-			endif
+  # Write a row of data from data array, column by column
+  
+    for jj=1:ncols
+      scell = row.getCellAt (lcol + jj - 1);
+      if (~newsh)
+        if (isempty (scell))
+          # Apparently end of row encountered. Add cell
+          scell = java_new ('org.odftoolkit.odfdom.doc.table.OdfTableCell', odfcont);
+          scell = row.appendCell (scell);
+        else
+          # If needed expand nr-cols-repeated
+          repcnt = scell.getTableNumberColumnsRepeatedAttribute ();
+          if (repcnt > 1)
+            scell.removeAttribute ('table:number-columns-repeated');
+            for kk=2:repcnt
+              ncell = scell.cloneNode (1);
+              row.insertBefore (ncell, scell.getNextSibling ());
+            endfor
+          endif
+        endif
+        # Clear text contents
+        while (scell.hasChildNodes ())
+          tmp = scell.getFirstChild ();
+          scell.removeChild (tmp);
+        endwhile
+        scell.removeAttribute ('table:formula');
+      endif
 
-			# Empty cell count stuff done. At last we can add the data
-			switch (typearr (ii, jj))
-				case 1	# float
-					scell.setOfficeValueTypeAttribute ('float');
-					scell.setOfficeValueAttribute (c_arr{ii, jj});
-				case 2		# boolean
-					# Beware, for unpatched-for-booleans java-1.2.7- we must resort to floats
-					try
-						# First try the preferred java-boolean way
-						scell.setOfficeValueTypeAttribute ('boolean');
-						scell.removeAttribute ('office:value');
-						if (c_arr{ii, jj})
-							scell.setOfficeBooleanValueAttribute (1);
-						else
-							scell.setOfficeBooleanValueAttribute (0);
-						endif
-					catch
-						# Unpatched java package. Fall back to transferring a float
-						scell.setOfficeValueTypeAttribute ('float');
-						if (c_arr{ii, jj})
-							scell.setOfficeValueAttribute (1);
-						else
-							scell.setOfficeValueAttribute (0);
-						endif
-					end_try_catch
-				case 3	# string
-					scell.setOfficeValueTypeAttribute ('string');
-					pe = java_new ('org.odftoolkit.odfdom.doc.text.OdfTextParagraph', odfcont,'', c_arr{ii, jj});
-					scell.appendChild (pe);
-				case 4  # Formula.  
-					# As we don't know the result type, simply remove previous type info.
-					# Once OOo Calc reads it, it'll add the missing attributes
-					scell.removeAttribute ('office:value');
-					scell.removeAttribute ('office:value-type');
-					# Try-catch not strictly needed, there's no formula validator yet
-					try
-						scell.setTableFormulaAttribute (c_arr{ii, jj});
-						scell.setOfficeValueTypeAttribute ('string');
-						pe = java_new ('org.odftoolkit.odfdom.doc.text.OdfTextParagraph', odfcont,'', '#Recalc Formula#');
-						scell.appendChild (pe);
-					catch
-						++f_errs;
-						scell.setOfficeValueTypeAttribute ('string');
-						pe = java_new ('org.odftoolkit.odfdom.doc.text.OdfTextParagraph', odfcont,'', c_arr{ii, jj});
-						scell.appendChild (pe);
-					end_try_catch
-				case {0 5}	# Empty. Clear value attributes
-					if (~newsh)
-						scell.removeAttribute ('office:value-type');
-						scell.removeAttribute ('office:value');
-					endif
-				case 6	# Date (implemented but Octave has no "date" data type - yet?)
-					scell.setOfficeValueTypeAttribute ('date');
-					[hh mo dd hh mi ss] = datevec (c_arr{ii,jj});
-					str = sprintf ("%4d-%2d-%2dT%2d:%2d:%2d", yy, mo, dd, hh, mi, ss);
-					scell.setOfficeDateValueAttribute (str);
-				case 7	# Time (implemented but Octave has no "time" data type)
-					scell.setOfficeValueTypeAttribute ('time');
-					[hh mo dd hh mi ss] = datevec (c_arr{ii,jj});
-					str = sprintf ("PT%2d:%2d:%2d", hh, mi, ss);
-					scell.setOfficeTimeValuettribute (str);
-				otherwise
-					# Nothing
-			endswitch
+      # Empty cell count stuff done. At last we can add the data
+      switch (typearr (ii, jj))
+        case 1  # float
+          scell.setOfficeValueTypeAttribute ('float');
+          scell.setOfficeValueAttribute (c_arr{ii, jj});
+        case 2    # boolean
+          # Beware, for unpatched-for-booleans java-1.2.7- we must resort to floats
+          try
+            # First try the preferred java-boolean way
+            scell.setOfficeValueTypeAttribute ('boolean');
+            scell.removeAttribute ('office:value');
+            if (c_arr{ii, jj})
+              scell.setOfficeBooleanValueAttribute (1);
+            else
+              scell.setOfficeBooleanValueAttribute (0);
+            endif
+          catch
+            # Unpatched java package. Fall back to transferring a float
+            scell.setOfficeValueTypeAttribute ('float');
+            if (c_arr{ii, jj})
+              scell.setOfficeValueAttribute (1);
+            else
+              scell.setOfficeValueAttribute (0);
+            endif
+          end_try_catch
+        case 3  # string
+          scell.setOfficeValueTypeAttribute ('string');
+          pe = java_new ('org.odftoolkit.odfdom.doc.text.OdfTextParagraph', odfcont,'', c_arr{ii, jj});
+          scell.appendChild (pe);
+        case 4  # Formula.  
+          # As we don't know the result type, simply remove previous type info.
+          # Once OOo Calc reads it, it'll add the missing attributes
+          scell.removeAttribute ('office:value');
+          scell.removeAttribute ('office:value-type');
+          # Try-catch not strictly needed, there's no formula validator yet
+          try
+            scell.setTableFormulaAttribute (c_arr{ii, jj});
+            scell.setOfficeValueTypeAttribute ('string');
+            pe = java_new ('org.odftoolkit.odfdom.doc.text.OdfTextParagraph', odfcont,'', '#Recalc Formula#');
+            scell.appendChild (pe);
+          catch
+            ++f_errs;
+            scell.setOfficeValueTypeAttribute ('string');
+            pe = java_new ('org.odftoolkit.odfdom.doc.text.OdfTextParagraph', odfcont,'', c_arr{ii, jj});
+            scell.appendChild (pe);
+          end_try_catch
+        case {0 5}  # Empty. Clear value attributes
+          if (~newsh)
+            scell.removeAttribute ('office:value-type');
+            scell.removeAttribute ('office:value');
+          endif
+        case 6  # Date (implemented but Octave has no "date" data type - yet?)
+          scell.setOfficeValueTypeAttribute ('date');
+          [hh mo dd hh mi ss] = datevec (c_arr{ii,jj});
+          str = sprintf ("%4d-%2d-%2dT%2d:%2d:%2d", yy, mo, dd, hh, mi, ss);
+          scell.setOfficeDateValueAttribute (str);
+        case 7  # Time (implemented but Octave has no "time" data type)
+          scell.setOfficeValueTypeAttribute ('time');
+          [hh mo dd hh mi ss] = datevec (c_arr{ii,jj});
+          str = sprintf ("PT%2d:%2d:%2d", hh, mi, ss);
+          scell.setOfficeTimeValuettribute (str);
+        otherwise
+          # Nothing
+      endswitch
 
-			scell = scell.getNextSibling ();
+      scell = scell.getNextSibling ();
 
-		endfor
+    endfor
 
-		row = row.getNextSibling ();
+    row = row.getNextSibling ();
 
-	endfor
+  endfor
 
-	if (f_errs) 
-		printf ("%d formula errors encountered - please check input array\n", f_errs); 
-	endif
-	ods.changed = max (min (ods.changed, 2), changed);	# Preserve 2 (new file), 1 (existing)
-	rstatus = 1;
-	
+  if (f_errs) 
+    printf ("%d formula errors encountered - please check input array\n", f_errs); 
+  endif
+  ods.changed = max (min (ods.changed, 2), changed);  # Preserve 2 (new file), 1 (existing)
+  rstatus = 1;
+  
 endfunction
 
 
 #=============================================================================
 
-## Copyright (C) 2010,2011 Philip Nienhuis <prnienhuis _at- users.sf.net>
+## Copyright (C) 2010,2011,2012 Philip Nienhuis <prnienhuis _at- users.sf.net>
 ##
 ## 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
@@ -662,9 +660,9 @@
 ## You should have received a copy of the GNU General Public License along with
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
-## odf3jotk2oct - read ODS spreadsheet data using Java & odftoolkit v 0.8.6.
-## You need proper java-for-octave & odfdom.jar 0.8.6 + xercesImpl.jar
-## in your javaclasspath. For reliable writing odfdom-0.8.6 is still
+## odf3jotk2oct - read ODS spreadsheet data using Java & odftoolkit v 0.8.6+.
+## You need proper java-for-octave & odfdom.jar 0.8.6+ & xercesImpl.jar 2.9.1
+## in your javaclasspath. For reliable writing odfdom-0.8.6+ is still
 ## experimental :-(  v. 0.7.5 has been tested much more
 ##
 ## Author: Philip Nenhuis <pr.nienhuis at users.sf.net>
@@ -684,167 +682,168 @@
 ## 2010-11-12 Improved file change tracking tru ods.changed
 ## 2010-12-08 Bugfixes (obj -> arg L.715; removed stray arg in call to spsh_prstype L.719)
 ## 2011-03-23 First try of odfdom 0.8.7
+## 2012-06-08 Support for odfdom-incubator-0.8.8
 
 function [ ods, rstatus ] = oct3jotk2ods (c_arr, ods, wsh, crange, spsh_opts)
 
-	persistent ctype;
-	if (isempty (ctype))
-		# Number, Boolean, String, Formula, Empty; Date, Time - last two aren't used
-		ctype = [1, 2, 3, 4, 5, 6, 7];
-	endif
+  persistent ctype;
+  if (isempty (ctype))
+    # Number, Boolean, String, Formula, Empty; Date, Time - last two aren't used
+    ctype = [1, 2, 3, 4, 5, 6, 7];
+  endif
 
-	rstatus = 0; changed = 0; newsh = 0;
+  rstatus = 0; changed = 0; newsh = 0;
 
-	# Get contents and table stuff from the workbook
-	odfcont = ods.workbook;		# Use a local copy just to be sure. octave 
-								# makes physical copies only when needed (?)
-	odfroot = odfcont.getRootElement ();
-	offsprdsh = ods.app.getContentRoot();
-	if (strcmp (ods.odfvsn, '0.8.7'))
-		spsh = odfcont.getDocument ();
-	else
-		spsh = odfcont.getOdfDocument ();
-	endif
+  # Get contents and table stuff from the workbook
+  odfcont = ods.workbook;    # Use a local copy just to be sure. octave 
+                # makes physical copies only when needed (?)
+  odfroot = odfcont.getRootElement ();
+  offsprdsh = ods.app.getContentRoot();
+  if (strcmp (ods.odfvsn, '0.8.7') || strfind (ods.odfvsn, "0.8.8"))
+    spsh = odfcont.getDocument ();
+  else
+    spsh = odfcont.getOdfDocument ();
+  endif
 
-	# Get some basic spreadsheet data from the pointer using ODFtoolkit
-	autostyles = odfcont.getOrCreateAutomaticStyles();
-	officestyles = ods.app.getOrCreateDocumentStyles();
+  # Get some basic spreadsheet data from the pointer using ODFtoolkit
+  autostyles = odfcont.getOrCreateAutomaticStyles();
+  officestyles = ods.app.getOrCreateDocumentStyles();
 
-	# Parse sheets ("tables") from ODS file
-	sheets = ods.app.getTableList();
-	nr_of_sheets = sheets.size ();
-	# Check user input & find sheet pointer
-	if (~isnumeric (wsh))
-		try
-			sh = ods.app.getTableByName (wsh);
-			# We do need a sheet index number...
-			ii = 0;
-			while (ischar (wsh) && ii < nr_of_sheets) 
-				sh_nm = sh.getTableName ();
-				if (strcmp (sh_nm, wsh)) wsh = ii + 1; else ++ii; endif
-			endwhile
-		catch
-			newsh = 1;
-		end_try_catch
-		if isempty (sh) newsh = 1; endif
-	elseif (wsh < 1)
-		# Negative sheet number:
-		error (sprintf ("Illegal worksheet nr. %d\n", wsh));
-	elseif (wsh > nr_of_sheets)
-		newsh = 1;
-	else
-		sh = sheets.get (wsh - 1);
-	endif
+  # Parse sheets ("tables") from ODS file
+  sheets = ods.app.getTableList();
+  nr_of_sheets = sheets.size ();
+  # Check user input & find sheet pointer
+  if (~isnumeric (wsh))
+    try
+      sh = ods.app.getTableByName (wsh);
+      # We do need a sheet index number...
+      ii = 0;
+      while (ischar (wsh) && ii < nr_of_sheets) 
+        sh_nm = sh.getTableName ();
+        if (strcmp (sh_nm, wsh)) wsh = ii + 1; else ++ii; endif
+      endwhile
+    catch
+      newsh = 1;
+    end_try_catch
+    if isempty (sh) newsh = 1; endif
+  elseif (wsh < 1)
+    # Negative sheet number:
+    error (sprintf ("Illegal worksheet nr. %d\n", wsh));
+  elseif (wsh > nr_of_sheets)
+    newsh = 1;
+  else
+    sh = sheets.get (wsh - 1);
+  endif
 
-	# Check size of data array & range / capacity of worksheet & prepare vars
-	[nr, nc] = size (c_arr);
-	[topleft, nrows, ncols, trow, lcol] = spsh_chkrange (crange, nr, nc, ods.xtype, ods.filename);
-	--trow; --lcol;									# Zero-based row # & col #
-	if (nrows < nr || ncols < nc)
-		warning ("Array truncated to fit in range");
-		c_arr = c_arr(1:nrows, 1:ncols);
-	endif
-	
+  # Check size of data array & range / capacity of worksheet & prepare vars
+  [nr, nc] = size (c_arr);
+  [topleft, nrows, ncols, trow, lcol] = spsh_chkrange (crange, nr, nc, ods.xtype, ods.filename);
+  --trow; --lcol;                  # Zero-based row # & col #
+  if (nrows < nr || ncols < nc)
+    warning ("Array truncated to fit in range");
+    c_arr = c_arr(1:nrows, 1:ncols);
+  endif
+  
 # Parse data array, setup typarr and throw out NaNs  to speed up writing;
-	typearr = spsh_prstype (c_arr, nrows, ncols, ctype, spsh_opts);
-	if ~(spsh_opts.formulas_as_text)
-		# Find formulas (designated by a string starting with "=" and ending in ")")
-		fptr = cellfun (@(x) ischar (x) && strncmp (x, "=", 1) && strncmp (x(end:end), ")", 1), c_arr);
-		typearr(fptr) = ctype(4);					# FORMULA
-	endif
+  typearr = spsh_prstype (c_arr, nrows, ncols, ctype, spsh_opts);
+  if ~(spsh_opts.formulas_as_text)
+    # Find formulas (designated by a string starting with "=" and ending in ")")
+    fptr = cellfun (@(x) ischar (x) && strncmp (x, "=", 1) && strncmp (x(end:end), ")", 1), c_arr);
+    typearr(fptr) = ctype(4);          # FORMULA
+  endif
 
 # Prepare spreadsheet for writing (size, etc.). If needed create new sheet
-	if (newsh)
-		if (ods.changed > 2)
-			# New spreadsheet, use default first sheet
-			sh = sheets.get (0);
-		else
-			# Create a new sheet using DOM API. This part works OK.
-			sh = sheets.get (nr_of_sheets - 1).newTable (spsh, nrows, ncols);
-		endif
-		changed = 1;
-		if (isnumeric (wsh))
-			# Give sheet a name
-			str = sprintf ("Sheet%d", wsh);
-			sh.setTableName (str);
-			wsh = str;
-		else
-			# Assign name to sheet and change wsh into numeric pointer
-			sh.setTableName (wsh);
-		endif
-		printf ("Sheet %s added to spreadsheet.\n", wsh);
-		
-	else
-		# Add "physical" rows & columns. Spreadsheet max. capacity checks have been done above
-		# Add spreadsheet data columns if needed. Compute nr of extra columns & rows.
-		curr_ncols = sh.getColumnCount ();
-		ii = max (0, lcol + ncols - curr_ncols);
-		if (ii == 1)
-			nwcols = sh.appendColumn ();
-		else
-			nwcols = sh.appendColumns (ii);
-		endif
+  if (newsh)
+    if (ods.changed > 2)
+      # New spreadsheet, use default first sheet
+      sh = sheets.get (0);
+    else
+      # Create a new sheet using DOM API. This part works OK.
+      sh = sheets.get (nr_of_sheets - 1).newTable (spsh, nrows, ncols);
+    endif
+    changed = 1;
+    if (isnumeric (wsh))
+      # Give sheet a name
+      str = sprintf ("Sheet%d", wsh);
+      sh.setTableName (str);
+      wsh = str;
+    else
+      # Assign name to sheet and change wsh into numeric pointer
+      sh.setTableName (wsh);
+    endif
+    printf ("Sheet %s added to spreadsheet.\n", wsh);
+    
+  else
+    # Add "physical" rows & columns. Spreadsheet max. capacity checks have been done above
+    # Add spreadsheet data columns if needed. Compute nr of extra columns & rows.
+    curr_ncols = sh.getColumnCount ();
+    ii = max (0, lcol + ncols - curr_ncols);
+    if (ii == 1)
+      nwcols = sh.appendColumn ();
+    else
+      nwcols = sh.appendColumns (ii);
+    endif
 
-		# Add spreadsheet rows if needed
-		curr_nrows = sh.getRowCount ();
-		ii = max (0, trow + nrows - curr_nrows);
-		if (ii == 1)
-			nwrows = sh.appendRow ();
-		else
-			nwrows = sh.appendRows (ii);
-		endif
-	endif
+    # Add spreadsheet rows if needed
+    curr_nrows = sh.getRowCount ();
+    ii = max (0, trow + nrows - curr_nrows);
+    if (ii == 1)
+      nwrows = sh.appendRow ();
+    else
+      nwrows = sh.appendRows (ii);
+    endif
+  endif
  
-	# Transfer array data to sheet
-	for ii=1:nrows
-		for jj=1:ncols
-			ocell = sh.getCellByPosition (jj+lcol-1, ii+trow-1);
-			if ~(isempty (ocell )) # Might be spanned (merged), hidden, ....
-				# Number, String, Boolean, Date, Time
-				try
-					switch typearr (ii, jj)
-						case {1, 6, 7}	# Numeric, Date, Time
-							ocell.setDoubleValue (c_arr{ii, jj}); 
-						case 2	# Logical / Boolean
-							# ocell.setBooleanValue (c_arr{ii, jj}); # Doesn't work, bug in odfdom 0.8.6
-							# Bug workaround: 1. Remove all cell contents
-							ocell.removeContent ();
-							# 2. Switch to TableTableElement API
-							tocell = ocell.getOdfElement ();
-							tocell.setAttributeNS ('office', 'office:value-type', 'boolean');
-							# 3. Add boolean-value attribute. 
-							# This is only accepted in TTE API with a NS tag (actual bug, IMO)
-							if (c_arr {ii,jj})
-								tocell.setAttributeNS ('office', 'office:boolean-value', 'true');
-							else
-								tocell.setAttributeNS ('office', 'office:boolean-value', 'false');
-							endif
-						case 3	# String
-							ocell.setStringValue (c_arr{ii, jj});
-						case 4	# Formula
-							ocell.setFormula (c_arr{ii, jj});
-						otherwise     # 5, empty and catch-all
-							# The above is all octave has to offer & java can accept...
-					endswitch
-					changed = 1;
-				catch
-					printf ("\n");
-				end_try_catch
-			endif
-		endfor
-	endfor
+  # Transfer array data to sheet
+  for ii=1:nrows
+    for jj=1:ncols
+      ocell = sh.getCellByPosition (jj+lcol-1, ii+trow-1);
+      if ~(isempty (ocell )) # Might be spanned (merged), hidden, ....
+        # Number, String, Boolean, Date, Time
+        try
+          switch typearr (ii, jj)
+            case {1, 6, 7}  # Numeric, Date, Time
+              ocell.setDoubleValue (c_arr{ii, jj}); 
+            case 2  # Logical / Boolean
+              # ocell.setBooleanValue (c_arr{ii, jj}); # Doesn't work, bug in odfdom 0.8.6
+              # Bug workaround: 1. Remove all cell contents
+              ocell.removeContent ();
+              # 2. Switch to TableTableElement API
+              tocell = ocell.getOdfElement ();
+              tocell.setAttributeNS ('office', 'office:value-type', 'boolean');
+              # 3. Add boolean-value attribute. 
+              # This is only accepted in TTE API with a NS tag (actual bug, IMO)
+              if (c_arr {ii,jj})
+                tocell.setAttributeNS ('office', 'office:boolean-value', 'true');
+              else
+                tocell.setAttributeNS ('office', 'office:boolean-value', 'false');
+              endif
+            case 3  # String
+              ocell.setStringValue (c_arr{ii, jj});
+            case 4  # Formula
+              ocell.setFormula (c_arr{ii, jj});
+            otherwise     # 5, empty and catch-all
+              # The above is all octave has to offer & java can accept...
+          endswitch
+          changed = 1;
+        catch
+          printf ("\n");
+        end_try_catch
+      endif
+    endfor
+  endfor
 
-	if (changed)	
-		ods.changed = max (min (ods.changed, 2), changed);	# Preserve 2 (new file), 1 (existing)
-		rstatus = 1;
-	endif
+  if (changed)  
+    ods.changed = max (min (ods.changed, 2), changed);  # Preserve 2 (new file), 1 (existing)
+    rstatus = 1;
+  endif
 
 endfunction
 
 
 #=============================================================================
 
-## Copyright (C) 2009,2010,2011 Philip Nienhuis <pr.nienhuis at users.sf.net>
+## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <pr.nienhuis at users.sf.net>
 ##
 ## 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
@@ -887,124 +886,124 @@
 
 function [ ods, rstatus ] = oct2jod2ods (c_arr, ods, wsh, crange)
 
-	rstatus = 0; sh = []; changed = 0;
+  rstatus = 0; sh = []; changed = 0;
 
-	# Get worksheet. Use first one if none given
-	if (isempty (wsh)) wsh = 1; endif
-	sh_cnt = ods.workbook.getSheetCount ();
-	if (isnumeric (wsh))
-		if (wsh > 1024)
-			error ("Sheet number out of range of ODS specification (>1024)");
-		elseif (wsh > sh_cnt)
-			error ("Sheet number (%d) larger than number of sheets in file (%d)\n", wsh, sh_cnt);
-		else
-			wsh = wsh - 1;
-			sh = ods.workbook.getSheet (wsh);
-			if (isempty (sh))
-				# Sheet number wsh didn't exist yet
-				wsh = sprintf ("Sheet%d", wsh+1);
-			elseif (ods.changed > 2)
-				sh.setName ('Sheet1');
-				changed = 1;
-			endif
-		endif
-	endif
-	# wsh is now either a 0-based sheet no. or a string. In latter case:
-	if (isempty (sh) && ischar (wsh))
-		sh = ods.workbook.getSheet (wsh);
-		if (isempty (sh))
-			# Still doesn't exist. Create sheet
-			if (ods.odfvsn == 3)
-				if (ods.changed > 2)
-					# 1st "new" -unnamed- sheet has already been made when creating the spreadsheet
-					sh = ods.workbook.getSheet (0);
-					sh.setName (wsh);
-					changed = 1;
-				else
-					# For existing spreadsheets
-					printf ("Adding sheet '%s'\n", wsh);
-					sh = ods.workbook.addSheet (sh_cnt, wsh);
-					changed = 1;
-				endif
-			else
-				error ("jOpenDocument v. 1.2b2 does not support adding sheets - upgrade to v. 1.2b3\n");
-			endif
-		endif
-	endif
+  # Get worksheet. Use first one if none given
+  if (isempty (wsh)) wsh = 1; endif
+  sh_cnt = ods.workbook.getSheetCount ();
+  if (isnumeric (wsh))
+    if (wsh > 1024)
+      error ("Sheet number out of range of ODS specification (>1024)");
+    elseif (wsh > sh_cnt)
+      error ("Sheet number (%d) larger than number of sheets in file (%d)\n", wsh, sh_cnt);
+    else
+      wsh = wsh - 1;
+      sh = ods.workbook.getSheet (wsh);
+      if (isempty (sh))
+        # Sheet number wsh didn't exist yet
+        wsh = sprintf ("Sheet%d", wsh+1);
+      elseif (ods.changed > 2)
+        sh.setName ('Sheet1');
+        changed = 1;
+      endif
+    endif
+  endif
+  # wsh is now either a 0-based sheet no. or a string. In latter case:
+  if (isempty (sh) && ischar (wsh))
+    sh = ods.workbook.getSheet (wsh);
+    if (isempty (sh))
+      # Still doesn't exist. Create sheet
+      if (ods.odfvsn == 3)
+        if (ods.changed > 2)
+          # 1st "new" -unnamed- sheet has already been made when creating the spreadsheet
+          sh = ods.workbook.getSheet (0);
+          sh.setName (wsh);
+          changed = 1;
+        else
+          # For existing spreadsheets
+          printf ("Adding sheet '%s'\n", wsh);
+          sh = ods.workbook.addSheet (sh_cnt, wsh);
+          changed = 1;
+        endif
+      else
+        error ("jOpenDocument v. 1.2b2 does not support adding sheets - upgrade to v. 1.2b3\n");
+      endif
+    endif
+  endif
 
-	[nr, nc] = size (c_arr);
-	if (isempty (crange))
-		trow = 0;
-		lcol = 0;
-		nrows = nr;
-		ncols = nc;
-	elseif (isempty (strfind (deblank (crange), ':'))) 
-		[dummy1, dummy2, dummy3, trow, lcol] = parse_sp_range (crange);
-		nrows = nr;
-		ncols = nc;
-		# Row/col = 0 based in jOpenDocument
-		trow = trow - 1; lcol = lcol - 1;
-	else
-		[dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
-		# Row/col = 0 based in jOpenDocument
-		trow = trow - 1; lcol = lcol - 1;
-	endif
+  [nr, nc] = size (c_arr);
+  if (isempty (crange))
+    trow = 0;
+    lcol = 0;
+    nrows = nr;
+    ncols = nc;
+  elseif (isempty (strfind (deblank (crange), ':'))) 
+    [dummy1, dummy2, dummy3, trow, lcol] = parse_sp_range (crange);
+    nrows = nr;
+    ncols = nc;
+    # Row/col = 0 based in jOpenDocument
+    trow = trow - 1; lcol = lcol - 1;
+  else
+    [dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
+    # Row/col = 0 based in jOpenDocument
+    trow = trow - 1; lcol = lcol - 1;
+  endif
 
-	if (trow > 65535 || lcol > 1023)
-		error ("Topleft cell beyond spreadsheet limits (AMJ65536).");
-	endif
-	# Check spreadsheet capacity beyond requested topleft cell
-	nrows = min (nrows, 65536 - trow);		# Remember, lcol & trow are zero-based
-	ncols = min (ncols, 1024 - lcol);
-	# Check array size and requested range
-	nrows = min (nrows, nr);
-	ncols = min (ncols, nc);
-	if (nrows < nr || ncols < nc) warning ("Array truncated to fit in range"); endif
+  if (trow > 65535 || lcol > 1023)
+    error ("Topleft cell beyond spreadsheet limits (AMJ65536).");
+  endif
+  # Check spreadsheet capacity beyond requested topleft cell
+  nrows = min (nrows, 65536 - trow);    # Remember, lcol & trow are zero-based
+  ncols = min (ncols, 1024 - lcol);
+  # Check array size and requested range
+  nrows = min (nrows, nr);
+  ncols = min (ncols, nc);
+  if (nrows < nr || ncols < nc) warning ("Array truncated to fit in range"); endif
 
-	if (isnumeric (c_arr)) c_arr = num2cell (c_arr); endif
+  if (isnumeric (c_arr)) c_arr = num2cell (c_arr); endif
 
-	# Ensure sheet capacity is large enough to contain new data
-	try		# try-catch needed to work around bug in jOpenDocument v 1.2b3 and earlier
-		sh.ensureColumnCount (lcol + ncols);	# Remember, lcol & trow are zero-based
-	catch	# catch is needed for new empty sheets (first ensureColCnt() hits null ptr)
-		sh.ensureColumnCount (lcol + ncols);
-		# Kludge needed because upper row is defective (NPE jOpenDocument bug). ?Fixed in 1.2b4?
-		if (trow == 0)
-			# Shift rows one down to avoid defective upper row
-			++trow;
-			printf ("Info: empy upper row above data added to avoid JOD bug.\n");
-		endif
-	end_try_catch
-	sh.ensureRowCount (trow + nrows);
+  # Ensure sheet capacity is large enough to contain new data
+  try    # try-catch needed to work around bug in jOpenDocument v 1.2b3 and earlier
+    sh.ensureColumnCount (lcol + ncols);  # Remember, lcol & trow are zero-based
+  catch  # catch is needed for new empty sheets (first ensureColCnt() hits null ptr)
+    sh.ensureColumnCount (lcol + ncols);
+    # Kludge needed because upper row is defective (NPE jOpenDocument bug). ?Fixed in 1.2b4?
+    if (trow == 0)
+      # Shift rows one down to avoid defective upper row
+      ++trow;
+      printf ("Info: empy upper row above data added to avoid JOD bug.\n");
+    endif
+  end_try_catch
+  sh.ensureRowCount (trow + nrows);
 
-	# Write data to worksheet
-	for ii = 1 : nrows
-		for jj = 1 : ncols
-			val = c_arr {ii, jj};
-			if ((isnumeric (val) && ~isnan (val)) || ischar (val) || islogical (val))
+  # Write data to worksheet
+  for ii = 1 : nrows
+    for jj = 1 : ncols
+      val = c_arr {ii, jj};
+      if ((isnumeric (val) && ~isnan (val)) || ischar (val) || islogical (val))
         # FIXME: jOpenDocument doesn't really support writing booleans (doesn't set OffValAttr)
         if (islogical (val)); val = double (val); endif
-				try
-					sh.getCellAt (jj + lcol - 1, ii + trow - 1).clearValue();
-					jcell = sh.getCellAt (jj + lcol - 1, ii + trow - 1).setValue (val);
-					changed = 1;
-				catch
-					# No panic, probably a merged cell
-				#	printf (sprintf ("Cell skipped at (%d, %d)\n", ii+lcol-1, jj+trow-1));
-				end_try_catch
-			endif
-		endfor
-	endfor
+        try
+          sh.getCellAt (jj + lcol - 1, ii + trow - 1).clearValue();
+          jcell = sh.getCellAt (jj + lcol - 1, ii + trow - 1).setValue (val);
+          changed = 1;
+        catch
+          # No panic, probably a merged cell
+        #  printf (sprintf ("Cell skipped at (%d, %d)\n", ii+lcol-1, jj+trow-1));
+        end_try_catch
+      endif
+    endfor
+  endfor
 
-	if (changed)
-		ods.changed = max (min (ods.changed, 2), changed);	# Preserve 2 (new file), 1 (existing)
-		rstatus = 1;
-	endif
+  if (changed)
+    ods.changed = max (min (ods.changed, 2), changed);  # Preserve 2 (new file), 1 (existing)
+    rstatus = 1;
+  endif
 
 endfunction
 
 
-## Copyright (C) 2011 Philip Nienhuis <prnienhuis@users.sf.net>
+## Copyright (C) 2011,2012 Philip Nienhuis <prnienhuis@users.sf.net>
 ##
 ## 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
@@ -1101,7 +1100,7 @@
       # wsh is a sheet name. See if it exists already
       if (isempty (strmatch (wsh, sh_names)))
         # Not found. New sheet to be added
-	      newsh = 1;
+        newsh = 1;
       endif
     endif
     if (newsh)
@@ -1122,7 +1121,7 @@
     warning ("Array truncated to fit in range");
     c_arr = c_arr(1:nrows, 1:ncols);
   endif
-	
+  
   # Parse data array, setup typarr and throw out NaNs  to speed up writing;
   typearr = spsh_prstype (c_arr, nrows, ncols, ctype, spsh_opts, 0);
   if ~(spsh_opts.formulas_as_text)
@@ -1137,14 +1136,14 @@
       try
         XCell = sh.getCellByPosition (lcol+jj-1, trow+ii-1);
         switch typearr(ii, jj)
-          case 1	# Float
+          case 1  # Float
             XCell.setValue (c_arr{ii, jj});
-          case 2	# Logical. Convert to float
+          case 2  # Logical. Convert to float
             XCell.setValue (double (c_arr{ii, jj}));
-          case 3	# String
+          case 3  # String
             unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText');
             XCell.queryInterface (unotmp).setString (c_arr{ii, jj});
-          case 4	# Formula
+          case 4  # Formula
             if (spsh_opts.formulas_as_text)
               unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText');
               XCell.queryInterface (unotmp).setString (c_arr{ii, jj});
@@ -1154,15 +1153,15 @@
           otherwise
             # Empty cell
         endswitch
-		    changed = 1;
+        changed = 1;
       catch
         printf ("Error writing cell %s (typearr() = %d)\n", calccelladdress(trow+ii, lcol+jj), typearr(ii, jj));
-		  end_try_catch
+      end_try_catch
     endfor
   endfor
 
-  if (changed)	
-    ods.changed = max (min (ods.changed, 2), changed);	# Preserve 2 (new file), 1 (existing)
+  if (changed)  
+    ods.changed = max (min (ods.changed, 2), changed);  # Preserve 2 (new file), 1 (existing)
     rstatus = 1;
   endif
 
--- a/main/io/inst/ods2oct.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/ods2oct.m	Fri Jun 08 15:28:27 2012 +0000
@@ -121,91 +121,93 @@
 ## 2012-01-26 Fixed "seealso" help string
 ## 2012-02-25 Added 0.8.7 to supported odfdom versions in L.155
 ## 2012-02-26 Updated texinfo header help text
+## 2012-06-08 Support for odfdom-incubator 0.8.8
+##     ''     Replaced tabs by double space
 ##
-## (Latest update of subfunctions below: 2012-02-25)
+## (Latest update of subfunctions below: 2012-06-08)
 
 function [ rawarr, ods, rstatus ] = ods2oct (ods, wsh=1, datrange=[], spsh_opts=[])
 
-	# Check if ods struct pointer seems valid
-	if (~isstruct (ods)), error ("File ptr struct expected for arg @ 1"); endif
-	test1 = ~isfield (ods, "xtype");
-	test1 = test1 || ~isfield (ods, "workbook");
-	test1 = test1 || isempty (ods.workbook);
-	test1 = test1 || isempty (ods.app);
-	if (test1)
-		error ("Arg #1 is an invalid ods file struct");
-	endif
-	# Check worksheet ptr
-	if (~(ischar (wsh) || isnumeric (wsh))), error ("Integer (index) or text (wsh name) expected for arg # 2"); endif
-	# Check range
-	if (~(isempty (datrange) || ischar (datrange))), error ("Character string (range) expected for arg # 3"); endif
-	# Check & setup options struct
-	if (nargin < 4 || isempty (spsh_opts))
-		spsh_opts.formulas_as_text = 0;
-		spsh_opts.strip_array = 1;
-		# Other options here
-	elseif (~isstruct (spsh_opts))
-		error ("struct expected for OPTIONS argument (# 4)");
-	else
-		if (~isfield (spsh_opts, 'formulas_as_text')), spsh_opts.formulas_as_text = 0; endif
-		if (~isfield (spsh_opts, 'strip_array')), spsh_opts.strip_array = 1; endif
-		% Future options:
-	endif
+  # Check if ods struct pointer seems valid
+  if (~isstruct (ods)), error ("File ptr struct expected for arg @ 1"); endif
+  test1 = ~isfield (ods, "xtype");
+  test1 = test1 || ~isfield (ods, "workbook");
+  test1 = test1 || isempty (ods.workbook);
+  test1 = test1 || isempty (ods.app);
+  if (test1)
+    error ("Arg #1 is an invalid ods file struct");
+  endif
+  # Check worksheet ptr
+  if (~(ischar (wsh) || isnumeric (wsh))), error ("Integer (index) or text (wsh name) expected for arg # 2"); endif
+  # Check range
+  if (~(isempty (datrange) || ischar (datrange))), error ("Character string (range) expected for arg # 3"); endif
+  # Check & setup options struct
+  if (nargin < 4 || isempty (spsh_opts))
+    spsh_opts.formulas_as_text = 0;
+    spsh_opts.strip_array = 1;
+    # Other options here
+  elseif (~isstruct (spsh_opts))
+    error ("struct expected for OPTIONS argument (# 4)");
+  else
+    if (~isfield (spsh_opts, 'formulas_as_text')), spsh_opts.formulas_as_text = 0; endif
+    if (~isfield (spsh_opts, 'strip_array')), spsh_opts.strip_array = 1; endif
+    % Future options:
+  endif
 
-	# Select the proper interfaces
-	if (strcmp (ods.xtype, 'OTK'))
-		# Read ods file tru Java & ODF toolkit
-		switch ods.odfvsn
-			case '0.7.5'
-				[rawarr, ods] = ods2jotk2oct (ods, wsh, datrange, spsh_opts);
-			case {'0.8.6', '0.8.7'}
-				[rawarr, ods] = ods3jotk2oct (ods, wsh, datrange, spsh_opts);
-			otherwise
-				error ("Unsupported odfdom version or invalid ods file pointer.");
-		endswitch
-	elseif (strcmp (ods.xtype, 'JOD'))
-		# Read ods file tru Java & jOpenDocument. JOD doesn't know about formulas :-(
-		[rawarr, ods] = ods2jod2oct  (ods, wsh, datrange);
-	elseif (strcmp (ods.xtype, 'UNO'))
-		# Read ods file tru Java & UNO
-		[rawarr, ods] = ods2uno2oct (ods, wsh, datrange, spsh_opts);
-#	elseif 
-	#	---- < Other interfaces here >
-	else
-		error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype));
-	endif
+  # Select the proper interfaces
+  if (strcmp (ods.xtype, 'OTK'))
+    # Read ods file tru Java & ODF toolkit
+    switch ods.odfvsn
+      case '0.7.5'
+        [rawarr, ods] = ods2jotk2oct (ods, wsh, datrange, spsh_opts);
+      case {'0.8.6', '0.8.7', '0.8.8'}
+        [rawarr, ods] = ods3jotk2oct (ods, wsh, datrange, spsh_opts);
+      otherwise
+        error ("Unsupported odfdom version or invalid ods file pointer.");
+    endswitch
+  elseif (strcmp (ods.xtype, 'JOD'))
+    # Read ods file tru Java & jOpenDocument. JOD doesn't know about formulas :-(
+    [rawarr, ods] = ods2jod2oct  (ods, wsh, datrange);
+  elseif (strcmp (ods.xtype, 'UNO'))
+    # Read ods file tru Java & UNO
+    [rawarr, ods] = ods2uno2oct (ods, wsh, datrange, spsh_opts);
+#  elseif 
+  #  ---- < Other interfaces here >
+  else
+    error (sprintf ("ods2oct: unknown OpenOffice.org .ods interface - %s.", ods.xtype));
+  endif
 
   rstatus = ~isempty (rawarr);
 
-	# Optionally strip empty outer rows and columns & keep track of original data location
-	if (spsh_opts.strip_array && rstatus)
-		emptr = cellfun ('isempty', rawarr);
-		if (all (all (emptr)))
-			rawarr = {};
-			ods.limits= [];
-		else
-			nrows = size (rawarr, 1); ncols = size (rawarr, 2);
-			irowt = 1;
-			while (all (emptr(irowt, :))), irowt++; endwhile
-			irowb = nrows;
-			while (all (emptr(irowb, :))), irowb--; endwhile
-			icoll = 1;
-			while (all (emptr(:, icoll))), icoll++; endwhile
-			icolr = ncols;
-			while (all (emptr(:, icolr))), icolr--; endwhile
+  # Optionally strip empty outer rows and columns & keep track of original data location
+  if (spsh_opts.strip_array && rstatus)
+    emptr = cellfun ('isempty', rawarr);
+    if (all (all (emptr)))
+      rawarr = {};
+      ods.limits= [];
+    else
+      nrows = size (rawarr, 1); ncols = size (rawarr, 2);
+      irowt = 1;
+      while (all (emptr(irowt, :))), irowt++; endwhile
+      irowb = nrows;
+      while (all (emptr(irowb, :))), irowb--; endwhile
+      icoll = 1;
+      while (all (emptr(:, icoll))), icoll++; endwhile
+      icolr = ncols;
+      while (all (emptr(:, icolr))), icolr--; endwhile
 
-			# Crop outer rows and columns and update limits
-			rawarr = rawarr(irowt:irowb, icoll:icolr);
-			ods.limits = ods.limits + [icoll-1, icolr-ncols; irowt-1, irowb-nrows];
-		endif
-	endif
+      # Crop outer rows and columns and update limits
+      rawarr = rawarr(irowt:irowb, icoll:icolr);
+      ods.limits = ods.limits + [icoll-1, icolr-ncols; irowt-1, irowb-nrows];
+    endif
+  endif
 
 endfunction
 
 
 #=====================================================================
 
-## Copyright (C) 2009,2010,2011 Philip Nienhuis <prnienhuis _at- users.sf.net>
+## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <prnienhuis _at- users.sf.net>
 ##
 ## 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
@@ -239,187 +241,187 @@
 
 function [ rawarr, ods ] = ods2jotk2oct (ods, wsh, crange, spsh_opts)
 
-	# Parts after user gfterry in
-	# http://www.oooforum.org/forum/viewtopic.phtml?t=69060
-	
-	# Get contents and table stuff from the workbook
-	odfcont = ods.workbook;		# Use a local copy just to be sure. octave 
-								# makes physical copies only when needed (?)
-	xpath = ods.app.getXPath;
-	
-	# AFAICS ODS spreadsheets have the following hierarchy (after Xpath processing):
-	# <table:table> - table nodes, the actual worksheets;
-	# <table:table-row> - row nodes, the rows in a worksheet;
-	# <table:table-cell> - cell nodes, the cells in a row;
-	# Styles (formatting) are defined in a section "settings" outside the
-	# contents proper but are referenced in the nodes.
-	
-	# Create an instance of type NODESET for use in subsequent statement
-	NODESET = java_get ('javax.xml.xpath.XPathConstants', 'NODESET');
-	# Parse sheets ("tables") from ODS file
-	sheets = xpath.evaluate ("//table:table", odfcont, NODESET);
-	nr_of_sheets = sheets.getLength ();
+  # Parts after user gfterry in
+  # http://www.oooforum.org/forum/viewtopic.phtml?t=69060
+  
+  # Get contents and table stuff from the workbook
+  odfcont = ods.workbook;    # Use a local copy just to be sure. octave 
+                            # makes physical copies only when needed (?)
+  xpath = ods.app.getXPath;
+  
+  # AFAICS ODS spreadsheets have the following hierarchy (after Xpath processing):
+  # <table:table> - table nodes, the actual worksheets;
+  # <table:table-row> - row nodes, the rows in a worksheet;
+  # <table:table-cell> - cell nodes, the cells in a row;
+  # Styles (formatting) are defined in a section "settings" outside the
+  # contents proper but are referenced in the nodes.
+  
+  # Create an instance of type NODESET for use in subsequent statement
+  NODESET = java_get ('javax.xml.xpath.XPathConstants', 'NODESET');
+  # Parse sheets ("tables") from ODS file
+  sheets = xpath.evaluate ("//table:table", odfcont, NODESET);
+  nr_of_sheets = sheets.getLength ();
 
-	# Check user input & find sheet pointer (1-based), using ugly hacks
-	if (~isnumeric (wsh))
-		# Search in sheet names, match sheet name to sheet number
-		ii = 0;
-		while (++ii <= nr_of_sheets && ischar (wsh))	
-			# Look in first part of the sheet nodeset
-			sh_name = sheets.item(ii-1).getTableNameAttribute ();
-			if (strcmp (sh_name, wsh))
-				# Convert local copy of wsh into a number (pointer)
-				wsh = ii;
-			endif
-		endwhile
-		if (ischar (wsh))
-			error (sprintf ("No worksheet '%s' found in file %s", wsh, ods.filename));
-		endif
-	elseif (wsh > nr_of_sheets || wsh < 1)
-		# We already have a numeric sheet pointer. If it's not in range:
-		error (sprintf ("Worksheet no. %d out of range (1 - %d)", wsh, nr_of_sheets));
-	endif
+  # Check user input & find sheet pointer (1-based), using ugly hacks
+  if (~isnumeric (wsh))
+    # Search in sheet names, match sheet name to sheet number
+    ii = 0;
+    while (++ii <= nr_of_sheets && ischar (wsh))  
+      # Look in first part of the sheet nodeset
+      sh_name = sheets.item(ii-1).getTableNameAttribute ();
+      if (strcmp (sh_name, wsh))
+        # Convert local copy of wsh into a number (pointer)
+        wsh = ii;
+      endif
+    endwhile
+    if (ischar (wsh))
+      error (sprintf ("No worksheet '%s' found in file %s", wsh, ods.filename));
+    endif
+  elseif (wsh > nr_of_sheets || wsh < 1)
+    # We already have a numeric sheet pointer. If it's not in range:
+    error (sprintf ("Worksheet no. %d out of range (1 - %d)", wsh, nr_of_sheets));
+  endif
 
-	# Get table-rows in sheet no. wsh. Sheet count = 1-based (!)
-	str = sprintf ("//table:table[%d]/table:table-row", wsh);
-	sh = xpath.evaluate (str, odfcont, NODESET);
-	nr_of_rows = sh.getLength (); 
+  # Get table-rows in sheet no. wsh. Sheet count = 1-based (!)
+  str = sprintf ("//table:table[%d]/table:table-row", wsh);
+  sh = xpath.evaluate (str, odfcont, NODESET);
+  nr_of_rows = sh.getLength (); 
 
-	# Either parse (given cell range) or prepare (unknown range) help variables 
-	if (isempty (crange))
-		[ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
-		nrows = brow - trow + 1;	# Number of rows to be read
-		ncols = rcol - lcol + 1;	# Number of columns to be read
-	else
-		[dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
-		brow = min (trow + nrows - 1, nr_of_rows);
-		# Check ODS column limits
-		if (lcol > 1024 || trow > 65536) 
-			error ("ods2oct: invalid range; max 1024 columns & 65536 rows."); 
-		endif
-		# Truncate range silently if needed
-		rcol = min (lcol + ncols - 1, 1024);
-		ncols = min (ncols, 1024 - lcol + 1);
-		nrows = min (nrows, 65536 - trow + 1);
-	endif
-	# Create storage for data content
-	rawarr = cell (nrows, ncols);
+  # Either parse (given cell range) or prepare (unknown range) help variables 
+  if (isempty (crange))
+    [ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
+    nrows = brow - trow + 1;  # Number of rows to be read
+    ncols = rcol - lcol + 1;  # Number of columns to be read
+  else
+    [dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
+    brow = min (trow + nrows - 1, nr_of_rows);
+    # Check ODS column limits
+    if (lcol > 1024 || trow > 65536) 
+      error ("ods2oct: invalid range; max 1024 columns & 65536 rows."); 
+    endif
+    # Truncate range silently if needed
+    rcol = min (lcol + ncols - 1, 1024);
+    ncols = min (ncols, 1024 - lcol + 1);
+    nrows = min (nrows, 65536 - trow + 1);
+  endif
+  # Create storage for data content
+  rawarr = cell (nrows, ncols);
 
-	# Prepare reading sheet row by row
-	rightmcol = 0;		# Used to find actual rightmost column
-	ii = trow - 1;		# Spreadsheet row counter
-	rowcnt = 0;
-	# Find uppermost requested *tablerow*. It may be influenced by nr-rows-repeated
-	if (ii >= 1)
-		tfillrows = 0;
-		while (tfillrows < ii)
-			row = sh.item(tfillrows);
-			extrarows = row.getTableNumberRowsRepeatedAttribute ();
-			tfillrows = tfillrows + extrarows;
-			++rowcnt;
-		endwhile
-		# Desired top row may be in a nr-rows-repeated tablerow....
-		if (tfillrows > ii) ii = tfillrows; endif
-	endif
+  # Prepare reading sheet row by row
+  rightmcol = 0;    # Used to find actual rightmost column
+  ii = trow - 1;    # Spreadsheet row counter
+  rowcnt = 0;
+  # Find uppermost requested *tablerow*. It may be influenced by nr-rows-repeated
+  if (ii >= 1)
+    tfillrows = 0;
+    while (tfillrows < ii)
+      row = sh.item(tfillrows);
+      extrarows = row.getTableNumberRowsRepeatedAttribute ();
+      tfillrows = tfillrows + extrarows;
+      ++rowcnt;
+    endwhile
+    # Desired top row may be in a nr-rows-repeated tablerow....
+    if (tfillrows > ii); ii = tfillrows; endif
+  endif
 
-	# Read from worksheet row by row. Row numbers are 0-based
-	while (ii < brow)
-		row = sh.item(rowcnt++);
-		nr_of_cells = min (row.getLength (), rcol);
-		rightmcol = max (rightmcol, nr_of_cells);	# Keep track of max row length
-		# Read column (cell, "table-cell" in ODS speak) by column
-		jj = lcol; 
-		while (jj <= rcol)
-			tcell = row.getCellAt(jj-1);
-			form = 0;
-			if (~isempty (tcell)) 		# If empty it's possibly in columns-repeated/spanned
-				if (spsh_opts.formulas_as_text)   # Get spreadsheet formula rather than value
-					# Check for formula attribute
-					tmp = tcell.getTableFormulaAttribute ();
-					if isempty (tmp)
-						form = 0;
-					else
-						if (strcmp (tolower (tmp(1:3)), 'of:'))
-							tmp (1:end-3) = tmp(4:end);
-						endif
-						rawarr(ii-trow+2, jj-lcol+1) = tmp;
-						form = 1;
-					endif
-				endif
-				if ~(form || index (char(tcell), 'text:p>Err:') || index (char(tcell), 'text:p>#DIV'))	
-					# Get data from cell
-					ctype = tcell.getOfficeValueTypeAttribute ();
-					cvalue = tcell.getOfficeValueAttribute ();
-					switch deblank (ctype)
-						case  {'float', 'currency', 'percentage'}
-							rawarr(ii-trow+2, jj-lcol+1) = cvalue;
-						case 'date'
-							cvalue = tcell.getOfficeDateValueAttribute ();
-							# Dates are returned as octave datenums, i.e. 0-0-0000 based
-							yr = str2num (cvalue(1:4));
-							mo = str2num (cvalue(6:7));
-							dy = str2num (cvalue(9:10));
-							if (index (cvalue, 'T'))
-								hh = str2num (cvalue(12:13));
-								mm = str2num (cvalue(15:16));
-								ss = str2num (cvalue(18:19));
-								rawarr(ii-trow+2, jj-lcol+1) = datenum (yr, mo, dy, hh, mm, ss);
-							else
-								rawarr(ii-trow+2, jj-lcol+1) = datenum (yr, mo, dy);
-							endif
-						case 'time'
-							cvalue = tcell.getOfficeTimeValueAttribute ();
-							if (index (cvalue, 'PT'))
-								hh = str2num (cvalue(3:4));
-								mm = str2num (cvalue(6:7));
-								ss = str2num (cvalue(9:10));
-								rawarr(ii-trow+2, jj-lcol+1) = datenum (0, 0, 0, hh, mm, ss);
-							endif
-						case 'boolean'
-							cvalue = tcell.getOfficeBooleanValueAttribute ();
-							rawarr(ii-trow+2, jj-lcol+1) = cvalue; 
-						case 'string'
-							cvalue = tcell.getOfficeStringValueAttribute ();
-							if (isempty (cvalue))     # Happens with e.g., hyperlinks
-								tmp = char (tcell);
-								# Hack string value from between <text:p|r> </text:p|r> tags
-								ist = findstr (tmp, '<text:');
-								if (ist)
-									ist = ist (length (ist));
-									ist = ist + 8;
-									ien = index (tmp(ist:end), '</text') + ist - 2;
-									tmp (ist:ien);
-									cvalue = tmp(ist:ien);
-								endif
-							endif
-							rawarr(ii-trow+2, jj-lcol+1)= cvalue;
-						otherwise
-							# Nothing
-					endswitch
-				endif
-			endif
-			++jj;						# Next cell
-		endwhile
+  # Read from worksheet row by row. Row numbers are 0-based
+  while (ii < brow)
+    row = sh.item(rowcnt++);
+    nr_of_cells = min (row.getLength (), rcol);
+    rightmcol = max (rightmcol, nr_of_cells);  # Keep track of max row length
+    # Read column (cell, "table-cell" in ODS speak) by column
+    jj = lcol; 
+    while (jj <= rcol)
+      tcell = row.getCellAt(jj-1);
+      form = 0;
+      if (~isempty (tcell))     # If empty it's possibly in columns-repeated/spanned
+        if (spsh_opts.formulas_as_text)   # Get spreadsheet formula rather than value
+          # Check for formula attribute
+          tmp = tcell.getTableFormulaAttribute ();
+          if isempty (tmp)
+            form = 0;
+          else
+            if (strcmp (tolower (tmp(1:3)), 'of:'))
+              tmp (1:end-3) = tmp(4:end);
+            endif
+            rawarr(ii-trow+2, jj-lcol+1) = tmp;
+            form = 1;
+          endif
+        endif
+        if ~(form || index (char(tcell), 'text:p>Err:') || index (char(tcell), 'text:p>#DIV'))  
+          # Get data from cell
+          ctype = tcell.getOfficeValueTypeAttribute ();
+          cvalue = tcell.getOfficeValueAttribute ();
+          switch deblank (ctype)
+            case  {'float', 'currency', 'percentage'}
+              rawarr(ii-trow+2, jj-lcol+1) = cvalue;
+            case 'date'
+              cvalue = tcell.getOfficeDateValueAttribute ();
+              # Dates are returned as octave datenums, i.e. 0-0-0000 based
+              yr = str2num (cvalue(1:4));
+              mo = str2num (cvalue(6:7));
+              dy = str2num (cvalue(9:10));
+              if (index (cvalue, 'T'))
+                hh = str2num (cvalue(12:13));
+                mm = str2num (cvalue(15:16));
+                ss = str2num (cvalue(18:19));
+                rawarr(ii-trow+2, jj-lcol+1) = datenum (yr, mo, dy, hh, mm, ss);
+              else
+                rawarr(ii-trow+2, jj-lcol+1) = datenum (yr, mo, dy);
+              endif
+            case 'time'
+              cvalue = tcell.getOfficeTimeValueAttribute ();
+              if (index (cvalue, 'PT'))
+                hh = str2num (cvalue(3:4));
+                mm = str2num (cvalue(6:7));
+                ss = str2num (cvalue(9:10));
+                rawarr(ii-trow+2, jj-lcol+1) = datenum (0, 0, 0, hh, mm, ss);
+              endif
+            case 'boolean'
+              cvalue = tcell.getOfficeBooleanValueAttribute ();
+              rawarr(ii-trow+2, jj-lcol+1) = cvalue; 
+            case 'string'
+              cvalue = tcell.getOfficeStringValueAttribute ();
+              if (isempty (cvalue))     # Happens with e.g., hyperlinks
+                tmp = char (tcell);
+                # Hack string value from between <text:p|r> </text:p|r> tags
+                ist = findstr (tmp, '<text:');
+                if (ist)
+                  ist = ist (length (ist));
+                  ist = ist + 8;
+                  ien = index (tmp(ist:end), '</text') + ist - 2;
+                  tmp (ist:ien);
+                  cvalue = tmp(ist:ien);
+                endif
+              endif
+              rawarr(ii-trow+2, jj-lcol+1)= cvalue;
+            otherwise
+              # Nothing
+          endswitch
+        endif
+      endif
+      ++jj;            # Next cell
+    endwhile
 
-		# Check for repeated rows (i.e. condensed in one table-row)
-		extrarows = row.getTableNumberRowsRepeatedAttribute () - 1;
-		if (extrarows > 0 && (ii + extrarows) < 65535)
-			# Expand rawarr cf. table-row
-			nr_of_rows = nr_of_rows + extrarows;
-			ii = ii + extrarows;
-		endif
-		++ii;
-	endwhile
+    # Check for repeated rows (i.e. condensed in one table-row)
+    extrarows = row.getTableNumberRowsRepeatedAttribute () - 1;
+    if (extrarows > 0 && (ii + extrarows) < 65535)
+      # Expand rawarr cf. table-row
+      nr_of_rows = nr_of_rows + extrarows;
+      ii = ii + extrarows;
+    endif
+    ++ii;
+  endwhile
 
-	# Keep track of data rectangle limits
-	ods.limits = [lcol, rcol; trow, brow];
+  # Keep track of data rectangle limits
+  ods.limits = [lcol, rcol; trow, brow];
 
 endfunction
 
 
 #===========================================================================
 
-## Copyright (C) 2010,2011 Philip Nienhuis <prnienhuis@users.sf.net>
+## Copyright (C) 2010,2011,2012 Philip Nienhuis <prnienhuis@users.sf.net>
 ##
 ## 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
@@ -447,146 +449,146 @@
 
 function [ rawarr, ods ] = ods3jotk2oct (ods, wsh, crange, spsh_opts)
 
-	# Get contents and table stuff from the workbook
-	odfcont = ods.workbook;		# Use a local copy just to be sure. octave 
-								# makes physical copies only when needed (?)
-	
-	# Parse sheets ("tables") from ODS file
-	sheets = ods.app.getTableList();
-	nr_of_sheets = sheets.size ();
+  # Get contents and table stuff from the workbook
+  odfcont = ods.workbook;  # Use a local copy just to be sure. octave 
+                           # makes physical copies only when needed (?)
+  
+  # Parse sheets ("tables") from ODS file
+  sheets = ods.app.getTableList();
+  nr_of_sheets = sheets.size ();
 
-	# Check user input & find sheet pointer (1-based)
-	if (~isnumeric (wsh))
-		try
-			sh = ods.app.getTableByName (wsh);
-			sh_err = isempty (sh);
-		catch
-			sh_err = 1;
-		end_try_catch
-		if (sh_err)
-			error (sprintf ("Sheet %s not found in file %s\n", wsh, ods.filename)); 
-		endif
-	elseif (wsh > nr_of_sheets || wsh < 1)
-		# We already have a numeric sheet pointer. If it's not in range:
-		error (sprintf ("Worksheet no. %d out of range (1 - %d)", wsh, nr_of_sheets));
-	else
-		sh = sheets.get (wsh - 1);
-	endif
+  # Check user input & find sheet pointer (1-based)
+  if (~isnumeric (wsh))
+    try
+      sh = ods.app.getTableByName (wsh);
+      sh_err = isempty (sh);
+    catch
+      sh_err = 1;
+    end_try_catch
+    if (sh_err)
+      error (sprintf ("Sheet %s not found in file %s\n", wsh, ods.filename)); 
+    endif
+  elseif (wsh > nr_of_sheets || wsh < 1)
+    # We already have a numeric sheet pointer. If it's not in range:
+    error (sprintf ("Worksheet no. %d out of range (1 - %d)", wsh, nr_of_sheets));
+  else
+    sh = sheets.get (wsh - 1);
+  endif
 
-	# Either parse (given cell range) or prepare (unknown range) help variables 
-	if (isempty (crange))
-		if ~isnumeric (wsh)
-			# Get sheet index
-			jj = nr_of_sheets;
-			while jj-- >= 0
-				if (strcmp (wsh, sheets.get(jj).getTableName()) == 1)
-					wsh = jj +1;
-					jj = -1;
-				endif
-			endwhile
-		endif
-		[ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
-		nrows = brow - trow + 1;	# Number of rows to be read
-		ncols = rcol - lcol + 1;	# Number of columns to be read
-	else
-		[dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
-		# Check ODS row/column limits
-		if (lcol > 1024 || trow > 65536) 
-			error ("ods2oct: invalid range; max 1024 columns & 65536 rows."); 
-		endif
-		# Truncate range silently if needed
-		rcol = min (lcol + ncols - 1, 1024);
-		ncols = min (ncols, 1024 - lcol + 1);
-		nrows = min (nrows, 65536 - trow + 1);
-		brow = trow + nrows - 1;
-	endif
+  # Either parse (given cell range) or prepare (unknown range) help variables 
+  if (isempty (crange))
+    if ~isnumeric (wsh)
+      # Get sheet index
+      jj = nr_of_sheets;
+      while jj-- >= 0
+        if (strcmp (wsh, sheets.get(jj).getTableName()) == 1)
+          wsh = jj +1;
+          jj = -1;
+        endif
+      endwhile
+    endif
+    [ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
+    nrows = brow - trow + 1;  # Number of rows to be read
+    ncols = rcol - lcol + 1;  # Number of columns to be read
+  else
+    [dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
+    # Check ODS row/column limits
+    if (lcol > 1024 || trow > 65536) 
+      error ("ods2oct: invalid range; max 1024 columns & 65536 rows."); 
+    endif
+    # Truncate range silently if needed
+    rcol = min (lcol + ncols - 1, 1024);
+    ncols = min (ncols, 1024 - lcol + 1);
+    nrows = min (nrows, 65536 - trow + 1);
+    brow = trow + nrows - 1;
+  endif
 
-	# Create storage for data content
-	rawarr = cell (nrows, ncols);
+  # Create storage for data content
+  rawarr = cell (nrows, ncols);
 
-	# Read from worksheet row by row. Row numbers are 0-based
-	for ii=trow:nrows+trow-1
-		row = sh.getRowByIndex (ii-1);
-		for jj=lcol:ncols+lcol-1;
-			ocell = row.getCellByIndex (jj-1);
-			if ~isempty (ocell)
-				otype = deblank (tolower (ocell.getValueType ()));
-   			if (spsh_opts.formulas_as_text)
-					if ~isempty (ocell.getFormula ())
-						otype = 'formula';
-					endif
-				endif
-#				# Provisions for catching jOpenDocument 1.2b bug where text cells
-#				# haven't been assigned an <office:value-type='string'> attribute
-#				if (~isempty (ocell))
-#					if (findstr ('<text:', char (ocell.getOdfElement ()))), otype = 'string'; endif
-#				endif
-				# At last, read the data
-				switch otype
-					case  {'float', 'currency', 'percentage'}
-						rawarr(ii-trow+1, jj-lcol+1) = ocell.getDoubleValue ();
-					case 'date'
-						# Dive into TableTable API
-						tvalue = ocell.getOdfElement ().getOfficeDateValueAttribute ();
-						# Dates are returned as octave datenums, i.e. 0-0-0000 based
-						yr = str2num (tvalue(1:4));
-						mo = str2num (tvalue(6:7));
-						dy = str2num (tvalue(9:10));
-						if (index (tvalue, 'T'))
-							hh = str2num (tvalue(12:13));
-							mm = str2num (tvalue(15:16));
-							ss = str2num (tvalue(18:19));
-							rawarr(ii-trow+1, jj-lcol+1) = datenum (yr, mo, dy, hh, mm, ss);
-						else
-							rawarr(ii-trow+1, jj-lcol+1) = datenum (yr, mo, dy);
-						endif
-					case 'time'
-						# Dive into TableTable API
-						tvalue = ocell.getOdfElement ().getOfficeTimeValueAttribute ();
-						if (index (tvalue, 'PT'))
-							hh = str2num (tvalue(3:4));
-							mm = str2num (tvalue(6:7));
-							ss = str2num (tvalue(9:10));
-							rawarr(ii-trow+1, jj-lcol+1) = datenum (0, 0, 0, hh, mm, ss);
-						endif
-					case 'boolean'
-						rawarr(ii-trow+1, jj-lcol+1) = ocell.getBooleanValue ();
-					case 'string'
-						rawarr(ii-trow+1, jj-lcol+1) = ocell.getStringValue ();
-#						# Code left in for in case odfdom 0.8.6+ has similar bug
-#						# as 0.7.5
-#						cvalue = tcell.getOfficeStringValueAttribute ();
-#						if (isempty (cvalue))     # Happens with e.g., hyperlinks
-#							tmp = char (tcell);
-#							# Hack string value from between <text:p|r> </text:p|r> tags
-#							ist = findstr (tmp, '<text:');
-#							if (ist)
-#								ist = ist (length (ist));
-#								ist = ist + 8;
-#								ien = index (tmp(ist:end), '</text') + ist - 2;
-#								tmp (ist:ien);
-#								cvalue = tmp(ist:ien);
-#							endif
-#						endif
-#						rawarr(ii-trow+1, jj-lcol+1)= cvalue;
-					case 'formula'
-						rawarr(ii-trow+1, jj-lcol+1) = ocell.getFormula ();
-					otherwise
-						# Nothing.
-				endswitch
-			endif
-		endfor
-	endfor
+  # Read from worksheet row by row. Row numbers are 0-based
+  for ii=trow:nrows+trow-1
+    row = sh.getRowByIndex (ii-1);
+    for jj=lcol:ncols+lcol-1;
+      ocell = row.getCellByIndex (jj-1);
+      if ~isempty (ocell)
+        otype = deblank (tolower (ocell.getValueType ()));
+         if (spsh_opts.formulas_as_text)
+          if ~isempty (ocell.getFormula ())
+            otype = 'formula';
+          endif
+        endif
+#        # Provisions for catching jOpenDocument 1.2b bug where text cells
+#        # haven't been assigned an <office:value-type='string'> attribute
+#        if (~isempty (ocell))
+#          if (findstr ('<text:', char (ocell.getOdfElement ()))), otype = 'string'; endif
+#        endif
+        # At last, read the data
+        switch otype
+          case  {'float', 'currency', 'percentage'}
+            rawarr(ii-trow+1, jj-lcol+1) = ocell.getDoubleValue ();
+          case 'date'
+            # Dive into TableTable API
+            tvalue = ocell.getOdfElement ().getOfficeDateValueAttribute ();
+            # Dates are returned as octave datenums, i.e. 0-0-0000 based
+            yr = str2num (tvalue(1:4));
+            mo = str2num (tvalue(6:7));
+            dy = str2num (tvalue(9:10));
+            if (index (tvalue, 'T'))
+              hh = str2num (tvalue(12:13));
+              mm = str2num (tvalue(15:16));
+              ss = str2num (tvalue(18:19));
+              rawarr(ii-trow+1, jj-lcol+1) = datenum (yr, mo, dy, hh, mm, ss);
+            else
+              rawarr(ii-trow+1, jj-lcol+1) = datenum (yr, mo, dy);
+            endif
+          case 'time'
+            # Dive into TableTable API
+            tvalue = ocell.getOdfElement ().getOfficeTimeValueAttribute ();
+            if (index (tvalue, 'PT'))
+              hh = str2num (tvalue(3:4));
+              mm = str2num (tvalue(6:7));
+              ss = str2num (tvalue(9:10));
+              rawarr(ii-trow+1, jj-lcol+1) = datenum (0, 0, 0, hh, mm, ss);
+            endif
+          case 'boolean'
+            rawarr(ii-trow+1, jj-lcol+1) = ocell.getBooleanValue ();
+          case 'string'
+            rawarr(ii-trow+1, jj-lcol+1) = ocell.getStringValue ();
+#            # Code left in for in case odfdom 0.8.6+ has similar bug
+#            # as 0.7.5
+#            cvalue = tcell.getOfficeStringValueAttribute ();
+#            if (isempty (cvalue))     # Happens with e.g., hyperlinks
+#              tmp = char (tcell);
+#              # Hack string value from between <text:p|r> </text:p|r> tags
+#              ist = findstr (tmp, '<text:');
+#              if (ist)
+#                ist = ist (length (ist));
+#                ist = ist + 8;
+#                ien = index (tmp(ist:end), '</text') + ist - 2;
+#                tmp (ist:ien);
+#                cvalue = tmp(ist:ien);
+#              endif
+#            endif
+#            rawarr(ii-trow+1, jj-lcol+1)= cvalue;
+          case 'formula'
+            rawarr(ii-trow+1, jj-lcol+1) = ocell.getFormula ();
+          otherwise
+            # Nothing.
+        endswitch
+      endif
+    endfor
+  endfor
 
-	# Keep track of data rectangle limits
-	ods.limits = [lcol, rcol; trow, brow];
+  # Keep track of data rectangle limits
+  ods.limits = [lcol, rcol; trow, brow];
 
 endfunction
 
 
 #===========================================================================
 
-## Copyright (C) 2009,2010,2011 Philip Nienhuis <pr.nienhuis at users.sf.net>
+## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <pr.nienhuis at users.sf.net>
 ##
 ## 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
@@ -622,156 +624,156 @@
 
 function [ rawarr, ods] = ods2jod2oct (ods, wsh, crange)
 
-	persistent months;
-	months = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
+  persistent months;
+  months = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
 
-	# Check jOpenDocument version
-	sh = ods.workbook.getSheet (0);
-	cl = sh.getCellAt (0, 0);
-	if (ods.odfvsn == 3)
-		# 1.2b3+ has public getValueType ()
-		persistent ctype;
-		if (isempty (ctype))
-			BOOLEAN    = char (java_get ('org.jopendocument.dom.ODValueType', 'BOOLEAN'));
-			CURRENCY   = char (java_get ('org.jopendocument.dom.ODValueType', 'CURRENCY'));
-			DATE       = char (java_get ('org.jopendocument.dom.ODValueType', 'DATE'));
-			FLOAT      = char (java_get ('org.jopendocument.dom.ODValueType', 'FLOAT'));
-			PERCENTAGE = char (java_get ('org.jopendocument.dom.ODValueType', 'PERCENTAGE'));
-			STRING     = char (java_get ('org.jopendocument.dom.ODValueType', 'STRING'));
-			TIME       = char (java_get ('org.jopendocument.dom.ODValueType', 'TIME'));
-		endif
-#	else
-#		# 1.2b2 has not
-#		ver = 2;
-	endif
+  # Check jOpenDocument version
+  sh = ods.workbook.getSheet (0);
+  cl = sh.getCellAt (0, 0);
+  if (ods.odfvsn == 3)
+    # 1.2b3+ has public getValueType ()
+    persistent ctype;
+    if (isempty (ctype))
+      BOOLEAN    = char (java_get ('org.jopendocument.dom.ODValueType', 'BOOLEAN'));
+      CURRENCY   = char (java_get ('org.jopendocument.dom.ODValueType', 'CURRENCY'));
+      DATE       = char (java_get ('org.jopendocument.dom.ODValueType', 'DATE'));
+      FLOAT      = char (java_get ('org.jopendocument.dom.ODValueType', 'FLOAT'));
+      PERCENTAGE = char (java_get ('org.jopendocument.dom.ODValueType', 'PERCENTAGE'));
+      STRING     = char (java_get ('org.jopendocument.dom.ODValueType', 'STRING'));
+      TIME       = char (java_get ('org.jopendocument.dom.ODValueType', 'TIME'));
+    endif
+#  else
+#    # 1.2b2 has not
+#    ver = 2;
+  endif
 
-	if (isnumeric (wsh)) wsh = wsh - 1; endif	 # Sheet INDEX starts at 0
-	# Check if sheet exists. If wsh = numeric, nonexistent sheets throw errors.
-	try
-		sh	= ods.workbook.getSheet (wsh);
-	catch
-		error ("Illegal sheet number (%d) requested for file %s\n", wsh+1, ods.filename);
-	end_try_catch
-	# If wsh = string, nonexistent sheets yield empty results
-	if (isempty (sh))
-		error ("No sheet called '%s' present in file %s\n", wsh, ods.filename);
-	endif
+  if (isnumeric (wsh)) wsh = wsh - 1; endif   # Sheet INDEX starts at 0
+  # Check if sheet exists. If wsh = numeric, nonexistent sheets throw errors.
+  try
+    sh  = ods.workbook.getSheet (wsh);
+  catch
+    error ("Illegal sheet number (%d) requested for file %s\n", wsh+1, ods.filename);
+  end_try_catch
+  # If wsh = string, nonexistent sheets yield empty results
+  if (isempty (sh))
+    error ("No sheet called '%s' present in file %s\n", wsh, ods.filename);
+  endif
 
-	# Either parse (given cell range) or prepare (unknown range) help variables 
-	if (isempty (crange))
-		if (ods.odfvsn < 3)
-			error ("No empty read range allowed in jOpenDocument version 1.2b2")
-		else
-			if (isnumeric (wsh)) wsh = wsh + 1; endif
-			[ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
-			nrows = brow - trow + 1;	# Number of rows to be read
-			ncols = rcol - lcol + 1;	# Number of columns to be read
-		endif
-	else
-		[dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
-		# Check ODS column limits
-		if (lcol > 1024 || trow > 65536) 
-			error ("ods2oct: invalid range; max 1024 columns & 65536 rows."); 
-		endif
-		# Truncate range silently if needed
-		rcol = min (lcol + ncols - 1, 1024);
-		ncols = min (ncols, 1024 - lcol + 1);
-		nrows = min (nrows, 65536 - trow + 1);
-		brow= trow + nrows - 1;
-	endif
-	# Create storage for data content
-	rawarr = cell (nrows, ncols);
+  # Either parse (given cell range) or prepare (unknown range) help variables 
+  if (isempty (crange))
+    if (ods.odfvsn < 3)
+      error ("No empty read range allowed in jOpenDocument version 1.2b2")
+    else
+      if (isnumeric (wsh)) wsh = wsh + 1; endif
+      [ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
+      nrows = brow - trow + 1;  # Number of rows to be read
+      ncols = rcol - lcol + 1;  # Number of columns to be read
+    endif
+  else
+    [dummy, nrows, ncols, trow, lcol] = parse_sp_range (crange);
+    # Check ODS column limits
+    if (lcol > 1024 || trow > 65536) 
+      error ("ods2oct: invalid range; max 1024 columns & 65536 rows."); 
+    endif
+    # Truncate range silently if needed
+    rcol = min (lcol + ncols - 1, 1024);
+    ncols = min (ncols, 1024 - lcol + 1);
+    nrows = min (nrows, 65536 - trow + 1);
+    brow= trow + nrows - 1;
+  endif
+  # Create storage for data content
+  rawarr = cell (nrows, ncols);
 
-	if (ods.odfvsn >= 3) 
-		# Version 1.2b3+
-		for ii=1:nrows
-			for jj = 1:ncols
-				try
-					scell = sh.getCellAt (lcol+jj-2, trow+ii-2);
-					sctype = char (scell.getValueType ());
-					switch sctype
-						case { FLOAT, CURRENCY, PERCENTAGE }
-							rawarr{ii, jj} = scell.getValue ().doubleValue ();
-						case BOOLEAN
-							rawarr {ii, jj} = scell.getValue () == 1;
-						case STRING
-							rawarr{ii, jj} = scell.getValue();
-						case DATE
-							tmp = strsplit (char (scell.getValue ()), ' ');
-							yy = str2num (tmp{6});
-							mo = find (ismember (months, toupper (tmp{2})) == 1);
-							dd = str2num (tmp{3});
-							hh = str2num (tmp{4}(1:2));
-							mi = str2num (tmp{4}(4:5));
-							ss = str2num (tmp{4}(7:8));
-							rawarr{ii, jj} = datenum (yy, mo, dd, hh, mi, ss);
-						case TIME
-							tmp = strsplit (char (scell.getValue ().getTime ()), ' ');
-							hh = str2num (tmp{4}(1:2)) /    24.0;
-							mi = str2num (tmp{4}(4:5)) /  1440.0;
-							ss = str2num (tmp{4}(7:8)) / 86600.0;
-							rawarr {ii, jj} = hh + mi + ss;
-						otherwise
+  if (ods.odfvsn >= 3) 
+    # Version 1.2b3+
+    for ii=1:nrows
+      for jj = 1:ncols
+        try
+          scell = sh.getCellAt (lcol+jj-2, trow+ii-2);
+          sctype = char (scell.getValueType ());
+          switch sctype
+            case { FLOAT, CURRENCY, PERCENTAGE }
+              rawarr{ii, jj} = scell.getValue ().doubleValue ();
+            case BOOLEAN
+              rawarr {ii, jj} = scell.getValue () == 1;
+            case STRING
+              rawarr{ii, jj} = scell.getValue();
+            case DATE
+              tmp = strsplit (char (scell.getValue ()), ' ');
+              yy = str2num (tmp{6});
+              mo = find (ismember (months, toupper (tmp{2})) == 1);
+              dd = str2num (tmp{3});
+              hh = str2num (tmp{4}(1:2));
+              mi = str2num (tmp{4}(4:5));
+              ss = str2num (tmp{4}(7:8));
+              rawarr{ii, jj} = datenum (yy, mo, dd, hh, mi, ss);
+            case TIME
+              tmp = strsplit (char (scell.getValue ().getTime ()), ' ');
+              hh = str2num (tmp{4}(1:2)) /    24.0;
+              mi = str2num (tmp{4}(4:5)) /  1440.0;
+              ss = str2num (tmp{4}(7:8)) / 86600.0;
+              rawarr {ii, jj} = hh + mi + ss;
+            otherwise
               # Workaround for sheets written by jOpenDocument (no value-type attrb):
               if (~isempty (scell.getValue) )
                 # FIXME Assume cell contains string if there's a text attr. But it could be BOOLEAN too...
                 if (findstr ('<text:', char (scell))), sctype = STRING; endif
                 rawarr{ii, jj} = scell.getValue();
               endif
-							# Nothing
-					endswitch
-				catch
-					# Probably a merged cell, just skip
-					# printf ("Error in row %d, col %d (addr. %s)\n", ii, jj, calccelladdress (lcol+jj-2, trow+ii-2));
-				end_try_catch
-			endfor
-		endfor
-	else	# ods,odfvsn == 3
-		# 1.2b2
-		for ii=1:nrows
-			for jj = 1:ncols
-				celladdress = calccelladdress (trow+ii-1, lcol+jj-1);
-				try
-					val = sh.getCellAt (celladdress).getValue ();
-				catch
-					# No panic, probably a merged cell
-					val = {};
-				end_try_catch
-				if (~isempty (val))
-					if (ischar (val))
-						# Text string
-						rawarr(ii, jj) = val;
-					elseif (isnumeric (val))
-						# Boolean
-						if (val) rawarr(ii, jj) = true; else; rawarr(ii, jj) = false; endif 
-					else
-						try
-							val = sh.getCellAt (celladdress).getValue ().doubleValue ();
-							rawarr(ii, jj) = val;
-						catch
-							val = char (val);
-							if (isempty (val))
-								# Probably empty Cell
-							else
-								# Maybe date / time value. Dirty hack to get values:
-								mo = strmatch (toupper (val(5:7)), months);
-								dd = str2num (val(9:10));
-								yy = str2num (val(25:end));
-								hh = str2num (val(12:13));
-								mm = str2num (val(15:16));
-								ss = str2num (val(18:19));
-								rawarr(ii, jj) = datenum (yy, mo, dd, hh, mm,ss);
-							endif
-						end_try_catch
-					endif
-				endif
-			endfor
-		endfor
+              # Nothing
+          endswitch
+        catch
+          # Probably a merged cell, just skip
+          # printf ("Error in row %d, col %d (addr. %s)\n", ii, jj, calccelladdress (lcol+jj-2, trow+ii-2));
+        end_try_catch
+      endfor
+    endfor
+  else  # ods.odfvsn == 3
+    # 1.2b2
+    for ii=1:nrows
+      for jj = 1:ncols
+        celladdress = calccelladdress (trow+ii-1, lcol+jj-1);
+        try
+          val = sh.getCellAt (celladdress).getValue ();
+        catch
+          # No panic, probably a merged cell
+          val = {};
+        end_try_catch
+        if (~isempty (val))
+          if (ischar (val))
+            # Text string
+            rawarr(ii, jj) = val;
+          elseif (isnumeric (val))
+            # Boolean
+            if (val) rawarr(ii, jj) = true; else; rawarr(ii, jj) = false; endif 
+          else
+            try
+              val = sh.getCellAt (celladdress).getValue ().doubleValue ();
+              rawarr(ii, jj) = val;
+            catch
+              val = char (val);
+              if (isempty (val))
+                # Probably empty Cell
+              else
+                # Maybe date / time value. Dirty hack to get values:
+                mo = strmatch (toupper (val(5:7)), months);
+                dd = str2num (val(9:10));
+                yy = str2num (val(25:end));
+                hh = str2num (val(12:13));
+                mm = str2num (val(15:16));
+                ss = str2num (val(18:19));
+                rawarr(ii, jj) = datenum (yy, mo, dd, hh, mm,ss);
+              endif
+            end_try_catch
+          endif
+        endif
+      endfor
+    endfor
 
-	endif	
+  endif  
 
-	# Keep track of data rectangle limits
-	ods.limits = [lcol, rcol; trow, brow];
+  # Keep track of data rectangle limits
+  ods.limits = [lcol, rcol; trow, brow];
 
 endfunction
 
@@ -813,7 +815,7 @@
 
   # Check sheet pointer
   if (isnumeric (wsh))
-	  if (wsh < 1 || wsh > numel (sh_names))
+    if (wsh < 1 || wsh > numel (sh_names))
       error ("Sheet index %d out of range 1-%d", wsh, numel (sh_names));
     endif
   else
@@ -822,7 +824,7 @@
     wsh = ii;
   endif
   unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XSpreadsheet');
-  sh = sheets.getByName(sh_names{wsh}).getObject.queryInterface (unotmp);
+  sh = sheets.getByName (sh_names{wsh}).getObject.queryInterface (unotmp);
 
   unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.sheet.XCellRangesQuery');
   xRQ = sh.queryInterface (unotmp);
@@ -848,8 +850,8 @@
   # out-of-range errors
   [ trow, brow, lcol, rcol ] = getusedrange (ods, wsh);
   if (isempty (datrange))
-    nrows = brow - trow + 1;	# Number of rows to be read
-    ncols = rcol - lcol + 1;	# Number of columns to be read
+    nrows = brow - trow + 1;  # Number of rows to be read
+    ncols = rcol - lcol + 1;  # Number of columns to be read
   else
     [dummy, nrows, ncols, srow, scol] = parse_sp_range (datrange);
     # Truncate range silently if needed
@@ -857,8 +859,8 @@
     rcol = min (scol + ncols - 1, rcol);
     trow = max (trow, srow);
     lcol = max (lcol, scol);
-    nrows = min (brow - trow + 1, nrows);	# Number of rows to be read
-    ncols = min (rcol - lcol + 1, ncols);	# Number of columns to be read
+    nrows = min (brow - trow + 1, nrows);  # Number of rows to be read
+    ncols = min (rcol - lcol + 1, ncols);  # Number of columns to be read
   endif
   # Create storage for data at Octave side
   rawarr = cell (nrows, ncols);
@@ -869,12 +871,12 @@
       XCell = sh.getCellByPosition (jj, ii);
       cType = XCell.getType().getValue ();
       switch cType
-        case 1	# Value
+        case 1  # Value
           rawarr{ii-trow+2, jj-lcol+2} = XCell.getValue ();
-        case 2	# String
+        case 2  # String
           unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.text.XText');
           rawarr{ii-trow+2, jj-lcol+2} = XCell.queryInterface (unotmp).getString ();
-        case 3	# Formula
+        case 3  # Formula
           if (spsh_opts.formulas_as_text)
             rawarr{ii-trow+2, jj-lcol+2} = XCell.getFormula ();
           else
--- a/main/io/inst/odsclose.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/odsclose.m	Fri Jun 08 15:28:27 2012 +0000
@@ -58,128 +58,129 @@
 ## 2010-10-17 Fixed typo in error message about unknown interface
 ## 2010-10-27 Improved file change tracking tru ods.changed
 ## 2010-11-12 Keep ods file pointer when write errors occur.
-##      "     Added optional filename arg to change filename to be written to
+##     ''     Added optional filename arg to change filename to be written to
 ## 2011-05-06 Experimental UNO support
 ## 2011-05-07 In case of UNO, soffice now properly closed using xDesk
 ## 2011-05-18 Saving newly created files using UNO supported now
 ## 2011-09-08 FIXME - closing OOo kills all other OOo invocations (known Java-UNO issue)
 ## 2012-01-26 Fixed "seealso" help string
+## 2012-06-08 tabs replaced by double space
 
 function [ ods ] = odsclose (ods, varargs)
 
-	# If needed warn that dangling spreadsheet pointers may be left
-	if (nargout < 1) warning ("return argument missing - ods invocation not reset."); endif
+  # If needed warn that dangling spreadsheet pointers may be left
+  if (nargout < 1) warning ("return argument missing - ods invocation not reset."); endif
 
-	force = 0;
+  force = 0;
 
-	if (nargin > 1)
-		for ii=2:nargin
-			if (strcmp (lower (varargin{ii}), "force"))
-				# Close .ods anyway even if write errors occur
-				force = 1;
-			elseif (~isempty (strfind (tolower (varargin{ii}), '.ods')) || ...
-					~isempty (strfind (tolower (varargin{ii}), '.sxc')))
-				# Apparently a file name
-				if (ods.changed == 0 || ods.changed > 2)
-					warning ("File %s wasn't changed, new filename ignored.", ods.filename);
-				else
-					if (strfind (tolower (filename), '.sxc') || strfind (tolower (filename), '.ods'))
-						ods.filename = filename;
-					else
-						error ('No .sxc or .ods filename extension specified');
-					endif
-				endif
-			endif
-		endfor
-	endif
+  if (nargin > 1)
+    for ii=2:nargin
+      if (strcmp (lower (varargin{ii}), "force"))
+        # Close .ods anyway even if write errors occur
+        force = 1;
+      elseif (~isempty (strfind (tolower (varargin{ii}), '.ods')) || ...
+          ~isempty (strfind (tolower (varargin{ii}), '.sxc')))
+        # Apparently a file name
+        if (ods.changed == 0 || ods.changed > 2)
+          warning ("File %s wasn't changed, new filename ignored.", ods.filename);
+        else
+          if (strfind (tolower (filename), '.sxc') || strfind (tolower (filename), '.ods'))
+            ods.filename = filename;
+          else
+            error ('No .sxc or .ods filename extension specified');
+          endif
+        endif
+      endif
+    endfor
+  endif
 
-	if (strcmp (ods.xtype, 'OTK'))
-		# Java & ODF toolkit
-		try
-			if (ods.changed && ods.changed < 3)
-				ods.app.save (ods.filename);
-				ods.changed = 0;
-			endif
-			ods.app.close ();
-		catch
-			if (force)
-				ods.app.close ();
-			endif
-		end_try_catch
+  if (strcmp (ods.xtype, 'OTK'))
+    # Java & ODF toolkit
+    try
+      if (ods.changed && ods.changed < 3)
+        ods.app.save (ods.filename);
+        ods.changed = 0;
+      endif
+      ods.app.close ();
+    catch
+      if (force)
+        ods.app.close ();
+      endif
+    end_try_catch
 
-	elseif (strcmp (ods.xtype, 'JOD'))
-		# Java & jOpenDocument
-		try
-			if (ods.changed && ods.changed < 3)
-				ofile = java_new ('java.io.File', ods.filename);
-				ods.workbook.saveAs (ofile);
-				ods.changed = 0;
-			endif
-		catch
-		end_try_catch
+  elseif (strcmp (ods.xtype, 'JOD'))
+    # Java & jOpenDocument
+    try
+      if (ods.changed && ods.changed < 3)
+        ofile = java_new ('java.io.File', ods.filename);
+        ods.workbook.saveAs (ofile);
+        ods.changed = 0;
+      endif
+    catch
+    end_try_catch
 
-	elseif (strcmp (ods.xtype, 'UNO'))
-		# Java & UNO bridge
-		try
-			if (ods.changed && ods.changed < 3)
-				# Workaround:
-				unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XModel');
-				xModel = ods.workbook.queryInterface (unotmp);
-				unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.util.XModifiable');
-				xModified = xModel.queryInterface (unotmp);
-				if (xModified.isModified ())
-					unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XStorable');	# isReadonly() ?  	
-					xStore = ods.app.xComp.queryInterface (unotmp);
-					if (ods.changed == 2)
-						# Some trickery as Octave Java cannot create non-numeric arrays
-						lProps = javaArray ('com.sun.star.beans.PropertyValue', 1);
-						lProp = java_new ('com.sun.star.beans.PropertyValue', "Overwrite", 0, true, []);
-						lProps(1) = lProp;
-						# OK, save file to disk
-						xStore.storeAsURL (ods.filename, lProps);
-					else
-						xStore.store ();
-					endif
-				endif
-			endif
-			ods.changed = -1;		# Needed for check op properly shutting down OOo
-			# Workaround:
-			unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XModel');
-			xModel = ods.app.xComp.queryInterface (unotmp);
-			unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.util.XCloseable');
-			xClosbl = xModel.queryInterface (unotmp);
-			xClosbl.close (true);
-			unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XDesktop');
-			xDesk = ods.app.aLoader.queryInterface (unotmp);
-			xDesk.terminate();
-			ods.changed = 0;
-		catch
-			if (force)
-				# Force closing OOo
-				unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XDesktop');
-				xDesk = ods.app.aLoader.queryInterface (unotmp);
-				xDesk.terminate();
-			else
-				warning ("Error dbclosing ods pointer (UNO)");
-			endif
-			return
-		end_try_catch
+  elseif (strcmp (ods.xtype, 'UNO'))
+    # Java & UNO bridge
+    try
+      if (ods.changed && ods.changed < 3)
+        # Workaround:
+        unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XModel');
+        xModel = ods.workbook.queryInterface (unotmp);
+        unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.util.XModifiable');
+        xModified = xModel.queryInterface (unotmp);
+        if (xModified.isModified ())
+          unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XStorable');  # isReadonly() ?    
+          xStore = ods.app.xComp.queryInterface (unotmp);
+          if (ods.changed == 2)
+            # Some trickery as Octave Java cannot create non-numeric arrays
+            lProps = javaArray ('com.sun.star.beans.PropertyValue', 1);
+            lProp = java_new ('com.sun.star.beans.PropertyValue', "Overwrite", 0, true, []);
+            lProps(1) = lProp;
+            # OK, save file to disk
+            xStore.storeAsURL (ods.filename, lProps);
+          else
+            xStore.store ();
+          endif
+        endif
+      endif
+      ods.changed = -1;    # Needed for check op properly shutting down OOo
+      # Workaround:
+      unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XModel');
+      xModel = ods.app.xComp.queryInterface (unotmp);
+      unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.util.XCloseable');
+      xClosbl = xModel.queryInterface (unotmp);
+      xClosbl.close (true);
+      unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XDesktop');
+      xDesk = ods.app.aLoader.queryInterface (unotmp);
+      xDesk.terminate();
+      ods.changed = 0;
+    catch
+      if (force)
+        # Force closing OOo
+        unotmp = java_new ('com.sun.star.uno.Type', 'com.sun.star.frame.XDesktop');
+        xDesk = ods.app.aLoader.queryInterface (unotmp);
+        xDesk.terminate();
+      else
+        warning ("Error dbclosing ods pointer (UNO)");
+      endif
+      return
+    end_try_catch
 
-#	elseif ---- < Other interfaces here >
+#  elseif ---- < Other interfaces here >
 
-	else
-		error (sprintf ("ods2close: unknown OpenOffice.org .ods interface - %s.", ods.xtype));
+  else
+    error (sprintf ("ods2close: unknown OpenOffice.org .ods interface - %s.", ods.xtype));
 
-	endif
+  endif
 
-	if (ods.changed && ods.changed < 3)
-		error ( sprintf ("Could not save file %s - read-only or in use elsewhere?\nFile pointer preserved", ods.filename));
-		if (force)
-			ods = [];
-		endif
-	else
-		# Reset file pointer
-		ods = [];
-	endif
+  if (ods.changed && ods.changed < 3)
+    error ( sprintf ("Could not save file %s - read-only or in use elsewhere?\nFile pointer preserved", ods.filename));
+    if (force)
+      ods = [];
+    endif
+  else
+    # Reset file pointer
+    ods = [];
+  endif
 
 endfunction
--- a/main/io/inst/odsfinfo.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/odsfinfo.m	Fri Jun 08 15:28:27 2012 +0000
@@ -76,6 +76,7 @@
 ## 2012-02-25 Return occupied sheet ranges in output args
 ##     ''     Improve echo of sheet names & ranges if interactive
 ## 2012-03-01 Fix wrong cell refs in UNO section ("(..)" rather than "{..}"
+## 2012-06-08 Support for odfdom-0.8.8-incubator
 
 function [ filetype, sheetnames ] = odsfinfo (filename, reqintf=[])
 
@@ -95,7 +96,7 @@
     if (strcmp (ods.xtype, 'OTK'))
       # Get contents and table (= sheet) stuff from the workbook
       odfcont = ods.workbook;    # Local copy just in case
-      if (strcmp (ods.odfvsn, '0.8.7'))
+      if (strcmp (ods.odfvsn, '0.8.7') || strfind (ods.odfvsn, "0.8.8"))
         xpath = ods.workbook.getXPath;
       else
         xpath = ods.app.getXPath;
--- a/main/io/inst/odsopen.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/odsopen.m	Fri Jun 08 15:28:27 2012 +0000
@@ -94,7 +94,7 @@
 ## 2012-06-06 Made interface checking routine less verbose when same requested interface
 ##            was used consecutively
 ##
-## Latest change on subfunctions below: 2012-06-06
+## Latest change on subfunctions below: 2012-06-08
 
 function [ ods ] = odsopen (filename, rw=0, reqinterface=[])
 
@@ -335,7 +335,7 @@
 endfunction
 
 
-## Copyright (C) 2009,2010,2011 Philip Nienhuis <prnienhuis at users.sf.net>
+## Copyright (C) 2009,2010,2011,2012 Philip Nienhuis <prnienhuis at users.sf.net>
 ## 
 ## 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
@@ -399,6 +399,7 @@
 ## 2011-09-18 Added temporary warning about UNO interface
 ## 2012-03-22 Improved Java checks (analogous to xlsopen)
 ## 2012-06-06 Again improved & simplified Java-based interface checking support
+## 2012-06-08 Support for odfdom-0.8.8 (-incubator)
 
 function [odsinterfaces] = getodsinterfaces (odsinterfaces)
 
@@ -472,8 +473,11 @@
       catch
         odfvsn = java_invoke ('org.odftoolkit.odfdom.Version', 'getApplicationVersion');
       end_try_catch
-      if ~(strcmp (odfvsn, '0.7.5') || strcmp (odfvsn, '0.8.6') || strcmp (odfvsn, '0.8.7'))
-        warning ("\nodfdom version %s is not supported - use v. 0.8.6 or 0.8.7.\n", odfvsn);
+      # For odfdom-incubator, strip extra info
+      odfvsn = regexp (odfvsn, '\d\.\d\.\d', "match"){1};
+      if ~(strcmp  (odfvsn, "0.7.5") || strcmp (odfvsn, "0.8.6") || strcmp (odfvsn, "0.8.7")
+        || strfind (odfvsn, "0.8.8"))
+        warning ("\nodfdom version %s is not supported - use v. 0.8.6 or later\n", odfvsn);
       else
         if (strcmp (odfvsn, '0.7.5'))
           warning ("odfdom v. 0.7.5 support won't be maintained - please upgrade to 0.8.6 or higher."); 
--- a/main/io/inst/odsread.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/odsread.m	Fri Jun 08 15:28:27 2012 +0000
@@ -118,30 +118,31 @@
 ## 2011-09-18 Return empty output arg in case of empty rawarr
 ## 2012-01-26 Fixed "seealso" help string
 ## 2012-03-07 Updated texinfo help text
+## 2012-06-08 Tabs replaced by double space
 
 function [ numarr, txtarr, rawarr, lim ] = odsread (filename, wsh=1, datrange=[], reqintf=[])
 
-	if (nargin < 1 || isempty (findstr ('.ods', tolower (filename))))
-		usage ("odsread: at least a filename incl. suffix is needed");
-	endif
-	if (nargout < 1)
-		usage ("odsread: no output argument(s) specified");
-	endif
+  if (nargin < 1 || isempty (findstr ('.ods', tolower (filename))))
+    usage ("odsread: at least a filename incl. suffix is needed");
+  endif
+  if (nargout < 1)
+    usage ("odsread: no output argument(s) specified");
+  endif
 
-	ods = odsopen (filename, 0, reqintf);
+  ods = odsopen (filename, 0, reqintf);
   
   if (~isempty (ods))
 
-  	[rawarr, ods, rstatus] = ods2oct (ods, wsh, datrange);
+    [rawarr, ods, rstatus] = ods2oct (ods, wsh, datrange);
 
-	  if (rstatus)
-		  [numarr, txtarr, lim] = parsecell (rawarr, ods.limits);
-	  else
-		  warning (sprintf ("No data read from %s.", filename));
+    if (rstatus)
+      [numarr, txtarr, lim] = parsecell (rawarr, ods.limits);
+    else
+      warning (sprintf ("No data read from %s.", filename));
       numarr = [];
-  	endif
-	
-	  ods = odsclose (ods);
+    endif
+  
+    ods = odsclose (ods);
 
   endif
 
--- a/main/io/inst/odswrite.m	Fri Jun 08 14:11:42 2012 +0000
+++ b/main/io/inst/odswrite.m	Fri Jun 08 15:28:27 2012 +0000
@@ -94,29 +94,30 @@
 ## 2012-01-26 Fixed "seealso" help string
 ## 2012-02-20 Fixed range parameter to be default empty string rather than empty numeral
 ## 2010-03-07 Updated texinfo help text
+## 2012-06-08 Tabs replaced by double space
 
 function [ rstatus ] = odswrite (filename, data, wsh=1, crange='', reqintf=[])
 
-	# Input validity checks
-	if (nargin < 2)
-		usage ("Insufficient arguments - see 'help odswrite'");
-	elseif (~ischar (filename) || isempty (findstr ('.ods', tolower (filename))))
-		error ("First argument must be a filename (incl. .ods suffix for OTK & JOD)");
-	endif
+  # Input validity checks
+  if (nargin < 2)
+    usage ("Insufficient arguments - see 'help odswrite'");
+  elseif (~ischar (filename) || isempty (findstr ('.ods', tolower (filename))))
+    error ("First argument must be a filename (incl. .ods suffix for OTK & JOD)");
+  endif
 
-	ods = odsopen (filename, 1, reqintf);
+  ods = odsopen (filename, 1, reqintf);
 
-	if (~isempty (ods)) 
-		[ods, rstatus] = oct2ods (data, ods, wsh, crange);
+  if (~isempty (ods)) 
+    [ods, rstatus] = oct2ods (data, ods, wsh, crange);
 
-		# If rstatus was not OK, reset change indicator in ods pointer
-		if (~rstatus)
-			ods.changed = rstatus;
-			warning ("odswrite: data transfer errors, file not rewritten");
-		endif
+    # If rstatus was not OK, reset change indicator in ods pointer
+    if (~rstatus)
+      ods.changed = rstatus;
+      warning ("odswrite: data transfer errors, file not rewritten");
+    endif
 
-		ods = odsclose (ods);
+    ods = odsclose (ods);
 
-	endif
+  endif
 
 endfunction