[libwps] 01/01: New upstream version 0.4.6

Rene Engelhard rene at moszumanska.debian.org
Mon Jun 5 10:56:24 UTC 2017


This is an automated email from the git hooks/post-receive script.

rene pushed a commit to branch upstream
in repository libwps.

commit 3de912f5708b96a49f829c12af78cc85c03233ed
Author: Rene Engelhard <rene at rene-engelhard.de>
Date:   Mon Jun 5 12:54:11 2017 +0200

    New upstream version 0.4.6
---
 ChangeLog                      |  75 ++++++++++++
 NEWS                           |   4 +
 configure                      |  26 ++---
 configure.ac                   |   2 +-
 inc/libwps/WPSDocument.h       |   2 +-
 src/lib/Lotus.cpp              | 256 +++++++++++++++++++++++++++++++++++++---
 src/lib/Lotus.h                |   9 +-
 src/lib/LotusGraph.cpp         |  16 +--
 src/lib/LotusSpreadsheet.cpp   | 257 +++++++++++++++++++++++++++--------------
 src/lib/LotusSpreadsheet.h     |   2 +-
 src/lib/LotusStyleManager.cpp  |  32 ++++-
 src/lib/QuattroSpreadsheet.cpp |  45 +++++---
 src/lib/WKS4.cpp               | 141 ++++++++++++++++++++--
 src/lib/WKS4.h                 |  10 +-
 src/lib/WKS4Spreadsheet.cpp    |  50 +++++---
 src/lib/WKS4Spreadsheet.h      |   2 +
 src/lib/WKSContentListener.cpp |  29 ++---
 src/lib/WKSContentListener.h   |   9 +-
 src/lib/WKSParser.h            |   4 +
 src/lib/WPSCell.cpp            |  30 ++++-
 src/lib/WPSCell.h              |  17 ++-
 src/lib/WPSDocument.cpp        |  24 +++-
 src/lib/WPSHeader.cpp          |   2 +-
 src/lib/WPSHeader.h            |  12 ++
 src/lib/WPSTable.cpp           |  59 ++++++++++
 src/lib/WPSTable.h             | 117 +++++++++++++++++++
 src/lib/libwps_internal.cpp    |  58 ++++++++++
 src/lib/libwps_internal.h      |  15 +++
 28 files changed, 1101 insertions(+), 204 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d80a5d9..1596d1c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,78 @@
+2017-03-04  osnola  <alonso at loria.fr>  [1d29e6f12d6928b2a39df41db5022c0f4ecdf4bb]
+
+tupdate some README's files...
+
+
+2017-03-03  osnola  <alonso at loria.fr>  [71b1309d0961d7d06b5064061a6e293e52663f2e]
+
+tcorrect some cppcheck and coverity warnings, ...
+
+
+2017-03-03  osnola  <alonso at loria.fr>  [edbf8d4b97d3b837fe850b25f0789919e49a2eb5]
+
+t Lotus .wk3,.wk4,.123: try also to retrieve password with length 13       or 14, small correction.
+
+
+2017-03-03  osnola  <alonso at loria.fr>  [9a2fc4978f882d09b687ec28520899a86fa35c81]
+
+tLotus .wk3,.wk4,.123: try also to retrieve password with length 13   or 14...
+
+
+2017-03-03  osnola  <alonso at loria.fr>  [6bd5cb55bf9dfccf331b8cb3e2558dc55244d492]
+
+tCorrect some compilers' warnings...
+
+
+2017-03-02  osnola  <alonso at loria.fr>  [7fec47b58e0847b613994e97815cef2949de6ba2]
+
+tLotus .wk3,.wk4,.123: add a function to retrieve small passwords   (ie. with less than 13 characters), called when a bad password   was given by the user...
+
+
+2017-03-02  osnola  <alonso at loria.fr>  [54bef8967115ce11740320e93a52423c2a1f8fb8]
+
+tLotus .123(SmartSuite 98): add support for password...
+
+
+2017-03-02  osnola  <alonso at loria.fr>  [5b56551409c4be08e338c6da72df064b3585d3e2]
+
+tLotus .123(SmartSuite 97): add support for password...
+
+
+2017-03-02  osnola  <alonso at loria.fr>  [efd8af05edebb43f6eb4fc8897961004c5cbb5ba]
+
+tLotus .wk[124]: add support for password, ...
+
+
+2017-03-01  osnola  <alonso at loria.fr>  [e94f72e01f3b5daead887352fc0fa92a7e25359f]
+
+tLotus .wk3: add support for password, ...
+
+
+2017-02-23  osnola  <alonso at loria.fr>  [402d40740b165efa05d227ff3dc5a96e3953c643]
+
+tLotus SmartSuite 9[6-8]: try to find more dupplicated cell, WPSCell.cpp: force cell's padding to 0pt.
+
+
+2017-02-23  osnola  <alonso at loria.fr>  [44eef88b2230ca42c24fc5365727f6012f97080d]
+
+tLotus SmartSuite 9[6-8]: try to retrieve some "merged" cells...
+
+
+2017-02-23  osnola  <alonso at loria.fr>  [d85a63302997a461bff9979d32415c08968a052d]
+
+tLotus SmartSuite 9[6-8]: better retrieving of column's width(and row's height).
+
+
+2017-02-22  osnola  <alonso at loria.fr>  [0a32c559959a751c69755998d9255c0e5d2960b7]
+
+tLotus SmartSuite 9[6-8]:improve the font's name, the cell's wrapping, the row's height retrieving...
+
+
+2017-01-09  osnola  <alonso at loria.fr>  [b9e9ceeffa6798044bbbfb39b5cb21fe2a592523]
+
+tconfigure.ac: update minor version...
+
+
 2017-01-05  osnola  <alonso at loria.fr>  [d5ebb0c062cfb3d7b200565f22ec3945b0e9b91f]
 
 tUpdate the README files...
diff --git a/NEWS b/NEWS
index 9155cc7..2966df0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+0.4.5 -> 0.4.6
+- Lotus 123: add support to read SmartSuite 98's files,
+- Lotus: allow to convert file with a password.
+
 0.4.4 -> 0.4.5
 - Lotus 123: add support to read SmartSuite 97's files,
 - implement LICS conversion correctly.
diff --git a/configure b/configure
index 49f450e..f4c9dbe 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for libwps 0.4.5.
+# Generated by GNU Autoconf 2.69 for libwps 0.4.6.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='libwps'
 PACKAGE_TARNAME='libwps'
