summaryrefslogtreecommitdiffstats
path: root/filters/kspread/html/htmlexport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/kspread/html/htmlexport.cpp')
-rw-r--r--filters/kspread/html/htmlexport.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/filters/kspread/html/htmlexport.cpp b/filters/kspread/html/htmlexport.cpp
new file mode 100644
index 000000000..f680207e4
--- /dev/null
+++ b/filters/kspread/html/htmlexport.cpp
@@ -0,0 +1,475 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Eva Brucherseifer <eva@kde.org>
+ Copyright (C) 2005 Bram Schoenmakers <bramschoenmakers@kde.nl>
+ based on kspread csv export filter by David Faure
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <htmlexport.h>
+#include <exportdialog.h>
+
+#include <tqfile.h>
+#include <tqtextcodec.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <KoFilterChain.h>
+#include <KoDocumentInfo.h>
+#include <kofficeversion.h>
+
+#include <kspread_map.h>
+#include <kspread_sheet.h>
+#include <kspread_doc.h>
+#include <kspread_util.h>
+
+using namespace KSpread;
+
+typedef KGenericFactory<HTMLExport, KoFilter> HTMLExportFactory;
+K_EXPORT_COMPONENT_FACTORY( libkspreadhtmlexport, HTMLExportFactory( "kofficefilters" ) )
+
+const TQString html_table_tag = "table";
+const TQString html_table_options = TQString(" border=\"%1\" cellspacing=\"%2\"");
+const TQString html_row_tag = "tr";
+const TQString html_row_options = "";
+const TQString html_cell_tag = "td";
+const TQString html_cell_options = "";
+const TQString html_bold = "b";
+const TQString html_italic = "i";
+const TQString html_underline = "u";
+const TQString html_right= "right";
+const TQString html_left= "left";
+const TQString html_center= "center";
+const TQString html_top="top";
+const TQString html_bottom="bottom";
+const TQString html_middle="middle";
+const TQString html_h1="h1";
+
+HTMLExport::HTMLExport(KoFilter *, const char *, const TQStringList&) :
+ KoFilter(), m_dialog( new ExportDialog() )
+{
+}
+
+HTMLExport::~HTMLExport()
+{
+ delete m_dialog;
+}
+
+// HTML enitities, AFAIK we don't need to escape " to &quot; (dnaber):
+const TQString strAmp ("&amp;");
+const TQString nbsp ("&nbsp;");
+const TQString strLt ("&lt;");
+const TQString strGt ("&gt;");
+
+// The reason why we use the KoDocument* approach and not the TQDomDocument
+// approach is because we don't want to export formulas but values !
+KoFilter::ConversionStatus HTMLExport::convert( const TQCString& from, const TQCString& to )
+{
+ if(to!="text/html" || from!="application/x-kspread")
+ {
+ kdWarning(30501) << "Invalid mimetypes " << to << " " << from << endl;
+ return KoFilter::NotImplemented;
+ }
+
+ KoDocument* document = m_chain->inputDocument();
+
+ if ( !document )
+ return KoFilter::StupidError;
+
+ if( !::tqqt_cast<const KSpread::Doc *>( document ) ) // it's safer that way :)
+ {
+ kdWarning(30501) << "document isn't a KSpread::Doc but a " << document->className() << endl;
+ return KoFilter::NotImplemented;
+ }
+
+ const Doc * ksdoc=static_cast<const Doc *>(document);
+
+ if( ksdoc->mimeType() != "application/x-kspread" )
+ {
+ kdWarning(30501) << "Invalid document mimetype " << ksdoc->mimeType() << endl;
+ return KoFilter::NotImplemented;
+ }
+
+ Sheet *sheet = ksdoc->map()->firstSheet();
+ TQString filenameBase = m_chain->outputFile();
+ filenameBase = filenameBase.left( filenameBase.findRev( '.' ) );
+
+ TQStringList sheets;
+ while( sheet != 0 )
+ {
+ int rows = 0;
+ int columns = 0;
+ detectFilledCells( sheet, rows, columns );
+ m_rowmap[ sheet->sheetName() ] = rows;
+ m_columnmap[ sheet->sheetName() ] = columns;
+
+ if( rows > 0 && columns > 0 )
+ {
+ sheets.append( sheet->sheetName() );
+ }
+ sheet = ksdoc->map()->nextSheet();
+ }
+ m_dialog->setSheets( sheets );
+
+ if( m_dialog->exec() == TQDialog::Rejected )
+ return KoFilter::UserCancelled;
+
+ sheets = m_dialog->sheets();
+ TQString str;
+ for( uint i = 0; i < sheets.count() ; ++i )
+ {
+ sheet = ksdoc->map()->findSheet( sheets[i] );
+
+ TQString file = fileName( filenameBase, sheet->sheetName(), sheets.count() > 1 );
+
+ if( m_dialog->separateFiles() || sheets[i] == sheets.first() )
+ {
+ str = TQString();
+ openPage( sheet, document, str );
+ writeTOC( sheets, filenameBase, str );
+ }
+
+ convertSheet( sheet, str, m_rowmap[ sheet->sheetName() ], m_columnmap[ sheet->sheetName() ] );
+
+ if( m_dialog->separateFiles() || sheets[i] == sheets.last() )
+ {
+ closePage( str );
+ TQFile out(file);
+ if(!out.open(IO_WriteOnly)) {
+ kdError(30501) << "Unable to open output file!" << endl;
+ out.close();
+ return KoFilter::FileNotFound;
+ }
+ TQTextStream streamOut(&out);
+ streamOut.setCodec( m_dialog->encoding() );
+ streamOut << str << endl;
+ out.close();
+ }
+
+ if( !m_dialog->separateFiles() )
+ {
+ createSheetSeparator( str );
+ }
+
+ }
+
+ emit sigProgress(100);
+ return KoFilter::OK;
+}
+
+void HTMLExport::openPage( Sheet *sheet, KoDocument *document, TQString &str )
+{
+ TQString title;
+ KoDocumentInfo *info = document->documentInfo();
+ KoDocumentInfoAbout *aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
+ if ( aboutPage && !aboutPage->title().isEmpty() )
+ title = aboutPage->title() + " - ";
+
+ title += sheet->sheetName();
+
+ // header
+ str = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" ";
+ str += " \"http://www.w3.org/TR/html4/loose.dtd\"> \n";
+ str += "<html>\n";
+ str += "<head>\n";
+ str += "<meta http-equiv=\"Content-Type\" ";
+ str += TQString("content=\"text/html; charset=%1\">\n").arg( m_dialog->encoding()->mimeName() );
+ str += "<meta name=\"Generator\" ";
+ str += "content=\"KSpread HTML Export Filter Version = ";
+ str += KOFFICE_VERSION_STRING;
+ str += "\">\n";
+
+ // Insert stylesheet
+ if( !m_dialog->customStyleURL().isEmpty() )
+ {
+ str += "<link ref=\"stylesheet\" type=\"text/css\" href=\"";
+ str += m_dialog->customStyleURL();
+ str += "\" title=\"Style\" >\n";
+ }
+
+ str += "<title>" + title + "</title>\n";
+ str += "</head>\n";
+ str += TQString("<body bgcolor=\"#FFFFFF\" dir=\"%1\">\n").arg(
+ sheet->isRightToLeft()?"rtl":"ltr");
+
+ str += "<a name=\"__top\">\n";
+}
+
+void HTMLExport::closePage( TQString &str )
+{
+ str += "<p align=\"" + html_center + "\"><a href=\"#__top\">" + i18n("Top") + "</a></p>\n";
+ str += "</body>\n";
+ str += "</html>\n\n";
+}
+
+void HTMLExport::convertSheet( Sheet *sheet, TQString &str, int iMaxUsedRow, int iMaxUsedColumn )
+{
+ TQString emptyLines;
+
+ // Either we get hold of KSpreadTable::m_dctCells and apply the old method below (for sorting)
+ // or, cleaner and already sorted, we use KSpreadTable's API (slower probably, though)
+ int iMaxRow = sheet->maxRow();
+
+ if( !m_dialog->separateFiles() )
+ str += "<a name=\"" + sheet->sheetName().lower().stripWhiteSpace() + "\">\n";
+
+ str += ("<h1>" + sheet->sheetName() + "</h1><br>\n");
+
+ // this is just a bad approximation which fails for documents with less than 50 rows, but
+ // we don't need any progress stuff there anyway :) (Werner)
+ int value=0;
+ int step=iMaxRow > 50 ? iMaxRow/50 : 1;
+ int i=1;
+
+ str += "<" + html_table_tag + html_table_options.arg( m_dialog->useBorders() ? "1" : "0" ).arg( m_dialog->pixelsBetweenCells() ) +
+ TQString("dir=\"%1\">\n").arg(sheet->isRightToLeft()?"rtl":"ltr");
+
+ unsigned int nonempty_cells_prev=0;
+
+ for ( int currentrow = 1 ; currentrow <= iMaxUsedRow ; ++currentrow, ++i )
+ {
+ if(i>step) {
+ value+=2;
+ emit sigProgress(value);
+ i=0;
+ }
+
+ TQString separators;
+ TQString line;
+ unsigned int nonempty_cells=0;
+ unsigned int colspan_cells=0;
+
+ for ( int currentcolumn = 1 ; currentcolumn <= iMaxUsedColumn ; currentcolumn++ )
+ {
+ Cell * cell = sheet->cellAt( currentcolumn, currentrow, false );
+ colspan_cells=cell->extraXCells();
+ if (cell->needsPrinting())
+ nonempty_cells++;
+ TQString text;
+ TQColor bgcolor = cell->bgColor(currentcolumn,currentrow);
+ // FIXME: some formatting seems to be missing with cell->text(), e.g.
+ // "208.00" in KSpread will be "208" in HTML (not always?!)
+ bool link = false;
+
+ if ( !cell->link().isEmpty() )
+ {
+ if ( localReferenceAnchor(cell->link()) )
+ {
+ text = cell->text();
+ }
+ else
+ {
+ text = " <A href=\"" + cell->link() + "\">" + cell->text() + "</A>";
+ link = true;
+ }
+ }
+ else
+ text=cell->strOutText();
+#if 0
+ switch( cell->content() ) {
+ case Cell::Text:
+ text = cell->text();
+ break;
+ case Cell::RichText:
+ case Cell::VisualFormula:
+ text = cell->text(); // untested
+ break;
+ case Cell::Formula:
+ cell->calc( TRUE ); // Incredible, cells are not calculated if the document was just opened
+ text = cell->valueString();
+ break;
+ }
+ text = cell->prefix(currentrow, currentcolumn) + " " + text + " "
+ + cell->postfix(currentrow, currentcolumn);
+#endif
+ line += " <" + html_cell_tag + html_cell_options;
+ if (text.isRightToLeft() != sheet->isRightToLeft())
+ line += TQString(" dir=\"%1\" ").arg(text.isRightToLeft()?"rtl":"ltr");
+ if (bgcolor.isValid() && bgcolor.name()!="#ffffff") // change color only for non-white cells
+ line += " bgcolor=\"" + bgcolor.name() + "\"";
+
+ switch((Format::Align)cell->defineAlignX())
+ {
+ case Format::Left:
+ line+=" align=\"" + html_left +"\"";
+ break;
+ case Format::Right:
+ line+=" align=\"" + html_right +"\"";
+ break;
+ case Format::Center:
+ line+=" align=\"" + html_center +"\"";
+ break;
+ case Format::Undefined:
+ break;
+ }
+ switch((Format::AlignY)cell-> format()->alignY(currentrow, currentcolumn))
+ {
+ case Format::Top:
+ line+=" valign=\"" + html_top +"\"";
+ break;
+ case Format::Middle:
+ line+=" valign=\"" + html_middle +"\"";
+ break;
+ case Format::Bottom:
+ line+=" valign=\"" + html_bottom +"\"";
+ break;
+ case Format::UndefinedY:
+ break;
+ }
+ line+=" width=\""+TQString::number(cell->width())+"\"";
+ line+=" height=\""+TQString::number(cell->height())+"\"";
+
+ if (cell->extraXCells()>0)
+ {
+ TQString tmp;
+ int extra_cells=cell->extraXCells();
+ line += " colspan=\"" + tmp.setNum(extra_cells+1) + "\"";
+ currentcolumn += extra_cells;
+ }
+ text = text.stripWhiteSpace();
+ if( text.at(0) == '!' ) {
+ // this is supposed to be markup, just remove the '!':
+ text = text.right(text.length()-1);
+ } else if ( !link ) {
+ // Escape HTML characters.
+ text.replace ('&' , strAmp)
+ .replace ('<' , strLt)
+ .replace ('>' , strGt)
+ .replace (' ' , nbsp);
+ }
+ line += ">\n";
+
+ if (cell->format()->textFontBold(currentcolumn,currentrow))
+ {
+ text.insert(0, "<" + html_bold + ">");
+ text.append("</" + html_bold + ">");
+ }
+ if (cell->format()->textFontItalic(currentcolumn,currentrow))
+ {
+ text.insert(0, "<" + html_italic + ">");
+ text.append("</" + html_italic + ">");
+ }
+ if (cell->format()->textFontUnderline(currentcolumn,currentrow))
+ {
+ text.insert(0, "<" + html_underline + ">");
+ text.append("</" + html_underline + ">");
+ }
+ TQColor textColor = cell->format()->textColor(currentcolumn,currentrow);
+ if (textColor.isValid() && textColor.name()!="#000000") // change color only for non-default text
+ {
+ text.insert(0, "<font color=\"" + textColor.name() + "\">");
+ text.append("</font>");
+ }
+ line += " " + text;
+ line += "\n </" + html_cell_tag + ">\n";
+ }
+
+ if (nonempty_cells == 0 && nonempty_cells_prev == 0) {
+ nonempty_cells_prev = nonempty_cells;
+ // skip line if there's more than one empty line
+ continue;
+ } else {
+ nonempty_cells_prev = nonempty_cells;
+ str += emptyLines;
+ str += "<" + html_row_tag + html_row_options + ">\n";
+ str += line;
+ str += "</" + html_row_tag + ">";
+ emptyLines = TQString();
+ // Append a CR, but in a temp string -> if no other real line,
+ // then those will be dropped
+ emptyLines += "\n";
+ }
+ }
+ str += "\n</" + html_table_tag + ">\n<br>\n";
+}
+
+void HTMLExport::createSheetSeparator( TQString &str )
+{
+ str += ("<p align=\"" + html_center + "\"><a href=\"#__top\">" + i18n("Top") + "</a></p>\n" );
+ str += "<hr width=\"80%\">\n";
+}
+
+void HTMLExport::writeTOC( const TQStringList &sheets, const TQString &base, TQString &str )
+{
+ // don't create TOC for 1 sheet
+ if( sheets.count() == 1 )
+ return;
+
+ str += "<p align=\"" + html_center + "\">\n";
+
+ for( uint i = 0 ; i < sheets.count() ; ++i )
+ {
+ str += "<a href=\"";
+
+ if( m_dialog->separateFiles() )
+ {
+ str += fileName( base, sheets[i], sheets.count() > 1 );
+ }
+ else
+ {
+ str += "#" + sheets[i].lower().stripWhiteSpace();
+ }
+
+ str += "\">" + sheets[i] + "</a>\n";
+ if( i != sheets.count() -1 )
+ str += " - ";
+ }
+
+ str += "</p><hr width=\"80%\">\n";
+}
+
+TQString HTMLExport::fileName( const TQString &base, const TQString &sheetName, bool multipleFiles )
+{
+ TQString fileName = base;
+ if( m_dialog->separateFiles() && multipleFiles )
+ {
+ fileName += "-" + sheetName;
+ }
+ fileName += ".html";
+
+ return fileName;
+}
+
+void HTMLExport::detectFilledCells( Sheet *sheet, int &rows, int &columns )
+{
+ int iMaxColumn = sheet->maxColumn();
+ int iMaxRow = sheet->maxRow();
+ rows = 0;
+ columns = 0;
+
+ for ( int currentrow = 1 ; currentrow <= iMaxRow ; ++currentrow)
+ {
+ Cell * cell = 0L;
+ int iUsedColumn=0;
+ for ( int currentcolumn = 1 ; currentcolumn <= iMaxColumn ; currentcolumn++ )
+ {
+ cell = sheet->cellAt( currentcolumn, currentrow, false );
+ TQString text;
+ if ( !cell->isDefault() && !cell->isEmpty() )
+ {
+ iUsedColumn = currentcolumn;
+ }
+ }
+ if (cell)
+ iUsedColumn += cell->extraXCells();
+ if (iUsedColumn > columns)
+ columns = iUsedColumn;
+ if ( iUsedColumn > 0 )
+ rows = currentrow;
+ }
+}
+
+#include <htmlexport.moc>