-PACKAGE_VERSION='0.4.5'
-PACKAGE_STRING='libwps 0.4.5'
+PACKAGE_VERSION='0.4.6'
+PACKAGE_STRING='libwps 0.4.6'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1379,7 +1379,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures libwps 0.4.5 to adapt to many kinds of systems.
+\`configure' configures libwps 0.4.6 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1449,7 +1449,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of libwps 0.4.5:";;
+     short | recursive ) echo "Configuration of libwps 0.4.6:";;
    esac
   cat <<\_ACEOF
 
@@ -1592,7 +1592,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-libwps configure 0.4.5
+libwps configure 0.4.6
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1991,7 +1991,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by libwps $as_me 0.4.5, which was
+It was created by libwps $as_me 0.4.6, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2857,7 +2857,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='libwps'
- VERSION='0.4.5'
+ VERSION='0.4.6'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -17156,9 +17156,9 @@ WPS_MAJOR_VERSION=0
 
 WPS_MINOR_VERSION=4
 
-WPS_MICRO_VERSION=5
+WPS_MICRO_VERSION=6
 
-WPS_VERSION=0.4.5
+WPS_VERSION=0.4.6
 
 WPS_OBJDIR=$objdir
 
@@ -17167,7 +17167,7 @@ LT_CURRENT=`expr 100 '*' 0 + 4`
 # For 1.0.0 comment the first line and uncomment the second
 LT_AGE=0
 
-LT_REVISION=5
+LT_REVISION=6
 
 
 
@@ -18742,7 +18742,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by libwps $as_me 0.4.5, which was
+This file was extended by libwps $as_me 0.4.6, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -18808,7 +18808,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-libwps config.status 0.4.5
+libwps config.status 0.4.6
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 4d6a052..b2a2a80 100644
--- a/configure.ac
+++ b/configure.ac
@@ -7,7 +7,7 @@ AC_PREREQ([2.65])
 # ====================
 m4_define([libwps_version_major],[0])
 m4_define([libwps_version_minor],[4])
-m4_define([libwps_version_micro],[5])
+m4_define([libwps_version_micro],[6])
 m4_define([libwps_version],[libwps_version_major.libwps_version_minor.libwps_version_micro])
 
 # =============
diff --git a/inc/libwps/WPSDocument.h b/inc/libwps/WPSDocument.h
index bf681db..c8fee70 100644
--- a/inc/libwps/WPSDocument.h
+++ b/inc/libwps/WPSDocument.h
@@ -46,7 +46,7 @@ namespace libwps
 
 enum WPSConfidence { WPS_CONFIDENCE_NONE=0, WPS_CONFIDENCE_EXCELLENT, WPS_CONFIDENCE_SUPPORTED_ENCRYPTION };
 enum WPSCreator { WPS_MSWORKS=0 /**< Microsoft Works documents (all wps, wks and wdb) */,
-                  WPS_LOTUS /**Lotus DOS(Wk1), Apple(Lotus 123 v1), Windows(Wk3,Wk4) spreadsheets*/,
+                  WPS_LOTUS /**Lotus DOS(Wk1), Apple(Lotus 123 v1), Windows(Wk3,Wk4,123) spreadsheets*/,
                   WPS_QUATTRO_PRO /**Quattro Pro Wq1 and Wq2 spreadsheets(minimum filter)*/,
                   WPS_SYMPHONY /**Lotus Symphony files(untested, probably DOS documents)*/,
                   WPS_RESERVED_0, WPS_RESERVED_1, WPS_RESERVED_2,
diff --git a/src/lib/Lotus.cpp b/src/lib/Lotus.cpp
index 326bcd0..407606b 100644
--- a/src/lib/Lotus.cpp
+++ b/src/lib/Lotus.cpp
@@ -42,6 +42,7 @@
 #include "WPSOLE1Parser.h"
 #include "WPSPageSpan.h"
 #include "WPSStream.h"
+#include "WPSStringStream.h"
 
 #include "LotusGraph.h"
 #include "LotusSpreadsheet.h"
@@ -120,11 +121,12 @@ void SubDocument::parse(shared_ptr<WKSContentListener> &listener, libwps::SubDoc
 struct State
 {
 	//! constructor
-	explicit State(libwps_tools_win::Font::Type fontType) : m_fontType(fontType), m_version(-1),
+	State(libwps_tools_win::Font::Type fontType, char const *password) : m_fontType(fontType), m_version(-1),
 		m_inMainContentBlock(false), m_fontsMap(), m_pageSpan(), m_maxSheet(0),
 		m_actualZoneId(0), m_actualZoneParentId(0), m_sheetZoneIdList(), m_dataZoneIdToSheetZoneIdMap(),
 		m_actualLevels(), m_zone1Stack(), m_sheetSubZoneOpened(0x20, false), m_actPage(0), m_numPages(0),
-		m_headerString(""), m_footerString(""), m_metaData()
+		m_headerString(""), m_footerString(""), m_metaData(),
+		m_password(password), m_isEncrypted(false), m_isDecoded(false)
 	{
 	}
 	//! return the default font style
@@ -249,6 +251,12 @@ struct State
 	//! the metadata
 	librevenge::RVNGPropertyList m_metaData;
 
+	//! the password (if known)
+	char const *m_password;
+	//! true if the file is encrypted
+	bool m_isEncrypted;
+	//! true if the main stream has been decoded
+	bool m_isDecoded;
 private:
 	State(State const &);
 	State operator=(State const &);
@@ -258,11 +266,12 @@ private:
 
 // constructor, destructor
 LotusParser::LotusParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
-                         libwps_tools_win::Font::Type encoding) :
+                         libwps_tools_win::Font::Type encoding,
+                         char const *password) :
 	WKSParser(input, header), m_listener(), m_state(), m_styleManager(), m_graphParser(), m_spreadsheetParser(), m_ole1Parser()
 
 {
-	m_state.reset(new LotusParserInternal::State(encoding));
+	m_state.reset(new LotusParserInternal::State(encoding, password));
 	m_styleManager.reset(new LotusStyleManager(*this));
 	m_graphParser.reset(new LotusGraph(*this));
 	m_spreadsheetParser.reset(new LotusSpreadsheet(*this));
@@ -337,7 +346,7 @@ void LotusParser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
 		throw (libwps::ParseException());
 	}
 
-	if (!checkHeader(0L, true)) throw(libwps::ParseException());
+	if (!checkHeader(0L)) throw(libwps::ParseException());
 
 	bool ok=false;
 	try
@@ -363,6 +372,12 @@ void LotusParser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
 			ok = true;
 		}
 	}
+	catch (libwps::PasswordException())
+	{
+		ascii().reset();
+		WPS_DEBUG_MSG(("LotusParser::parse: password exception catched when parsing MN0\n"));
+		throw (libwps::PasswordException());
+	}
 	catch (...)
 	{
 		WPS_DEBUG_MSG(("LotusParser::parse: exception catched when parsing MN0\n"));
@@ -407,7 +422,7 @@ bool LotusParser::createListener(librevenge::RVNGSpreadsheetInterface *interface
 ////////////////////////////////////////////////////////////
 bool LotusParser::checkHeader(WPSHeader *header, bool strict)
 {
-	m_state.reset(new LotusParserInternal::State(m_state->m_fontType));
+	m_state.reset(new LotusParserInternal::State(m_state->m_fontType, m_state->m_password));
 	shared_ptr<WPSStream> mainStream(new WPSStream(getInput(), ascii()));
 	if (!checkHeader(mainStream, true, strict))
 		return false;
@@ -417,6 +432,7 @@ bool LotusParser::checkHeader(WPSHeader *header, bool strict)
 		header->setCreator(libwps::WPS_LOTUS);
 		header->setKind(libwps::WPS_SPREADSHEET);
 		header->setNeedEncoding(true);
+		header->setIsEncrypted(m_state->m_isEncrypted);
 	}
 	return true;
 }
@@ -458,19 +474,13 @@ bool LotusParser::checkHeader(shared_ptr<WPSStream> stream, bool mainStream, boo
 		}
 		f << "lotus123[FMT],";
 	}
-	else if (val>=0x1000 && val<=0x1003)
+	else if (val>=0x1000 && val<=0x1005)
 	{
 		WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus123 file\n"));
 		m_state->m_version=(val-0x1000)+1;
 		f << "lotus123[" << m_state->m_version << "],";
 	}
 #ifdef DEBUG
-	else if (val>0x1003 && val<=0x1005)
-	{
-		WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus123 file\n"));
-		m_state->m_version=(val-0x1000)+1;
-		f << "lotus123[" << m_state->m_version << "],";
-	}
 	else if (val==0x8007)
 	{
 		WPS_DEBUG_MSG(("LotusParser::checkHeader: find lotus file format, sorry parsing this file is only implemented for debugging, not output will be created\n"));
@@ -489,6 +499,7 @@ bool LotusParser::checkHeader(shared_ptr<WPSStream> stream, bool mainStream, boo
 		for (int i=0; i < 4; ++i)
 		{
 			if (!readZone(stream)) return false;
+			if (m_state->m_isEncrypted) break;
 		}
 	}
 	ascFile.addPos(0);
@@ -575,7 +586,11 @@ bool LotusParser::readZones(shared_ptr<WPSStream> stream)
 		if (input->isEnd())
 			break;
 
-		while (readZone(stream)) ;
+		while (readZone(stream))
+		{
+			if (m_state->m_isEncrypted && !m_state->m_isDecoded)
+				throw(libwps::PasswordException());
+		}
 
 		//
 		// look for ending
@@ -705,6 +720,78 @@ bool LotusParser::readZone(shared_ptr<WPSStream> stream)
 		case 0x1: // EOF
 			ok = false;
 			break;
+		case 0x2:
+			m_state->m_isEncrypted=true;
+			if (sz==16)
+			{
+				input->seek(pos+4, librevenge::RVNG_SEEK_SET);
+				std::vector<uint8_t> fileKeys;
+				for (int i=0; i<16; ++i)
+				{
+					unsigned char c(libwps::readU8(input));
+					fileKeys.push_back(uint8_t(c));
+				}
+				isParsed=needWriteInAscii=true;
+				if (!m_state->m_isDecoded)
+				{
+					static uint8_t const(defValues[])=
+					{
+						0xb9,0x5f, 0xd7,0x31, 0xdb,0x75, 9,0x72,
+						0x5d,0x85, 0x32,0x11, 0x5,0x11, 0x58,0
+					};
+					uint16_t key;
+					std::vector<uint8_t> keys;
+					if (m_state->m_password && libwps::encodeLotusPassword(m_state->m_password, key, keys, defValues))
+					{
+						bool passwordOk=fileKeys.size()==keys.size();
+						if (passwordOk)
+						{
+							/* check that the password is ok, normally,
+							   all keys must be equal excepted:
+							   - fileKey7=key7^(key>>8),
+							   - and fileKey13=key13^key.
+
+							   This also means that knowing fileKeys,
+							   it is possible to retrieve the password
+							   if it is short enough.
+							*/
+							int numSame=0;
+							for (size_t c=0; c < fileKeys.size(); ++c)
+							{
+								if (keys[c]==fileKeys[c])
+									++numSame;
+							}
+							passwordOk=numSame>=14;
+							if (!passwordOk)
+							{
+								WPS_DEBUG_MSG(("LotusParser::parse: the password seems bad\n"));
+							}
+						}
+						if (!passwordOk)
+						{
+							keys=retrievePasswordKeys(fileKeys);
+							passwordOk=keys.size()==16;
+						}
+						RVNGInputStreamPtr newInput;
+						if (passwordOk) newInput=decodeStream(input, stream->m_eof, keys);
+						if (newInput)
+						{
+							// let's replace the current input by the decoded input
+							m_state->m_isDecoded=true;
+							stream->m_input=newInput;
+							stream->m_ascii.setStream(newInput);
+						}
+					}
+				}
+			}
+			else
+			{
+				WPS_DEBUG_MSG(("LotusParser::parse: find unexpected password field\n"));
+				throw (libwps::PasswordException());
+			}
+			f.str("");
+			f << "Entries(Password):";
+			break;
 		case 0x3:
 			if (sz!=6)
 			{
@@ -1981,8 +2068,8 @@ bool LotusParser::readSheetZone(shared_ptr<WPSStream> stream)
 	case 0x84:
 	case 0x93:
 	case 0x94:
-	case 0x95:
-	case 0x96:
+	case 0x95: // column definition
+	case 0x96: // row definition
 	{
 		size_t subZId=size_t(id&0x1F);
 		f << "sheetC" << std::hex << subZId << std::dec << "[" << (m_state->m_sheetSubZoneOpened[subZId] ? "close" : "open") << "],";
@@ -2630,7 +2717,9 @@ bool LotusParser::readZone8(shared_ptr<WPSStream> stream)
 		input->seek(pos, librevenge::RVNG_SEEK_SET);
 		WPSVec3i minC, maxC;
 		m_state->getLevels(minC, maxC);
-		return m_spreadsheetParser->readCellsFormat801(stream, minC, maxC);
+		return m_spreadsheetParser->readCellsFormat801
+		       (stream, minC, maxC, m_state->m_sheetSubZoneOpened[0x15] ? 0 :
+		        m_state->m_sheetSubZoneOpened[0x16] ? 1 : -1);
 	}
 	if (libwps::readU8(input)!=8)
 	{
@@ -2681,7 +2770,10 @@ bool LotusParser::readZone8(shared_ptr<WPSStream> stream)
 	// 1 already done
 	case 2: // very often 802 and 803 are close to each other (in the sheet's zone)
 	case 3:
-		f << "zoneA" << id << ",";
+		if (id==2)
+			f << "column[def],";
+		else
+			f << "zoneA" << id << ",";
 		if (sz!=2)
 		{
 			WPS_DEBUG_MSG(("LotusParser::readZone8: the size seems bad for id=%d\n", id));
@@ -2694,6 +2786,8 @@ bool LotusParser::readZone8(shared_ptr<WPSStream> stream)
 	case 4:
 	{
 		f << "zoneA4,";
+		if (m_state->m_sheetSubZoneOpened[0x15]) f << "cols,";
+		else if (m_state->m_sheetSubZoneOpened[0x16]) f << "rows,";
 		if (sz<4)
 		{
 			WPS_DEBUG_MSG(("LotusParser::readZone8: the size seems bad for 804\n"));
@@ -3245,8 +3339,132 @@ bool LotusParser::readChartName(shared_ptr<WPSStream> stream)
 }
 
 ////////////////////////////////////////////////////////////
-//   Unknown
+//   decode
 ////////////////////////////////////////////////////////////
+RVNGInputStreamPtr LotusParser::decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key)
+{
+	if (!input || key.size()!=16)
+	{
+		WPS_DEBUG_MSG(("LotusParser::decodeStream: the arguments seems bad\n"));
+		return RVNGInputStreamPtr();
+	}
+	long actPos=input->tell();
+	input->seek(0,librevenge::RVNG_SEEK_SET);
+	librevenge::RVNGBinaryData data;
+	if (!libwps::readData(input, static_cast<unsigned long>(endPos), data) || long(data.size())!=endPos || !data.getDataBuffer())
+	{
+		WPS_DEBUG_MSG(("LotusParser::decodeStream: can not read the original input\n"));
+		return RVNGInputStreamPtr();
+	}
+	unsigned char *buf=const_cast<unsigned char *>(data.getDataBuffer());
+	input->seek(actPos,librevenge::RVNG_SEEK_SET);
+	uint8_t d7=0;
+	bool transform=true;
+	while (!input->isEnd())
+	{
+		long pos=input->tell();
+		if (pos+4>endPos) break;
+		int type=int(libwps::readU16(input));
+		int sSz=int(libwps::readU16(input));
+		if (pos+4+sSz>endPos)
+		{
+			input->seek(pos,librevenge::RVNG_SEEK_SET);
+			break;
+		}
+		//   Special case :
+		// 123 files:
+		//   - the style zone (between 0x10e and 0x10f) is not transformed
+		//   - the stack1[open|close] field are not transformed
+		if (type==0x10e)
+			transform=false;
+		else if (type==0x10f)
+			transform=true;
+		if (type==0x104 || type==0x105 || !transform)
+		{
+			input->seek(pos+4+sSz,librevenge::RVNG_SEEK_SET);
+			continue;
+		}
+		uint8_t d4=uint8_t(sSz);
+		uint8_t d5=key[13];
+		for (int i=0; i<sSz; ++i)
+		{
+			uint8_t c=uint8_t(libwps::readU8(input));
+			buf[pos+4+i]=(c^key[d7&0xf]);
+			d7=uint8_t(c+d4);
+			d4=uint8_t(d4+d5++);
+		}
+	}
+	if (input->tell()!=endPos)
+	{
+		WPS_DEBUG_MSG(("LotusParser::decodeStream: can not decode the end of the file, data may be bad %lx %lx\n", static_cast<unsigned long>(input->tell()), static_cast<unsigned long>(endPos)));
+	}
+	RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(endPos)));
+	res->seek(actPos, librevenge::RVNG_SEEK_SET);
+	return res;
+}
+
+std::vector<uint8_t> LotusParser::retrievePasswordKeys(std::vector<uint8_t> const &fileKeys)
+{
+	/* let try to detect short password (|password|<=14) by using the
+	   fact that fileKeys differ from the keys in two positions.
 
+	   If the password length is less or equal to 12:
+	   Using fileKeys[12] and fileKeys[14], we can "retrieve"
+	   the password length. Then knowing this length, fileKeys[14]
+	   and fileKeys[15] give us the key. Finally, we can retrieve the
+	   password and check if it gives us again fileKeys.
+
+	   We can also test password with length 13 or 14 similarly.
+
+	   Note: if |password|>14, we can detect it by testing 256*256 posibilities, but :-~
+	 */
+	std::vector<uint8_t> res;
+	if (fileKeys.size()!=16)
+	{
+		WPS_DEBUG_MSG(("LotusParser::retrievePasswordKeys: the file keys seems bad\n"));
+		return res;
+	}
+	static uint8_t const(defValues[])=
+	{
+		0xb9,0x5f, 0xd7,0x31, 0xdb,0x75, 9,0x72,
+		0x5d,0x85, 0x32,0x11, 0x5,0x11, 0x58,0
+	};
+	std::map<uint8_t,size_t> diffToPosMap;
+	for (size_t i=0; i<14; ++i)
+		diffToPosMap[defValues[i+2]^defValues[i]]=i;
+	uint8_t diff12=fileKeys[12]^fileKeys[14];
+	std::vector<size_t> posToTest;
+	if (diffToPosMap.find(diff12)!=diffToPosMap.end() && diffToPosMap.find(diff12)->second+2<14)
+	{
+		posToTest.push_back(diffToPosMap.find(diff12)->second+2);
+		// defValues[0]^defValues[2]=defValues[1]^defValues[3]=0x6e => we must add by hand this position
+		if (diff12==0x6e)
+			posToTest.push_back(2);
+	}
+	// check also password with length 13 or 14
+	posToTest.push_back(0);
+	posToTest.push_back(1);
+	for (size_t st=0; st<posToTest.size(); ++st)
+	{
+		size_t actPos=posToTest[st];
+		uint16_t key=uint16_t(((fileKeys[14]^defValues[actPos])<<8)|(fileKeys[15]^defValues[actPos+1]));
+		res=fileKeys;
+		res[7]=uint8_t(res[7]^key);
+		res[13]=uint8_t(res[13]^(key>>8));
+		// now build the password
+		std::string password;
+		for (size_t i=0; i<size_t(16-actPos-2); ++i)
+			password+=char(res[i]^(key>>((i%2)==0 ? 8 : 0)));
+		// check if the password is correct
+		uint16_t resKey;
+		std::vector<uint8_t> resKeys;
+		if (libwps::encodeLotusPassword(password.c_str(), resKey, resKeys, defValues) && key==resKey && res==resKeys)
+		{
+			WPS_DEBUG_MSG(("LotusParser::retrievePasswordKeys: Find password %s\n", password.c_str()));
+			return res;
+		}
+	}
+	return std::vector<uint8_t>();
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
diff --git a/src/lib/Lotus.h b/src/lib/Lotus.h
index 69610bb..96abd9e 100644
--- a/src/lib/Lotus.h
+++ b/src/lib/Lotus.h
@@ -72,7 +72,8 @@ class LotusParser : public WKSParser
 public:
 	//! constructor
 	LotusParser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
-	            libwps_tools_win::Font::Type encoding=libwps_tools_win::Font::UNKNOWN);
+	            libwps_tools_win::Font::Type encoding=libwps_tools_win::Font::UNKNOWN,
+	            char const *password=0);
 	//! destructor
 	~LotusParser();
 	//! called by WPSDocument to parse the file
@@ -174,8 +175,12 @@ protected:
 	//! reads the chart name or title
 	bool readChartName(shared_ptr<WPSStream> stream);
 
-	//////////////////////// unknown zone //////////////////////////////
+	//////////////////////// decode a stream //////////////////////////////
 
+	//! try to decode a stream, if successful, replace the stream'input by the new one
+	static RVNGInputStreamPtr decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key);
+	//! try to guess a password knowing its file keys. Returns the keys if it founds a valid password
+	static std::vector<uint8_t> retrievePasswordKeys(std::vector<uint8_t> const &fileKeys);
 
 	shared_ptr<WKSContentListener> m_listener; /** the listener (if set)*/
 	//! the internal state
diff --git a/src/lib/LotusGraph.cpp b/src/lib/LotusGraph.cpp
index c03f43e..f6571ab 100644
--- a/src/lib/LotusGraph.cpp
+++ b/src/lib/LotusGraph.cpp
@@ -334,7 +334,7 @@ struct ZonePc
 	//! the stream
 	shared_ptr<WPSStream> m_stream;
 	//! the bdbox
-	WPSBox2i m_box;
+	WPSBox2f m_box;
 	//! the translation
 	Vec2f m_translate;
 	//! the rotation
@@ -365,14 +365,14 @@ bool ZonePc::getGraphicShape(WPSGraphicShape &shape, WPSPosition &pos) const
 	case Line:
 	{
 		// we need to recompute the bdbox
-		int bounds[4]= {m_box[0][0],m_box[0][1],m_box[1][0],m_box[1][1]};
+		float bounds[4]= {m_box[0][0],m_box[0][1],m_box[1][0],m_box[1][1]};
 		for (int i=0; i<2; ++i)
 		{
 			if (bounds[i]<=bounds[i+2]) continue;
 			bounds[i]=bounds[i+2];
 			bounds[i+2]=m_box[0][i];
 		}
-		WPSBox2i realBox(Vec2i(bounds[0],bounds[1]),Vec2i(bounds[2],bounds[3]));
+		WPSBox2f realBox(Vec2f(bounds[0],bounds[1]),Vec2f(bounds[2],bounds[3]));
 		pos=WPSPosition(realBox[0],realBox.size(), librevenge::RVNG_POINT);
 		pos.setRelativePosition(WPSPosition::Page);
 		shape=WPSGraphicShape::line(m_box[0]-realBox[0], m_box[1]-realBox[0]);
@@ -2014,6 +2014,7 @@ bool LotusGraph::readGraphZone(shared_ptr<WPSStream> stream, int zId)
 	RVNGInputStreamPtr &input = stream->m_input;
 	libwps::DebugFile &ascFile=stream->m_ascii;
 	libwps::DebugStream f;
+	float const unit=version()>=5 ? 1.f/16.f : 1.f/256.f;
 	long pos = input->tell();
 	int id = (int) libwps::readU8(input);
 	if (libwps::readU8(input) != 3)
@@ -2322,7 +2323,7 @@ bool LotusGraph::readGraphZone(shared_ptr<WPSStream> stream, int zId)
 				f2 << "g" << i << "=" << std::hex << val << std::dec << ",";
 		}
 		float translate[2];
-		for (int i=0; i<2; ++i) translate[i]=float(libwps::read32(input))/256.f;
+		for (int i=0; i<2; ++i) translate[i]=unit*float(libwps::read32(input));
 		zone->m_translate=Vec2f(translate[0],translate[1]);
 		for (int i=0; i<2; ++i)
 		{
@@ -2347,7 +2348,7 @@ bool LotusGraph::readGraphZone(shared_ptr<WPSStream> stream, int zId)
 		else
 		{
 			float dim[4];
-			for (int i=0; i<4; ++i) dim[i]=float(libwps::read32(input))/256.f;
+			for (int i=0; i<4; ++i) dim[i]=unit*float(libwps::read32(input));
 			zone->m_box=WPSBox2f(Vec2f(dim[0],dim[1]),Vec2f(dim[2],dim[3]));
 			if (id==0x8b || id==0x8e || id==0x9a)
 			{
@@ -2443,6 +2444,7 @@ bool LotusGraph::readGraphDataZone(shared_ptr<WPSStream> stream, long endPos)
 	RVNGInputStreamPtr &input = stream->m_input;
 	libwps::DebugFile &ascFile=stream->m_ascii;
 	libwps::DebugStream f;
+	float const unit=version()>=5 ? 1.f/16.f : 1.f/256.f;
 	f << "Entries(GraphZone)[data]:";
 	long pos = input->tell();
 	int sz=int(endPos-pos);
@@ -2450,7 +2452,7 @@ bool LotusGraph::readGraphDataZone(shared_ptr<WPSStream> stream, long endPos)
 	{
 		f << "line,";
 		float dim[4];
-		for (int i=0; i<4; ++i) dim[i]=float(libwps::read32(input))/256.f;
+		for (int i=0; i<4; ++i) dim[i]=unit*float(libwps::read32(input));
 		m_state->m_actualZonePc->m_box= WPSBox2f(Vec2f(dim[0],dim[1]),Vec2f(dim[2],dim[3]));
 		f << "dim=" << m_state->m_actualZonePc->m_box << ",";
 	}
@@ -2461,7 +2463,7 @@ bool LotusGraph::readGraphDataZone(shared_ptr<WPSStream> stream, long endPos)
 		float dim[2];
 		for (int n=0; n<m_state->m_actualZonePc->m_numPoints; ++n)
 		{
-			for (int i=0; i<2; ++i) dim[i]=float(libwps::read32(input))/256.f;
+			for (int i=0; i<2; ++i) dim[i]=unit*float(libwps::read32(input));
 			m_state->m_actualZonePc->m_vertices.push_back(Vec2f(dim[0],dim[1]));
 			f << m_state->m_actualZonePc->m_vertices.back() << ",";
 		}
diff --git a/src/lib/LotusSpreadsheet.cpp b/src/lib/LotusSpreadsheet.cpp
index 9fb7bc3..7e609df 100644
--- a/src/lib/LotusSpreadsheet.cpp
+++ b/src/lib/LotusSpreadsheet.cpp
@@ -40,6 +40,7 @@
 #include "WPSFont.h"
 #include "WPSCell.h"
 #include "WPSStream.h"
+#include "WPSTable.h"
 
 #include "Lotus.h"
 #include "LotusStyleManager.h"
@@ -259,7 +260,7 @@ class Spreadsheet
 public:
 	//! a constructor
 	Spreadsheet() : m_name(""), m_numCols(0), m_numRows(0), m_boundsColsMap(),
-		m_widthColsInChar(), m_rowHeightMap(), m_heightDefault(16),
+		m_widthCols(), m_rowHeightMap(), m_heightDefault(16),
 		m_rowPageBreaksList(), m_positionToCellMap(), m_rowToStyleIdMap(),
 		m_rowToExtraStyleMap() {}
 	//! return a cell corresponding to a spreadsheet, create one if needed
@@ -274,92 +275,97 @@ public:
 		return m_positionToCellMap.find(pos)->second;
 	}
 	//! set the columns size
-	void setColumnWidthInChar(int col, float w=-1)
+	void setColumnWidth(int col, WPSColumnFormat const &format)
 	{
-		if (col < 0) return;
-		if (col >= int(m_widthColsInChar.size()))
+		if (col >= int(m_widthCols.size()))
 		{
 			// sanity check
-			if (col>255 && !m_boundsColsMap.empty() && col >= int(m_widthColsInChar.size())+10 &&
+			if (col>255 && !m_boundsColsMap.empty() && col >= int(m_widthCols.size())+10 &&
 			        m_boundsColsMap.find(col)==m_boundsColsMap.end())
 			{
 				WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Spreadsheet::setColumnWidth: the column %d seems bad\n", col));
 				return;
 			}
-			m_widthColsInChar.resize(size_t(col)+1, -1);
+			WPSColumnFormat defCol;
+			defCol.m_useOptimalWidth=true;
+			m_widthCols.resize(size_t(col)+1, defCol);
 		}
-		m_widthColsInChar[size_t(col)] = w;
+		m_widthCols[size_t(col)] = format;
 		if (col >= m_numCols) m_numCols=col+1;
 	}
 	//! returns the row size in point
-	float getRowHeight(int row) const
+	WPSRowFormat getRowHeight(int row) const
 	{
-		std::map<Vec2i,int>::const_iterator rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
+		std::map<Vec2i,WPSRowFormat>::const_iterator rIt=m_rowHeightMap.lower_bound(Vec2i(-1,row));
 		if (rIt!=m_rowHeightMap.end() && rIt->first[0]<=row && rIt->first[1]>=row)
-			return (float) rIt->second;
-		return (float) -m_heightDefault;
+			return rIt->second;
+		WPSRowFormat format(m_heightDefault);
+		format.m_isMinimalHeight=true;
+		return format;
 	}
 	//! set the rows size
-	void setRowHeight(int row, int h)
+	void setRowHeight(int row, WPSRowFormat const &format)
 	{
-		if (h>=0)
-			m_rowHeightMap[Vec2i(row,row)]=h;
+		m_rowHeightMap[Vec2i(row,row)]=format;
 	}
 	//! returns the position corresponding to a cell
 	Vec2f getPosition(Vec2i const &cell) const
 	{
 		// first compute the height
 		int lastRow=0;
-		int h=0;
-		std::map<Vec2i,int>::const_iterator rIt=m_rowHeightMap.begin();
+		float h=0;
+		std::map<Vec2i,WPSRowFormat>::const_iterator rIt=m_rowHeightMap.begin();
 		while (rIt!=m_rowHeightMap.end() && rIt->first[1]<cell[1])
 		{
 			if (rIt->first[0]>lastRow)
 			{
-				h+=(rIt->first[0]-lastRow)*m_heightDefault;
+				h+=float(rIt->first[0]-lastRow)*m_heightDefault;
 				lastRow=rIt->first[0];
 			}
-			h+=(rIt->first[1]+1-lastRow)*rIt->second;
+			float rHeight=rIt->second.m_height>=0 ? rIt->second.m_height : m_heightDefault;
+			h+=float(rIt->first[1]+1-lastRow)*rHeight;
 			lastRow=rIt->first[1]+1;
 			++rIt;
 		}
 		if (lastRow<cell[1])
 		{
-			if (rIt!=m_rowHeightMap.end() && rIt->first[0]<cell[1])
-				h+=(cell[1]-lastRow)*rIt->second;
+			if (rIt!=m_rowHeightMap.end() && rIt->first[0]<cell[1] && rIt->second.m_height>=0)
+				h+=float(cell[1]-lastRow)*rIt->second.m_height;
 			else
-				h+=(cell[1]-lastRow)*m_heightDefault;
+				h+=float(cell[1]-lastRow)*m_heightDefault;
 
 		}
 		// now compute the width
-		size_t const numCols = m_widthColsInChar.size();
+		size_t const numCols = m_widthCols.size();
 		float w=0;
 		for (size_t i = 0; i < numCols && i<size_t(cell[0]); i++)
-			w+=m_widthColsInChar[i]>=0 ? 8*m_widthColsInChar[i] : 72;
+			w+=m_widthCols[i].m_width>=0 ? m_widthCols[i].m_width : 72;
 		if (numCols<size_t(cell[0]))
 			w+=72*float(size_t(cell[0])-numCols);
-		return Vec2f(w, float(h));
+		return Vec2f(w, h);
 	}
 	//! try to compress the list of row height
 	void compressRowHeights()
 	{
-		std::map<Vec2i,int> oldMap=m_rowHeightMap;
+		std::map<Vec2i,WPSRowFormat> oldMap=m_rowHeightMap;
 		m_rowHeightMap.clear();
-		std::map<Vec2i,int>::const_iterator rIt=oldMap.begin();
-		int actHeight=-1;
+		std::map<Vec2i,WPSRowFormat>::const_iterator rIt=oldMap.begin();
+		WPSRowFormat actHeight;
+		WPSRowFormat defHeight(m_heightDefault);
+		defHeight.m_isMinimalHeight=true;
 		Vec2i actPos(0,-1);
 		while (rIt!=oldMap.end())
 		{
 			// first check for not filled row
 			if (rIt->first[0]!=actPos[1]+1)
 			{
-				if (actHeight==m_heightDefault)
+				if (actHeight==defHeight)
 					actPos[1]=rIt->first[0]-1;
 				else
 				{
 					if (actPos[1]>=actPos[0])
 						m_rowHeightMap[actPos]=actHeight;
-					actHeight=m_heightDefault;
+					actHeight=defHeight;
 					actPos=Vec2i(actPos[1]+1, rIt->first[0]-1);
 				}
 			}
@@ -377,31 +383,31 @@ public:
 			m_rowHeightMap[actPos]=actHeight;
 	}
 	//! convert the m_widthColsInChar in a vector of of point size
-	std::vector<float> getColumnWidths(std::vector<int> &repeated) const
+	std::vector<WPSColumnFormat> getWidths() const
 	{
-		size_t numElt = m_widthColsInChar.size();
-		std::vector<float> res;
-		size_t actCol=0;
-		float prevWidth=-1;
-		for (size_t i = 0; i < numElt; i++)
+		std::vector<WPSColumnFormat> widths;
+		WPSColumnFormat defWidth, actWidth;
+		defWidth.m_useOptimalWidth=true;
+		int repeat=0;
+		for (size_t c = 0; c < m_widthCols.size(); c++)
 		{
-			float newVal=m_widthColsInChar[i]>=0 ? m_widthColsInChar[i]*8.f : -1.f;
-			if (newVal<=prevWidth && newVal>=prevWidth)
-				continue;
-			if (actCol!=i)
+			WPSColumnFormat newWidth=m_widthCols[c];
+			if (repeat && newWidth!=actWidth)
 			{
-				res.push_back(prevWidth);
-				repeated.push_back(int(i-actCol));
+				actWidth.m_numRepeat=repeat;
+				widths.push_back(actWidth);
+				repeat=0;
 			}
-			actCol=i;
-			prevWidth=newVal;
+			if (repeat==0)
+				actWidth=newWidth;
+			++repeat;
 		}
-		if (actCol<numElt && prevWidth>=0)
+		if (repeat)
 		{
-			res.push_back(prevWidth);
-			repeated.push_back(int(numElt-actCol));
+			actWidth.m_numRepeat=repeat;
+			widths.push_back(actWidth);
 		}
-		return res;
+		return widths;
 	}
 	//! returns the row style id corresponding to a sheetId (or -1)
 	int getRowStyleId(int row) const
@@ -425,12 +431,12 @@ public:
 	int m_numRows;
 	/** a map used to stored the min/max row of each columns */
 	std::map<int, Vec2i> m_boundsColsMap;
-	/** the column size in char */
-	std::vector<float> m_widthColsInChar;
+	/** the column size */
+	std::vector<WPSColumnFormat> m_widthCols;
 	/** the map Vec2i(min row, max row) to size in points */
-	std::map<Vec2i,int> m_rowHeightMap;
+	std::map<Vec2i,WPSRowFormat> m_rowHeightMap;
 	/** the default row size in point */
-	int m_heightDefault;
+	float m_heightDefault;
 	/** the list of row page break */
 	std::vector<int> m_rowPageBreaksList;
 	/** a map cell to not empty cells */
@@ -448,7 +454,7 @@ public:
 struct Format123Style : public WPSCellFormat
 {
 	//! constructor
-	Format123Style()
+	Format123Style() : m_alignAcrossColumn(false)
 	{
 	}
 	//! update the cell style
@@ -458,6 +464,13 @@ struct Format123Style : public WPSCellFormat
 		style.setFormat(getFormat(), getSubFormat());
 		style.setDigits(digits());
 	}
+	//! operator==
+	bool operator==(Format123Style const &f) const
+	{
+		return m_alignAcrossColumn==f.m_alignAcrossColumn && compare(f)==0;
+	}
+	//! flag to know if we must align across column
+	bool m_alignAcrossColumn;
 };
 
 //! the extra style for lotus 123
@@ -478,6 +491,16 @@ struct Extra123Style
 		}
 		return true;
 	}
+	//! operator==
+	bool operator==(Extra123Style const &f) const
+	{
+		for (int i=0; i<2; ++i)
+		{
+			if (m_borders[i]!=f.m_borders[i])
+				return false;
+		}
+		return true;
+	}
 	//! update the cell style
 	void update(Style &style) const
 	{
@@ -508,7 +531,17 @@ struct Table123Styles
 		{
 			WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Table123Styles::addCellStyle: find dupplicated cell\n"));
 		}
-		map[cols]=cellId;
+		std::map<Vec2i,int>::iterator it=map.lower_bound(cols);
+		if (!map.empty() && it!=map.begin()) --it;
+		if (it!=map.end() && it->first[1]+1==cols[0] && it->second==cellId)
+		{
+			Vec2i newCols=it->first;
+			map.erase(newCols);
+			newCols[1]=cols[1];
+			map[newCols]=cellId;
+		}
+		else
+			map[cols]=cellId;
 	}
 	//! add a extra style to a list of cell
 	void addCellStyle(Vec2i const &cols, Vec2i const &rows, Extra123Style const &extra)
@@ -517,7 +550,17 @@ struct Table123Styles
 			m_rowsToColsToExtraStyleMap[rows]= std::map<Vec2i,Extra123Style>();
 		std::map<Vec2i,Extra123Style> &map=m_rowsToColsToExtraStyleMap.find(rows)->second;
 		// checkme: sometimes, we can retrieve the same cells again
-		map[cols]=extra;
+		std::map<Vec2i,Extra123Style>::iterator it=map.lower_bound(cols);
+		if (!map.empty() && it!=map.begin()) --it;
+		if (it!=map.end() && it->first[1]+1==cols[0] && it->second==extra)
+		{
+			Vec2i newCols=it->first;
+			map.erase(newCols);
+			newCols[1]=cols[1];
+			map[newCols]=extra;
+		}
+		else
+			map[cols]=extra;
 	}
 	//! add a extra style to a list of cell
 	void addCellStyle(Vec2i const &cols, Vec2i const &rows, Format123Style const &format)
@@ -529,7 +572,17 @@ struct Table123Styles
 		{
 			WPS_DEBUG_MSG(("LotusSpreadsheetInternal::Table123Styles::addCellStyle: find dupplicated cell\n"));
 		}
-		map[cols]=format;
+		std::map<Vec2i,Format123Style>::iterator it=map.lower_bound(cols);
+		if (!map.empty() && it!=map.begin()) --it;
+		if (it!=map.end() && it->first[1]+1==cols[0] && it->second==format)
+		{
+			Vec2i newCols=it->first;
+			map.erase(newCols);
+			newCols[1]=cols[1];
+			map[newCols]=format;
+		}
+		else
+			map[cols]=format;
 	}
 	//! the default cell style
 	int m_defaultCellId;
@@ -939,7 +992,7 @@ bool LotusSpreadsheet::readColumnSizes(shared_ptr<WPSStream> stream)
 	{
 		int col=(int)libwps::readU8(input);
 		int width=(int)libwps::readU8(input); // width in char, default 12...
-		sheet->setColumnWidthInChar(col, float(width));
+		sheet->setColumnWidth(col, WPSColumnFormat(float(7*width)));
 		f << width << "C:col" << col << ",";
 	}
 	f << "],";
@@ -1282,7 +1335,7 @@ bool LotusSpreadsheet::readRowFormat(shared_ptr<WPSStream> stream, LotusSpreadsh
 	return true;
 }
 
-bool LotusSpreadsheet::readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i const &minC, WPSVec3i const &maxC)
+bool LotusSpreadsheet::readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i const &minC, WPSVec3i const &maxC, int subZoneId)
 {
 	if (!stream) return false;
 	RVNGInputStreamPtr &input=stream->m_input;
@@ -1440,7 +1493,11 @@ bool LotusSpreadsheet::readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i
 
 		if (values[1]&1) f << "neg[value,red],";
 		if (values[1]&2) f << "add[parenthesis],";
-		if (values[1]&0x10) f << "align[accross,column],";
+		if (values[1]&0x10)
+		{
+			format.m_alignAcrossColumn=true;
+			f << "align[across,column],";
+		}
 		if (values[1]&0x20) f << "hidden,";
 		values[1] &=0xCC;
 		for (int i=1; i<4; ++i)
@@ -1517,10 +1574,16 @@ bool LotusSpreadsheet::readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i
 	}
 	case 12:   // column data, in general the fourst one
 	{
+		if (subZoneId==0) f << "col,";
+		else if (subZoneId==1) f << "row,";
+		else if (subZoneId!=-1)
+		{
+			WPS_DEBUG_MSG(("LotusSpreadsheet::readCellsFormat801: the zone8 id seems bad\n"));
+			f << "###zone15=" << subZoneId << ",";
+		}
 		int width=-1;
 		bool isDefault=false;
-		bool setWidth=true;
-		bool isColumn=false;
+		bool isWidthDef=true;
 		f << "colUnkn=[";
 		for (int i=0; i<7; ++i)   // f1=[01][04][01], f2: big number, f3=0|1, f4=0|2d|3c|240
 		{
@@ -1531,31 +1594,25 @@ bool LotusSpreadsheet::readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i
 				if ((val&1)==0)
 				{
 					isDefault=true;
-					input->seek(endPos, librevenge::RVNG_SEEK_SET);
-					break;
+					f << "no[w],";
 				}
 				if (val&2) f << "hidden,";
 				if (val&0x20) f << "page[break],";
 				if (val&0x40)
 				{
-					setWidth=false;
+					isWidthDef=false;
 					f << "w[def],";
 				}
 				if (val&0x100)
-					f << "row,";
-				else
-				{
-					f << "col,";
-					isColumn=true;
-				}
-				val &= 0xFE9D;
-				if (val!=1)
+					f << "fl100,";
+				val &= 0xFE9C;
+				if (val)
 					f << "##fl=" << std::hex << val << std::dec << ",";
 			}
 			else if (i==3)
 			{
 				width=val;
-				if (setWidth)
+				if (!isDefault)
 					f << "w=" << width << ",";
 			}
 			else if (val)
@@ -1564,16 +1621,24 @@ bool LotusSpreadsheet::readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i
 				f << "_,";
 		}
 		f << "],";
-		if (isDefault || minC[1]<0 || width<0 || !setWidth) break;
+		if (isDefault || minC[1]<0 || width<0) break;
+		if (subZoneId<0 || subZoneId>1) break;
 		for (int i=minC[0]; i<=maxC[0]; ++i)
 		{
 			LotusSpreadsheetInternal::Spreadsheet &sheet=m_state->getSheet(i);
 			for (int c=minC[1]; c<=maxC[1]; ++c)
 			{
-				if (isColumn)
-					sheet.setColumnWidthInChar(c, vers>=5 ? float(width)/128.f : float(width)/8.f); // checkme
+				if (subZoneId==0)
+				{
+					WPSColumnFormat format(!isWidthDef ? 72 : vers>=5 ? float(width)/16.f : float(width));
+					sheet.setColumnWidth(c, format);
+				}
 				else
-					sheet.setRowHeight(c, int(width+0.5));
+				{
+					WPSRowFormat format(vers>=5 ? float(width)/16.f : float(width));
+					format.m_useOptimalHeight=!isWidthDef;
+					sheet.setRowHeight(c, format);
+				}
 			}
 		}
 		break;
@@ -1669,7 +1734,7 @@ bool LotusSpreadsheet::readRowSizes(shared_ptr<WPSStream> stream, long endPos)
 		if (val!=0xFFFF)
 		{
 			f << "dim=" << float(val+31)/32.f << ",";
-			sheet->setRowHeight(row, int((val+31)/32));
+			sheet->setRowHeight(row, WPSRowFormat(float(val+31)/32.f));
 		}
 		for (int j=0; j<2; ++j)
 		{
@@ -1817,7 +1882,7 @@ bool LotusSpreadsheet::readExtraRowFormats(shared_ptr<WPSStream> stream)
 
 	LotusSpreadsheetInternal::Spreadsheet &sheet=m_state->getSheet(m_state->m_sheetCurrentId);
 	int val=int(libwps::readU8(input));
-	sheet.setRowHeight(row, val);
+	sheet.setRowHeight(row, WPSRowFormat(float(val)));
 	if (val!=14) f << "height=" << val << ",";
 	val=int(libwps::readU8(input)); // 10|80
 	if (val) f << "f0=" << std::hex << val << std::dec << ",";
@@ -2292,10 +2357,7 @@ void LotusSpreadsheet::sendSpreadsheet(int sheetId)
 		return;
 	}
 	LotusSpreadsheetInternal::Spreadsheet &sheet = m_state->getSheet(sheetId);
-	std::vector<int> repeated;
-	std::vector<float> colWidths=sheet.getColumnWidths(repeated);
-	m_listener->openSheet(colWidths, librevenge::RVNG_POINT,
-	                      repeated, m_state->getSheetName(sheetId));
+	m_listener->openSheet(sheet.getWidths(), m_state->getSheetName(sheetId));
 	m_mainParser.sendGraphics(sheetId);
 	sheet.compressRowHeights();
 	/* create a set to know which row needed to be send, each value of
@@ -2327,7 +2389,7 @@ void LotusSpreadsheet::sendSpreadsheet(int sheetId)
 		newRowSet.insert(rows[0]);
 		newRowSet.insert(rows[1]+1);
 	}
-	for (std::map<Vec2i,int>::const_iterator rIt=sheet.m_rowHeightMap.begin();
+	for (std::map<Vec2i,WPSRowFormat>::const_iterator rIt=sheet.m_rowHeightMap.begin();
 	        rIt!=sheet.m_rowHeightMap.end(); ++rIt)
 	{
 		Vec2i rows=rIt->first;
@@ -2377,7 +2439,7 @@ void LotusSpreadsheet::sendSpreadsheet(int sheetId)
 		}
 		if (sIt==newRowSet.end())
 			break;
-		m_listener->openSheetRow(sheet.getRowHeight(row), librevenge::RVNG_POINT, false, *sIt-row);
+		m_listener->openSheetRow(sheet.getRowHeight(row), *sIt-row);
 		sendRowContent(sheet, row, table123Styles);
 		m_listener->closeSheetRow();
 	}
@@ -2456,6 +2518,8 @@ void LotusSpreadsheet::sendRowContent(LotusSpreadsheetInternal::Spreadsheet cons
 	std::map<Vec2i, LotusSpreadsheetInternal::Extra123Style>::const_iterator e123It;
 	std::map<Vec2i, LotusSpreadsheetInternal::Format123Style> colToFormatStyleMap;
 	std::map<Vec2i, LotusSpreadsheetInternal::Format123Style>::const_iterator f123It;
+
+	std::map<int,int> potentialMergeMap;
 	if (table123Styles)
 	{
 		for (std::map<Vec2i, std::map<Vec2i,int> >::const_iterator rIt=table123Styles->m_rowsToColsToCellIdMap.begin();
@@ -2494,6 +2558,8 @@ void LotusSpreadsheet::sendRowContent(LotusSpreadsheetInternal::Spreadsheet cons
 				colToFormatStyleMap.insert(std::map<Vec2i, LotusSpreadsheetInternal::Format123Style>::value_type(colIt->first,colIt->second));
 				newColSet.insert(colIt->first[0]);
 				newColSet.insert(colIt->first[1]+1);
+				if (colIt->first[0]!=colIt->first[1] && colIt->second.m_alignAcrossColumn)
+					potentialMergeMap[colIt->first[0]]=colIt->first[1]+1;
 			}
 		}
 		c123It=colToCellIdMap.begin();
@@ -2593,7 +2659,28 @@ void LotusSpreadsheet::sendRowContent(LotusSpreadsheetInternal::Spreadsheet cons
 		}
 		if (!hasCell && !hasStyle) continue;
 		if (!hasCell) emptyCell.setPosition(Vec2i(col, row));
-		sendCellContent(hasCell ? cIt->second : emptyCell, style, endCol-col);
+		bool canMerge=false;
+		if (hasCell && potentialMergeMap.find(col)!=potentialMergeMap.end())
+		{
+			int newEndCol=potentialMergeMap.find(col)->second;
+			std::map<Vec2i, LotusSpreadsheetInternal::Cell>::const_iterator newCIt=cIt;
+			if (newCIt!=sheet.m_positionToCellMap.end()) ++newCIt;
+			canMerge=(newCIt==sheet.m_positionToCellMap.end() || newCIt->first[1]!=row || newCIt->first[0]>=newEndCol);
+		}
+		if (canMerge)
+		{
+			int newEndCol=potentialMergeMap.find(col)->second;
+			while (colIt!=newColSet.end() && *colIt<newEndCol)
+			{
+				++colIt;
+				continue;
+			}
+			LotusSpreadsheetInternal::Cell cell=cIt->second;
+			cell.setNumSpannedCells(Vec2i(newEndCol-col,1));
+			sendCellContent(cell, style, 1);
+		}
+		else
+			sendCellContent(hasCell ? cIt->second : emptyCell, style, endCol-col);
 	}
 }
 
diff --git a/src/lib/LotusSpreadsheet.h b/src/lib/LotusSpreadsheet.h
index 7dd7ec5..9fd437a 100644
--- a/src/lib/LotusSpreadsheet.h
+++ b/src/lib/LotusSpreadsheet.h
@@ -109,7 +109,7 @@ protected:
 	bool readSheetName1B(shared_ptr<WPSStream> stream, long endPos);
 
 	//! reads a cell zone formats: zone 801, lotus 123
-	bool readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i const &minC, WPSVec3i const &maxC);
+	bool readCellsFormat801(shared_ptr<WPSStream> stream, WPSVec3i const &minC, WPSVec3i const &maxC, int typeZone);
 	//! reads the columns definitions
 	bool readColumnDefinition(shared_ptr<WPSStream> stream);
 	//! reads the column sizes ( in char )
diff --git a/src/lib/LotusStyleManager.cpp b/src/lib/LotusStyleManager.cpp
index bfc1766..10329ae 100644
--- a/src/lib/LotusStyleManager.cpp
+++ b/src/lib/LotusStyleManager.cpp
@@ -183,7 +183,7 @@ struct CellStyle
 	explicit CellStyle(libwps_tools_win::Font::Type fontType) :
 		m_borders(0), m_fontId(0), m_formatId(0),
 		m_colorStyle(), m_fontStyle(fontType),
-		m_hAlign(WPSCellFormat::HALIGN_DEFAULT), m_vAlign(WPSCellFormat::VALIGN_DEFAULT),
+		m_hAlign(WPSCellFormat::HALIGN_DEFAULT), m_vAlign(WPSCellFormat::VALIGN_DEFAULT), m_wrapping(WPSCellFormat::WRAP_DEFAULT),
 		m_rotation(0), m_extra("")
 	{
 		for (int i=0; i<4; ++i)
@@ -243,6 +243,18 @@ struct CellStyle
 		default:
 			break; // default
 		}
+		switch (cell.m_wrapping)
+		{
+		case WPSCellFormat::WRAP_WRAP:
+			o << "wrap,";
+			break;
+		case WPSCellFormat::WRAP_NO_WRAP:
+			o << "wrap[no],";
+			break;
+		case WPSCellFormat::WRAP_DEFAULT:
+		default:
+			break;
+		}
 		if (cell.m_borders)
 		{
 			o << "bord=";
@@ -278,6 +290,8 @@ struct CellStyle
 	WPSCellFormat::HorizontalAlignment m_hAlign;
 	//! the vertical align
 	WPSCellFormat::VerticalAlignment m_vAlign;
+	//! the wrapping
+	WPSCellFormat::Wrapping m_wrapping;
 	//! the rotation
 	int m_rotation;
 	//! the cell border
@@ -1368,7 +1382,13 @@ bool LotusStyleManager::readCellStyleD2Data(LotusStyleManagerInternal::CellStyle
 	val=int(libwps::readU8(input));
 	if (val!=255) f << "f0=" << val << ";";
 	val=int(libwps::readU8(input));
-	if (val!=255) font.m_fontId=val;
+	if (val!=255)
+	{
+		font.m_fontId=val;
+		WPSFont defFont;
+		if (m_mainParser.getFont(font.m_fontId, defFont, font.m_fontType))
+			font.m_font.m_name = defFont.m_name;
+	}
 	val=int(libwps::readU16(input)); // checkme what is this unit? 1de:18, 173: 14, 140: 12?, 778:72
 	if (val!=0xFFFF)
 		font.m_font.m_size=double(int(double(val)*3./80.+0.5));
@@ -1499,7 +1519,11 @@ bool LotusStyleManager::readCellStyleD2Data(LotusStyleManagerInternal::CellStyle
 				cell.m_vAlign=WPSCellFormat::VALIGN_BOTTOM;
 				break;
 			}
-			if (!(val&0x80)) f << "wrap[text],";
+			if (!(val&0x80))
+			{
+				f << "wrap[text],";
+				cell.m_wrapping=WPSCellFormat::WRAP_WRAP;
+			}
 			val&=0x7C;
 			if (val) f << "#vAlign=" << std::hex << val << std::dec << ",";
 			break;
@@ -2005,6 +2029,8 @@ bool LotusStyleManager::updateCellStyle(int cellId, WPSCellFormat &format,
 		format.setHAlignement(cellStyle.m_hAlign);
 	if (cellStyle.m_vAlign!=WPSCellFormat::VALIGN_DEFAULT)
 		format.setVAlignement(cellStyle.m_vAlign);
+	if (cellStyle.m_wrapping!=WPSCellFormat::WRAP_DEFAULT)
+		format.setWrapping(cellStyle.m_wrapping);
 	// wk3
 	if (cellStyle.m_fontId>=0)
 	{
diff --git a/src/lib/QuattroSpreadsheet.cpp b/src/lib/QuattroSpreadsheet.cpp
index a0d55b2..aaf7f55 100644
--- a/src/lib/QuattroSpreadsheet.cpp
+++ b/src/lib/QuattroSpreadsheet.cpp
@@ -36,6 +36,7 @@
 #include "WKSContentListener.h"
 #include "WPSEntry.h"
 #include "WPSFont.h"
+#include "WPSTable.h"
 
 #include "Quattro.h"
 
@@ -313,19 +314,36 @@ public:
 		if (col >= m_numCols) m_numCols=col+1;
 	}
 
-	//! convert the m_widthCols in a vector of of point size
-	static std::vector<float> convertInPoint(std::vector<int> const &list,
-	                                         float defSize)
+	//! return the columns format
+	std::vector<WPSColumnFormat> getWidths(float defSize=76) const
 	{
-		size_t numElt = list.size();
-		std::vector<float> res;
-		res.resize(numElt);
-		for (size_t i = 0; i < numElt; i++)
+		std::vector<WPSColumnFormat> widths;
+		WPSColumnFormat defWidth(defSize), actWidth;
+		defWidth.m_useOptimalWidth=true;
+		int repeat=0;
+		for (size_t c = 0; c < m_widthCols.size(); c++)
 		{
-			if (list[i] < 0) res[i] = defSize;
-			else res[i] = float(list[i])/20.f;
+			WPSColumnFormat newWidth;
+			if (m_widthCols[c] < 0)
+				newWidth=defWidth;
+			else
+				newWidth=WPSColumnFormat(float(m_widthCols[c])/20.f);
+			if (repeat && newWidth!=actWidth)
+			{
+				actWidth.m_numRepeat=repeat;
+				widths.push_back(actWidth);
+				repeat=0;
+			}
+			if (repeat==0)
+				actWidth=newWidth;
+			++repeat;
+		}
+		if (repeat)
+		{
+			actWidth.m_numRepeat=repeat;
+			widths.push_back(actWidth);
 		}
-		return res;
+		return widths;
 	}
 	//! returns the row size in point
 	float getRowHeight(int row) const
@@ -2158,8 +2176,7 @@ void QuattroSpreadsheet::sendSpreadsheet(int sId)
 		sheet.reset(new QuattroSpreadsheetInternal::Spreadsheet);
 	}
 
-	m_listener->openSheet(sheet->convertInPoint(sheet->m_widthCols,76), librevenge::RVNG_POINT,
-	                      std::vector<int>(), m_state->getSheetName(sId));
+	m_listener->openSheet(sheet->getWidths(), m_state->getSheetName(sId));
 	sheet->compressRowHeights();
 	std::map<Vec2i, QuattroSpreadsheetInternal::Cell>::const_iterator it = sheet->m_positionToCellMap.begin();
 	int prevRow = -1;
@@ -2176,14 +2193,14 @@ void QuattroSpreadsheet::sendSpreadsheet(int sId)
 				float h=sheet->getRowHeight(prevRow+1, numRepeat);
 				if (row<prevRow+1+numRepeat)
 					numRepeat=row-1-prevRow;
-				m_listener->openSheetRow(h, librevenge::RVNG_POINT, false, numRepeat);
+				m_listener->openSheetRow(WPSRowFormat(h), numRepeat);
 				prevRow+=numRepeat;
 			}
 		}
 		if (row!=prevRow)
 		{
 			if (prevRow != -1) m_listener->closeSheetRow();
-			m_listener->openSheetRow(sheet->getRowHeight(++prevRow), librevenge::RVNG_POINT);
+			m_listener->openSheetRow(WPSRowFormat(sheet->getRowHeight(++prevRow)));
 		}
 		sendCellContent(cell);
 	}
diff --git a/src/lib/WKS4.cpp b/src/lib/WKS4.cpp
index 70ceffc..d9fc262 100644
--- a/src/lib/WKS4.cpp
+++ b/src/lib/WKS4.cpp
@@ -38,6 +38,7 @@
 #include "WPSFont.h"
 #include "WPSHeader.h"
 #include "WPSPageSpan.h"
+#include "WPSStringStream.h"
 
 #include "WKS4Format.h"
 #include "WKS4Spreadsheet.h"
@@ -113,10 +114,11 @@ void SubDocument::parse(shared_ptr<WKSContentListener> &listener, libwps::SubDoc
 struct State
 {
 	//! constructor
-	explicit State(libwps_tools_win::Font::Type fontType) :
+	State(libwps_tools_win::Font::Type fontType, char const *password) :
 		m_eof(-1), m_creator(libwps::WPS_MSWORKS), m_isSpreadsheet(true), m_fontType(fontType), m_version(-1),
 		m_hasLICSCharacters(false), m_fontsList(), m_pageSpan(), m_actPage(0), m_numPages(0),
-		m_headerString(""), m_footerString("")
+		m_headerString(""), m_footerString(""),
+		m_password(password), m_isEncrypted(false), m_isDecoded(false)
 	{
 	}
 	//! returns a color corresponding to an id
@@ -164,6 +166,17 @@ struct State
 	std::string m_headerString;
 	//! the footer string
 	std::string m_footerString;
+
+	//! the password (if known)
+	char const *m_password;
+	//! true if the file is encrypted
+	bool m_isEncrypted;
+	//! true if the main stream has been decoded
+	bool m_isDecoded;
+
+private:
+	State(State const &);
+	State &operator=(State const &);
 };
 
 bool State::getColor(int id, WPSColor &color) const
@@ -206,11 +219,11 @@ bool State::getColor(int id, WPSColor &color) const
 
 // constructor, destructor
 WKS4Parser::WKS4Parser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
-                       libwps_tools_win::Font::Type encoding) :
+                       libwps_tools_win::Font::Type encoding, char const *password) :
 	WKSParser(input, header), m_listener(), m_state(), m_spreadsheetParser()
 
 {
-	m_state.reset(new WKS4ParserInternal::State(encoding));
+	m_state.reset(new WKS4ParserInternal::State(encoding, password));
 	m_spreadsheetParser.reset(new WKS4Spreadsheet(*this));
 }
 
@@ -223,6 +236,13 @@ int WKS4Parser::version() const
 	return m_state->m_version;
 }
 
+void WKS4Parser::resetMainInput(RVNGInputStreamPtr newInput)
+{
+	resetInput(newInput);
+	ascii().setStream(newInput);
+	m_spreadsheetParser->resetInput(newInput);
+}
+
 bool WKS4Parser::checkFilePosition(long pos)
 {
 	if (m_state->m_eof < 0)
@@ -284,7 +304,7 @@ void WKS4Parser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
 		throw (libwps::ParseException());
 	}
 
-	if (!checkHeader(0L, true)) throw(libwps::ParseException());
+	if (!checkHeader(0L)) throw(libwps::ParseException());
 
 	bool ok=false;
 	try
@@ -311,6 +331,12 @@ void WKS4Parser::parse(librevenge::RVNGSpreadsheetInterface *documentInterface)
 			ok = true;
 		}
 	}
+	catch (libwps::PasswordException())
+	{
+		ascii().reset();
+		WPS_DEBUG_MSG(("WKS4Parser::parse: password exception catched when parsing MN0\n"));
+		throw (libwps::PasswordException());
+	}
 	catch (...)
 	{
 		WPS_DEBUG_MSG(("WKS4Parser::parse: exception catched when parsing MN0\n"));
@@ -349,7 +375,7 @@ shared_ptr<WKSContentListener> WKS4Parser::createListener(librevenge::RVNGSpread
 ////////////////////////////////////////////////////////////
 bool WKS4Parser::checkHeader(WPSHeader *header, bool strict)
 {
-	*m_state = WKS4ParserInternal::State(m_state->m_fontType);
+	m_state.reset(new WKS4ParserInternal::State(m_state->m_fontType, m_state->m_password));
 	libwps::DebugStream f;
 
 	RVNGInputStreamPtr input = getInput();
@@ -456,25 +482,27 @@ bool WKS4Parser::checkHeader(WPSHeader *header, bool strict)
 		return false;
 	}
 
+	m_state->m_creator=creator;
 	input->seek(0, librevenge::RVNG_SEEK_SET);
 	if (strict && m_state->m_version<1000)
 	{
 		for (int i=0; i < 4; ++i)
 		{
 			if (!readZone()) return false;
+			if (m_state->m_isEncrypted) break;
 		}
 	}
 	ascii().addPos(0);
 	ascii().addNote(f.str().c_str());
 
 	m_state->m_isSpreadsheet=isSpreadsheet;
-	m_state->m_creator=creator;
 	if (header)
 	{
 		header->setMajorVersion((uint8_t) m_state->m_version);
 		header->setCreator(creator);
 		header->setKind(kind);
 		header->setNeedEncoding(needEncoding);
+		header->setIsEncrypted(m_state->m_isEncrypted);
 	}
 	return true;
 }
@@ -513,11 +541,16 @@ bool WKS4Parser::readZones()
 		return false;
 	}
 
-	while (readZone()) ;
+	while (readZone())
+	{
+		if (m_state->m_isEncrypted && !m_state->m_isDecoded)
+			throw(libwps::PasswordException());
+	}
 
 	//
 	// look for ending
 	//
+	input = getInput();
 	long pos = input->tell();
 	if (!checkFilePosition(pos+4))
 	{
@@ -782,6 +815,50 @@ bool WKS4Parser::readZone()
 			readChartName();
 			isParsed = true;
 			break;
+		case 0x4b:
+			if (sz==2 && m_state->m_creator==libwps::WPS_LOTUS)
+			{
+				m_state->m_isEncrypted=true;
+				input->seek(pos+4, librevenge::RVNG_SEEK_SET);
+				f.str("");
+				uint16_t fileKey(libwps::readU16(input));
+				f << "Entries(Password):pass=" << std::hex << fileKey << std::dec << ",";
+				isParsed = needWriteInAscii = true;
+				if (!m_state->m_isDecoded)
+				{
+					static uint8_t const(defValues[])=
+					{
+						0xbb,0xff, 0xff,0xba, 0xff,0xff, 0xb9,0x80,
+						0,0x0be, 0xf,0, 0xbf,0xf, 0,0
+					};
+					uint16_t key;
+					std::vector<uint8_t> keys;
+					if (m_state->m_password && libwps::encodeLotusPassword(m_state->m_password, key, keys, defValues))
+					{
+						RVNGInputStreamPtr newInput;
+						if (uint16_t(key<<8|key>>8)==fileKey)
+							newInput=decodeStream(input, m_state->m_eof, keys);
+						if (newInput)
+						{
+							// let's replace the current input by the decoded input
+							m_state->m_isDecoded=true;
+							input=newInput;
+							resetMainInput(newInput);
+						}
+						else
+						{
+							WPS_DEBUG_MSG(("LotusParser::parse: the password seems bad\n"));
+						}
+
+					}
+				}
+				break;
+			}
+			else
+			{
+				WPS_DEBUG_MSG(("LotusParser::parse: find unexpected password field\n"));
+			}
+			break;
 		case 0x64: // hidden column
 			isParsed = m_spreadsheetParser->readHiddenColumns();
 			break;
@@ -2083,4 +2160,52 @@ bool WKS4Parser::readUnknown1()
 	return true;
 }
 
+////////////////////////////////////////////////////////////
+//   decode
+////////////////////////////////////////////////////////////
+RVNGInputStreamPtr WKS4Parser::decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key)
+{
+	if (!input || key.size()!=16)
+	{
+		WPS_DEBUG_MSG(("WKS4Parser::decodeStream: the arguments seems bad\n"));
+		return RVNGInputStreamPtr();
+	}
+	long actPos=input->tell();
+	input->seek(0,librevenge::RVNG_SEEK_SET);
+	librevenge::RVNGBinaryData data;
+	if (!libwps::readDataToEnd(input, data) || long(data.size())!=endPos || !data.getDataBuffer())
+	{
+		WPS_DEBUG_MSG(("WKS4Parser::decodeStream: can not read the original input\n"));
+		return RVNGInputStreamPtr();
+	}
+	unsigned char *buf=const_cast<unsigned char *>(data.getDataBuffer());
+	input->seek(actPos,librevenge::RVNG_SEEK_SET);
+	uint8_t d7=0;
+	while (!input->isEnd())
+	{
+		long pos=input->tell();
+		if (pos+4>endPos) break;
+		input->seek(2,librevenge::RVNG_SEEK_CUR);
+		int sSz=int(libwps::readU16(input));
+		if (pos+4+sSz>endPos)
+		{
+			input->seek(pos,librevenge::RVNG_SEEK_SET);
+			break;
+		}
+		for (int i=0; i<sSz; ++i)
+		{
+			uint8_t c=uint8_t(libwps::readU8(input));
+			c=uint8_t((c<<1)|(c>>7));
+			c=(c^key[(d7++)&0xf]);
+			buf[pos+4+i]=uint8_t((c>>6)|(c<<2));
+		}
+	}
+	if (input->tell()!=endPos)
+	{
+		WPS_DEBUG_MSG(("WKS4Parser::decodeStream: can not decode the end of the file, data may be bad %lx %lx\n", static_cast<unsigned long>(input->tell()), static_cast<unsigned long>(endPos)));
+	}
+	RVNGInputStreamPtr res(new WPSStringStream(data.getDataBuffer(), static_cast<unsigned int>(endPos)));
+	res->seek(actPos, librevenge::RVNG_SEEK_SET);
+	return res;
+}
 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
diff --git a/src/lib/WKS4.h b/src/lib/WKS4.h
index cac0c86..97fff70 100644
--- a/src/lib/WKS4.h
+++ b/src/lib/WKS4.h
@@ -51,7 +51,8 @@ class WKS4Parser : public WKSParser
 public:
 	//! constructor
 	WKS4Parser(RVNGInputStreamPtr &input, WPSHeaderPtr &header,
-	           libwps_tools_win::Font::Type encoding=libwps_tools_win::Font::UNKNOWN);
+	           libwps_tools_win::Font::Type encoding=libwps_tools_win::Font::UNKNOWN,
+	           char const *password=0);
 	//! destructor
 	~WKS4Parser();
 	//! called by WPSDocument to parse the file
@@ -94,6 +95,8 @@ protected:
 
 	/// check for the existence of a format stream, if it exists, parse it
 	bool parseFormatStream();
+	/// reset the main input
+	void resetMainInput(RVNGInputStreamPtr newInput);
 
 	/** finds the different zones (spreadsheet, chart, print, ...) */
 	bool readZones();
@@ -142,6 +145,11 @@ protected:
 	//! reads an unknown structure which seems relative to a chart : CHECKME
 	bool readChartUnknown();
 
+	//////////////////////// decode a lotus stream //////////////////////////////
+
+	//! try to decode a stream, if successful, replace the stream'input by the new one
+	static RVNGInputStreamPtr decodeStream(RVNGInputStreamPtr input, long endPos, std::vector<uint8_t> const &key);
+
 	//////////////////////// unknown zone //////////////////////////////
 
 	//! reads windows record 0:7|0:9
diff --git a/src/lib/WKS4Spreadsheet.cpp b/src/lib/WKS4Spreadsheet.cpp
index 90a2734..29dd98e 100644
--- a/src/lib/WKS4Spreadsheet.cpp
+++ b/src/lib/WKS4Spreadsheet.cpp
@@ -36,6 +36,7 @@
 #include "WKSContentListener.h"
 #include "WPSEntry.h"
 #include "WPSFont.h"
+#include "WPSTable.h"
 
 #include "WKS4.h"
 
@@ -337,19 +338,36 @@ public:
 		if (actPos[1]>=actPos[0])
 			m_rowHeightMap[actPos]=actHeight;
 	}
-	//! convert the m_widthCols in a vector of of point size
-	static std::vector<float> convertInPoint(std::vector<int> const &list,
-	                                         float defSize)
+	//! return the columns format
+	std::vector<WPSColumnFormat> getWidths(float defSize=72) const
 	{
-		size_t numElt = list.size();
-		std::vector<float> res;
-		res.resize(numElt);
-		for (size_t i = 0; i < numElt; i++)
+		std::vector<WPSColumnFormat> widths;
+		WPSColumnFormat defWidth(defSize), actWidth;
+		defWidth.m_useOptimalWidth=true;
+		int repeat=0;
+		for (size_t c = 0; c < m_widthCols.size(); c++)
 		{
-			if (list[i] < 0) res[i] = defSize;
-			else res[i] = float(list[i])/20.f;
+			WPSColumnFormat newWidth;
+			if (m_widthCols[c] < 0)
+				newWidth=defWidth;
+			else
+				newWidth=WPSColumnFormat(float(m_widthCols[c])/20.f);
+			if (repeat && newWidth!=actWidth)
+			{
+				actWidth.m_numRepeat=repeat;
+				widths.push_back(actWidth);
+				repeat=0;
+			}
+			if (repeat==0)
+				actWidth=newWidth;
+			++repeat;
+		}
+		if (repeat)
+		{
+			actWidth.m_numRepeat=repeat;
+			widths.push_back(actWidth);
 		}
-		return res;
+		return widths;
 	}
 	//! returns true if the spreedsheet is empty
 	bool empty() const
@@ -475,6 +493,11 @@ WKS4Spreadsheet::~WKS4Spreadsheet()
 {
 }
 
+void WKS4Spreadsheet::resetInput(RVNGInputStreamPtr newInput)
+{
+	m_input=newInput;
+}
+
 int WKS4Spreadsheet::version() const
 {
 	if (m_state->m_version<0)
@@ -2322,8 +2345,7 @@ void WKS4Spreadsheet::sendSpreadsheet(int sId)
 		sheet.reset(new WKS4SpreadsheetInternal::Spreadsheet);
 	}
 
-	m_listener->openSheet(sheet->convertInPoint(sheet->m_widthCols,72), librevenge::RVNG_POINT,
-	                      std::vector<int>(), m_state->getSheetName(sId));
+	m_listener->openSheet(sheet->getWidths(), m_state->getSheetName(sId));
 	sheet->compressRowHeights();
 	std::map<Vec2i, WKS4SpreadsheetInternal::Cell>::const_iterator it = sheet->m_positionToCellMap.begin();
 	int prevRow = -1;
@@ -2340,14 +2362,14 @@ void WKS4Spreadsheet::sendSpreadsheet(int sId)
 				float h=sheet->getRowHeight(prevRow+1, numRepeat);
 				if (row<prevRow+1+numRepeat)
 					numRepeat=row-1-prevRow;
-				m_listener->openSheetRow(h, librevenge::RVNG_POINT, false, numRepeat);
+				m_listener->openSheetRow(WPSRowFormat(h), numRepeat);
 				prevRow+=numRepeat;
 			}
 		}
 		if (row!=prevRow)
 		{
 			if (prevRow != -1) m_listener->closeSheetRow();
-			m_listener->openSheetRow(sheet->getRowHeight(++prevRow), librevenge::RVNG_POINT);
+			m_listener->openSheetRow(WPSRowFormat(sheet->getRowHeight(++prevRow)));
 		}
 		sendCellContent(cell);
 	}
diff --git a/src/lib/WKS4Spreadsheet.h b/src/lib/WKS4Spreadsheet.h
index ebacf69..0b128a0 100644
--- a/src/lib/WKS4Spreadsheet.h
+++ b/src/lib/WKS4Spreadsheet.h
@@ -67,6 +67,8 @@ protected:
 	int version() const;
 	//! returns the true if the file has LICS characters
 	bool hasLICSCharacters() const;
+	/// reset the main input
+	void resetInput(RVNGInputStreamPtr input);
 
 	//! returns the number of spreadsheet
 	int getNumSpreadsheets() const;
diff --git a/src/lib/WKSContentListener.cpp b/src/lib/WKSContentListener.cpp
index 3b2a40b..08d8209 100644
--- a/src/lib/WKSContentListener.cpp
+++ b/src/lib/WKSContentListener.cpp
@@ -40,6 +40,7 @@
 #include "WPSPageSpan.h"
 #include "WPSParagraph.h"
 #include "WPSPosition.h"
+#include "WPSTable.h"
 #include "WKSSubDocument.h"
 
 ////////////////////////////////////////////////////////////
@@ -919,8 +920,7 @@ void WKSContentListener::_endSubDocument()
 ///////////////////
 // sheet
 ///////////////////
-void WKSContentListener::openSheet(std::vector<float> const &colWidth, librevenge::RVNGUnit unit,
-                                   std::vector<int> const &repeatColWidthNumber, librevenge::RVNGString const &name)
+void WKSContentListener::openSheet(std::vector<WPSColumnFormat> const &colList, librevenge::RVNGString const &name)
 {
 	if (m_ps->m_isSheetOpened)
 	{
@@ -941,21 +941,10 @@ void WKSContentListener::openSheet(std::vector<float> const &colWidth, libreveng
 
 	librevenge::RVNGPropertyListVector columns;
 
-	size_t nCols = colWidth.size();
-	bool useRepeated=repeatColWidthNumber.size()==nCols;
-	if (!useRepeated&&!repeatColWidthNumber.empty())
-	{
-		WPS_DEBUG_MSG(("WKSContentListener::openSheet: repeatColWidthNumber seems bad\n"));
-	}
-	for (size_t c = 0; c < nCols; c++)
+	for (size_t c = 0; c < colList.size(); c++)
 	{
 		librevenge::RVNGPropertyList column;
-		if (colWidth[c]>=0)
-			column.insert("style:column-width", colWidth[c], unit);
-		else
-			column.insert("style:use-optimal-column-width", true);
-		if (useRepeated && repeatColWidthNumber[c]>1)
-			column.insert("table:number-columns-repeated", repeatColWidthNumber[c]);
+		colList[c].addTo(column);
 		columns.append(column);
 	}
 	propList.insert("librevenge:columns", columns);
@@ -980,7 +969,7 @@ void WKSContentListener::closeSheet()
 	_popParsingState();
 }
 
-void WKSContentListener::openSheetRow(float h, librevenge::RVNGUnit unit, bool headerRow, int numRepeated)
+void WKSContentListener::openSheetRow(WPSRowFormat const &format, int numRepeated)
 {
 	if (m_ps->m_isSheetRowOpened)
 	{
@@ -993,14 +982,9 @@ void WKSContentListener::openSheetRow(float h, librevenge::RVNGUnit unit, bool h
 		return;
 	}
 	librevenge::RVNGPropertyList propList;
-	propList.insert("librevenge:is-header-row", headerRow);
+	format.addTo(propList);
 	if (numRepeated>1)
 		propList.insert("table:number-rows-repeated", numRepeated);
-
-	if (h > 0)
-		propList.insert("style:row-height", h, unit);
-	else if (h < 0)
-		propList.insert("style:min-row-height", -h, unit);
 	m_documentInterface->openSheetRow(propList);
 	m_ps->m_isSheetRowOpened = true;
 }
@@ -1231,6 +1215,7 @@ shared_ptr<WKSContentParsingState> WKSContentListener::_pushParsingState()
 	m_ps->m_pageMarginBottom = actual->m_pageMarginBottom;
 
 	m_ps->m_isNote = actual->m_isNote;
+	m_ps->m_isPageSpanOpened = actual->m_isPageSpanOpened;
 
 	return actual;
 }
diff --git a/src/lib/WKSContentListener.h b/src/lib/WKSContentListener.h
index 4dd0471..4b0c739 100644
--- a/src/lib/WKSContentListener.h
+++ b/src/lib/WKSContentListener.h
@@ -37,11 +37,13 @@
 #include "WPSListener.h"
 
 class WPSCellFormat;
+struct WPSColumnFormat;
 class WPSGraphicShape;
 class WPSGraphicStyle;
 class WPSList;
 class WPSPageSpan;
 struct WPSParagraph;
+struct WPSRowFormat;
 struct WPSTabStop;
 
 struct WKSContentParsingState;
@@ -199,12 +201,11 @@ public:
 
 	// ------- sheet -----------------
 	/** open a sheet*/
-	void openSheet(std::vector<float> const &colWidth, librevenge::RVNGUnit unit,
-	               std::vector<int> const &repeatColWidthNumber=std::vector<int>(), librevenge::RVNGString const &name="");
+	void openSheet(std::vector<WPSColumnFormat> const &columns, librevenge::RVNGString const &name="");
 	/** closes this sheet */
 	void closeSheet();
-	/** open a row with given height. If h<0, use min-row-heigth */
-	void openSheetRow(float h, librevenge::RVNGUnit unit, bool headerRow=false, int numRepeated=1);
+	/** open a row */
+	void openSheetRow(WPSRowFormat const &f, int numRepeated=1);
 	/** closes this row */
 	void closeSheetRow();
 	/** low level function to define a cell.
diff --git a/src/lib/WKSParser.h b/src/lib/WKSParser.h
index 7ad43a7..13717fd 100644
--- a/src/lib/WKSParser.h
+++ b/src/lib/WKSParser.h
@@ -42,6 +42,10 @@ protected:
 	{
 		return m_input;
 	}
+	void resetInput(RVNGInputStreamPtr newInput)
+	{
+		m_input=newInput;
+	}
 	RVNGInputStreamPtr getFileInput();
 	WPSHeaderPtr &getHeader()
 	{
diff --git a/src/lib/WPSCell.cpp b/src/lib/WPSCell.cpp
index 6b2e3d9..7086140 100644
--- a/src/lib/WPSCell.cpp
+++ b/src/lib/WPSCell.cpp
@@ -189,7 +189,20 @@ void WPSCellFormat::addTo(librevenge::RVNGPropertyList &propList) const
 	case VALIGN_DEFAULT:
 		break; // default
 	default:
-		WPS_DEBUG_MSG(("MWAWCell::addTo: called with unknown valign=%d\n", vAlignement()));
+		WPS_DEBUG_MSG(("WPSCell::addTo: called with unknown valign=%d\n", vAlignement()));
+	}
+	switch (wrapping())
+	{
+	case WRAP_WRAP:
+		propList.insert("fo:wrap-option", "wrap");
+		break;
+	case WRAP_NO_WRAP:
+		propList.insert("fo:wrap-option", "no-wrap");
+		break;
+	case WRAP_DEFAULT:
+		break;
+	default:
+		WPS_DEBUG_MSG(("WPSCell::addTo: called with unknown wrapping=%d\n", wrapping()));
 	}
 	if (m_rotation)
 		propList.insert("style:rotation-angle",m_rotation);
@@ -220,6 +233,7 @@ void WPSCellFormat::addTo(librevenge::RVNGPropertyList &propList) const
 		propList.insert("fo:background-color", backgroundColor().str().c_str());
 	if (m_protected)
 		propList.insert("style:cell-protect","protected");
+	propList.insert("fo:padding",0,librevenge::RVNG_POINT); // changme
 }
 
 std::string WPSCellFormat::getValueType() const
@@ -357,6 +371,8 @@ int WPSCellFormat::compare(WPSCellFormat const &cell, bool onlyNumbering) const
 	if (diff) return diff;
 	diff = int(m_vAlign) - int(cell.m_vAlign);
 	if (diff) return diff;
+	diff = int(m_wrapping) - int(cell.m_wrapping);
+	if (diff) return diff;
 	if (m_rotation<cell.m_rotation) return 1;
 	if (m_rotation>cell.m_rotation) return -1;
 	if (m_backgroundColor<cell.m_backgroundColor) return 1;
@@ -408,6 +424,18 @@ std::ostream &operator<<(std::ostream &o, WPSCellFormat const &cell)
 	default:
 		break; // default
 	}
+	switch (cell.m_wrapping)
+	{
+	case WPSCellFormat::WRAP_WRAP:
+		o << "wrap,";
+		break;
+	case WPSCellFormat::WRAP_NO_WRAP:
+		o << "wrap[no],";
+		break;
+	case WPSCellFormat::WRAP_DEFAULT:
+	default:
+		break;
+	}
 	if (cell.m_rotation)
 		o << "rotation=" << cell.m_rotation << ",";
 	int subForm = cell.m_subFormat;
diff --git a/src/lib/WPSCell.h b/src/lib/WPSCell.h
index 76bdd9c..1cf4ab7 100644
--- a/src/lib/WPSCell.h
+++ b/src/lib/WPSCell.h
@@ -48,7 +48,8 @@ public:
 	                         };
 	/** the default vertical alignement. */
 	enum VerticalAlignment { VALIGN_TOP, VALIGN_CENTER, VALIGN_BOTTOM, VALIGN_DEFAULT };
-
+	/** the wrapping */
+	enum Wrapping { WRAP_WRAP, WRAP_NO_WRAP, WRAP_DEFAULT };
 	/** the different types of cell's field */
 	enum FormatType { F_TEXT, F_BOOLEAN, F_NUMBER, F_DATE, F_TIME, F_UNKNOWN };
 
@@ -66,7 +67,7 @@ public:
 
 	//! constructor
 	WPSCellFormat() :
-		m_font(), m_hAlign(HALIGN_DEFAULT), m_vAlign(VALIGN_DEFAULT), m_rotation(0), m_bordersList(), m_format(F_UNKNOWN), m_subFormat(0), m_DTFormat(""), m_digits(-1000), m_protected(false), m_backgroundColor(WPSColor::white()) { }
+		m_font(), m_hAlign(HALIGN_DEFAULT), m_vAlign(VALIGN_DEFAULT), m_wrapping(WRAP_DEFAULT), m_rotation(0), m_bordersList(), m_format(F_UNKNOWN), m_subFormat(0), m_DTFormat(""), m_digits(-1000), m_protected(false), m_backgroundColor(WPSColor::white()) { }
 	//! destructor
 	virtual ~WPSCellFormat() {}
 	//! returns true if this is a basic format style
@@ -112,6 +113,16 @@ public:
 	{
 		m_vAlign = align;
 	}
+	//! returns the wrapping
+	Wrapping wrapping() const
+	{
+		return m_wrapping;
+	}
+	//! sets the wrapping
+	void setWrapping(Wrapping align)
+	{
+		m_wrapping = align;
+	}
 
 	//! returns the text rotation angle
 	int getTextRotation() const
@@ -240,6 +251,8 @@ protected:
 	HorizontalAlignment m_hAlign;
 	//! the cell vertical alignement : by default nothing
 	VerticalAlignment m_vAlign;
+	//! the wrapping : by default nothing
+	Wrapping m_wrapping;
 	//! the text rotation
 	int m_rotation;
 	//! the cell border WPSBorder::Pos
diff --git a/src/lib/WPSDocument.cpp b/src/lib/WPSDocument.cpp
index 52a0e66..615b8b4 100644
--- a/src/lib/WPSDocument.cpp
+++ b/src/lib/WPSDocument.cpp
@@ -105,7 +105,7 @@ WPSLIB WPSConfidence WPSDocument::isFileFormatSupported(librevenge::RVNGInputStr
 			if (!parser.checkHeader(header.get(), true))
 				return WPS_CONFIDENCE_NONE;
 			needEncoding=header->getNeedEncoding();
-			return WPS_CONFIDENCE_EXCELLENT;
+			return header->getIsEncrypted() ? WPS_CONFIDENCE_SUPPORTED_ENCRYPTION : WPS_CONFIDENCE_EXCELLENT;
 		}
 		else if (kind==WPS_SPREADSHEET && creator==WPS_QUATTRO_PRO && header->getMajorVersion()<=2)
 		{
@@ -123,7 +123,7 @@ WPSLIB WPSConfidence WPSDocument::isFileFormatSupported(librevenge::RVNGInputStr
 			if (!parser.checkHeader(header.get(), true))
 				return WPS_CONFIDENCE_NONE;
 			needEncoding=header->getNeedEncoding();
-			return WPS_CONFIDENCE_EXCELLENT;
+			return header->getIsEncrypted() ? WPS_CONFIDENCE_SUPPORTED_ENCRYPTION : WPS_CONFIDENCE_EXCELLENT;
 		}
 
 		/* A word document: as WPS8Parser does not have a checkHeader
@@ -145,6 +145,10 @@ WPSLIB WPSConfidence WPSDocument::isFileFormatSupported(librevenge::RVNGInputStr
 	{
 		WPS_DEBUG_MSG(("File exception trapped\n"));
 	}
+	catch (libwps::PasswordException)
+	{
+		WPS_DEBUG_MSG(("Password exception trapped\n"));
+	}
 	catch (libwps::ParseException)
 	{
 		WPS_DEBUG_MSG(("Parse exception trapped\n"));
@@ -228,6 +232,11 @@ WPSLIB WPSResult WPSDocument::parse(librevenge::RVNGInputStream *ip, librevenge:
 		WPS_DEBUG_MSG(("Parse exception trapped\n"));
 		error = WPS_PARSE_ERROR;
 	}
+	catch (libwps::PasswordException)
+	{
+		WPS_DEBUG_MSG(("Password exception trapped\n"));
+		error = WPS_ENCRYPTION_ERROR;
+	}
 	catch (...)
 	{
 		//fixme: too generic
@@ -239,7 +248,7 @@ WPSLIB WPSResult WPSDocument::parse(librevenge::RVNGInputStream *ip, librevenge:
 }
 
 WPSLIB WPSResult WPSDocument::parse(librevenge::RVNGInputStream *ip, librevenge::RVNGSpreadsheetInterface *documentInterface,
-                                    char const * /*password*/, char const *encoding)
+                                    char const *password, char const *encoding)
 {
 	if (!ip || !documentInterface)
 		return WPS_UNKNOWN_ERROR;
@@ -260,7 +269,7 @@ WPSLIB WPSResult WPSDocument::parse(librevenge::RVNGInputStream *ip, librevenge:
 		        header->getMajorVersion()>=100)
 		{
 			parser.reset(new LotusParser(header->getInput(), header,
-			                             libwps_tools_win::Font::getTypeForString(encoding)));
+			                             libwps_tools_win::Font::getTypeForString(encoding), password));
 			if (!parser) return WPS_UNKNOWN_ERROR;
 			parser->parse(documentInterface);
 		}
@@ -282,7 +291,7 @@ WPSLIB WPSResult WPSDocument::parse(librevenge::RVNGInputStream *ip, librevenge:
 			case 1:
 			{
 				parser.reset(new WKS4Parser(header->getInput(), header,
-				                            libwps_tools_win::Font::getTypeForString(encoding)));
+				                            libwps_tools_win::Font::getTypeForString(encoding), password));
 				if (!parser) return WPS_UNKNOWN_ERROR;
 				parser->parse(documentInterface);
 				break;
@@ -304,6 +313,11 @@ WPSLIB WPSResult WPSDocument::parse(librevenge::RVNGInputStream *ip, librevenge:
 		WPS_DEBUG_MSG(("Parse exception trapped\n"));
 		error = WPS_PARSE_ERROR;
 	}
+	catch (libwps::PasswordException)
+	{
+		WPS_DEBUG_MSG(("Password exception trapped\n"));
+		error = WPS_ENCRYPTION_ERROR;
+	}
 	catch (...)
 	{
 		//fixme: too generic
diff --git a/src/lib/WPSHeader.cpp b/src/lib/WPSHeader.cpp
index f20b7c1..05e680d 100644
--- a/src/lib/WPSHeader.cpp
+++ b/src/lib/WPSHeader.cpp
@@ -30,7 +30,7 @@ using namespace libwps;
 
 WPSHeader::WPSHeader(RVNGInputStreamPtr &input, RVNGInputStreamPtr &fileInput, uint8_t majorVersion, WPSKind kind, WPSCreator creator) :
 	m_input(input), m_fileInput(fileInput), m_majorVersion(majorVersion), m_kind(kind), m_creator(creator),
-	m_needEncodingFlag(false)
+	m_isEncrypted(false), m_needEncodingFlag(false)
 {
 }
 
diff --git a/src/lib/WPSHeader.h b/src/lib/WPSHeader.h
index 289adaf..21c97fb 100644
--- a/src/lib/WPSHeader.h
+++ b/src/lib/WPSHeader.h
@@ -66,6 +66,16 @@ public:
 		m_kind=kind;
 	}
 
+	bool getIsEncrypted() const
+	{
+		return m_isEncrypted;
+	}
+
+	void setIsEncrypted(bool isEncrypted)
+	{
+		m_isEncrypted=isEncrypted;
+	}
+
 	bool getNeedEncoding() const
 	{
 		return m_needEncodingFlag;
@@ -94,6 +104,8 @@ private:
 	uint8_t m_majorVersion;
 	libwps::WPSKind m_kind;
 	libwps::WPSCreator m_creator;
+	//! a flag to know if the file is encrypted
+	bool m_isEncrypted;
 	//! a flag to know if we need to have the character set encoding
 	bool m_needEncodingFlag;
 };
diff --git a/src/lib/WPSTable.cpp b/src/lib/WPSTable.cpp
index 040ab0b..0e13b50 100644
--- a/src/lib/WPSTable.cpp
+++ b/src/lib/WPSTable.cpp
@@ -38,6 +38,65 @@
 #include "WPSTable.h"
 
 ////////////////////////////////////////////////////////////
+// WPSColumnFormat
+std::ostream &operator<<(std::ostream &o, WPSColumnFormat const &column)
+{
+	if (column.m_width>=0)
+	{
+		if (column.m_isPercentWidth)
+			o<<"w=" << column.m_width << "%,";
+		else
+			o<<"w=" << column.m_width << ",";
+	}
+	if (column.m_useOptimalWidth) o << "optimal[h],";
+	if (column.m_isHeader) o << "table[header],";
+	if (column.m_numRepeat>1) o << "repeat=" << column.m_numRepeat << ",";
+	return o;
+}
+
+void WPSColumnFormat::addTo(librevenge::RVNGPropertyList &propList) const
+{
+	if (m_width>=0)
+		propList.insert("style:column-width", m_width, m_isPercentWidth ? librevenge::RVNG_PERCENT : librevenge::RVNG_POINT);
+	if (m_useOptimalWidth)
+		propList.insert("style:use-optimal-column-width", true);
+	if (m_isHeader)
+		propList.insert("librevenge:is-header-column", true); // checkme
+	if (m_numRepeat>1)
+		propList.insert("table:number-columns-repeated", m_numRepeat);
+}
+
+////////////////////////////////////////////////////////////
+// WPSRowFormat
+std::ostream &operator<<(std::ostream &o, WPSRowFormat const &row)
+{
+	if (row.m_height>=0)
+	{
+		if (row.m_isMinimalHeight)
+			o<<"h[min]=" << row.m_height << ",";
+		else
+			o<<"h=" << row.m_height << ",";
+	}
+	if (row.m_useOptimalHeight) o << "optimal[h],";
+	if (row.m_isHeader) o << "table[header],";
+	return o;
+}
+
+void WPSRowFormat::addTo(librevenge::RVNGPropertyList &propList) const
+{
+	if (m_height>=0)
+	{
+		if (m_isMinimalHeight)
+			propList.insert("style:min-row-height", m_height, librevenge::RVNG_POINT);
+		else
+			propList.insert("style:row-height", m_height, librevenge::RVNG_POINT);
+	}
+	if (m_useOptimalHeight)
+		propList.insert("style:use-optimal-row-height", true);
+	propList.insert("librevenge:is-header-row", m_isHeader);
+}
+
+////////////////////////////////////////////////////////////
 // destructor, ...
 WPSTable::~WPSTable()
 {
diff --git a/src/lib/WPSTable.h b/src/lib/WPSTable.h
index f871e7d..0d00f20 100644
--- a/src/lib/WPSTable.h
+++ b/src/lib/WPSTable.h
@@ -32,6 +32,123 @@
 #include "libwps_internal.h"
 
 /*
+ * Structure to store the column properties
+ *
+ * \note use only to define sheet properties, to be changed
+ */
+struct WPSColumnFormat
+{
+public:
+	//! constructor
+	explicit WPSColumnFormat(float width=-1)
+		: m_width(width)
+		, m_isPercentWidth(false)
+		, m_useOptimalWidth(false)
+		, m_isHeader(false)
+		, m_numRepeat(1)
+	{
+	}
+	//! add to the propList
+	void addTo(librevenge::RVNGPropertyList &propList) const;
+	/** a comparison  function
+		\note this comparison function does ignore m_numRepeat
+	 */
+	int compare(WPSColumnFormat const &col) const
+	{
+		if (m_width<col.m_width) return 1;
+		if (m_width>col.m_width) return -1;
+		if (m_isPercentWidth!=col.m_isPercentWidth) return m_isPercentWidth ? 1 : -1;
+		if (m_useOptimalWidth!=col.m_useOptimalWidth) return m_useOptimalWidth ? 1 : -1;
+		if (m_isHeader!=col.m_isHeader) return m_isHeader ? 1 : -1;
+		return 0;
+	}
+	//! operator==
+	bool operator==(WPSColumnFormat const &col) const
+	{
+		return compare(col)==0;
+	}
+	//! operator!=
+	bool operator!=(WPSColumnFormat const &col) const
+	{
+		return compare(col)!=0;
+	}
+	//! operator<
+	bool operator<(WPSColumnFormat const &col) const
+	{
+		return compare(col)<0;
+	}
+	//! operator<<
+	friend std::ostream &operator<<(std::ostream &o, WPSColumnFormat const &col);
+
+	//! the column width, if known
+	float m_width;
+	//! a flag to know if the width is in percent (or in point)
+	bool m_isPercentWidth;
+	//! a flag to know if we need to see use-optimal column width
+	bool m_useOptimalWidth;
+	//! a flag to know if the column is a header column
+	bool m_isHeader;
+	//! the number times a column is repeated
+	int m_numRepeat;
+};
+
+/*
+ * Structure to store the row properties
+ *
+ * \note use only to define sheet properties, to be changed
+ */
+struct WPSRowFormat
+{
+public:
+	//! constructor
+	explicit WPSRowFormat(float height=-1)
+		: m_height(height)
+		, m_isMinimalHeight(false)
+		, m_useOptimalHeight(false)
+		, m_isHeader(false)
+	{
+	}
+	//! add to the propList
+	void addTo(librevenge::RVNGPropertyList &propList) const;
+	//! a comparison  function
+	int compare(WPSRowFormat const &row) const
+	{
+		if (m_height<row.m_height) return 1;
+		if (m_height>row.m_height) return -1;
+		if (m_isMinimalHeight!=row.m_isMinimalHeight) return m_isMinimalHeight ? 1 : -1;
+		if (m_useOptimalHeight!=row.m_useOptimalHeight) return m_useOptimalHeight ? 1 : -1;
+		if (m_isHeader!=row.m_isHeader) return m_isHeader ? 1 : -1;
+		return 0;
+	}
+	//! operator==
+	bool operator==(WPSRowFormat const &row) const
+	{
+		return compare(row)==0;
+	}
+	//! operator!=
+	bool operator!=(WPSRowFormat const &row) const
+	{
+		return compare(row)!=0;
+	}
+	//! operator<
+	bool operator<(WPSRowFormat const &row) const
+	{
+		return compare(row)<0;
+	}
+	//! operator<<
+	friend std::ostream &operator<<(std::ostream &o, WPSRowFormat const &row);
+
+	//! the row height, if known
+	float m_height;
+	//! a flag to know if the height is only a minimum
+	bool m_isMinimalHeight;
+	//! a flag to know if we need to see use-optimal row height
+	bool m_useOptimalHeight;
+	//! a flag to know if the row is a header row
+	bool m_isHeader;
+};
+
+/*
  * Structure to store and construct a table from an unstructured list
  * of cell
  *
diff --git a/src/lib/libwps_internal.cpp b/src/lib/libwps_internal.cpp
index 0aae7d6..73d1999 100644
--- a/src/lib/libwps_internal.cpp
+++ b/src/lib/libwps_internal.cpp
@@ -825,6 +825,64 @@ bool WPSTransformation::decompose(float &rot, Vec2f &shearing, WPSTransformation
 	transform=WPSTransformation::rotation(-rot, center) * transform;
 	return true;
 }
+
+////////////////////////////////////////////////////////////
+// password utility
+////////////////////////////////////////////////////////////
+
+namespace libwps
+{
+//! try to encode a lotus file
+bool encodeLotusPassword(char const *password, uint16_t &key, std::vector<uint8_t> &keys, uint8_t const(&defValues)[16])
+{
+	if (!password)
+	{
+		WPS_DEBUG_MSG(("libwps::encodeLotusPassword: called without password\n"));
+		return false;
+	}
+	size_t const len=16;
+	key=0xFFFF;
+	uint16_t val=0;
+	for (size_t i=0; i<len; ++i)
+	{
+		if (password[i]==0)
+			break;
+		uint8_t c=uint8_t(password[i]);
+		key=uint16_t(key^c);
+		val=uint16_t((val&0xFF)|(key<<8));
+		val=uint16_t(((val<<4)&0xFFF0)|(val>>12));
+		key^=val;
+		val=uint16_t((val<<8)|(val>>8));
+		val=uint16_t((val<<1)|(val>>15));
+		val=uint16_t((val<<8)|(val>>8));
+		key=uint16_t((key<<8)|(key>>8));
+		key^=val;
+
+		val=uint16_t((((val>>4)&0xfff)|(val<<12))&0xe0ff);
+		key^=val;
+		val=uint16_t((val>>1)|(val<<15));
+		key=uint16_t(key^(val>>8));
+	}
+
+	size_t cPos;
+	keys.resize(len);
+	// copy password in keys and fill the remaining space with
+	// defValues
+	for (cPos=0 ; cPos<len; ++cPos)
+	{
+		if (password[cPos]==0)
+			break;
+		keys[cPos]=uint8_t(password[cPos]);
+	}
+	uint8_t const *defPtr=defValues;
+	for (; cPos<len; ++cPos)
+		keys[cPos] = *(defPtr++);
+	// now do an xor to code the result
+	for (size_t i=0; i<len; ++i)
+		keys[i]=uint8_t(keys[i]^(key>>((i%2)==0 ? 8 : 0)));
+	return true;
+}
+}
 ////////////////////////////////////////////////////////////
 // debug
 ////////////////////////////////////////////////////////////
diff --git a/src/lib/libwps_internal.h b/src/lib/libwps_internal.h
index 4df266d..690dedb 100644
--- a/src/lib/libwps_internal.h
+++ b/src/lib/libwps_internal.h
@@ -151,6 +151,11 @@ class ParseException
 	// needless to say, we could flesh this class out a bit
 };
 
+class PasswordException
+{
+	// needless to say, we could flesh this class out a bit
+};
+
 class GenericException
 {
 	// needless to say, we could flesh this class out a bit
@@ -1222,5 +1227,15 @@ protected:
 	//! flag to know if this matrix is an identity matrix
 	mutable bool m_isIdentity;
 };
+
+//
+// password utility
+//
+
+namespace libwps
+{
+//! try to encode a lotus file
+bool encodeLotusPassword(char const *password, uint16_t &key, std::vector<uint8_t> &keys, uint8_t const(&defValues)[16]);
+}
 #endif /* LIBWPS_INTERNAL_H */
 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-openoffice/libwps.git



More information about the Pkg-openoffice-commits mailing list