diff options
Diffstat (limited to 'filters/kspread/html/htmlexport.cpp')
-rw-r--r-- | filters/kspread/html/htmlexport.cpp | 475 |
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 " (dnaber): +const TQString strAmp ("&"); +const TQString nbsp (" "); +const TQString strLt ("<"); +const TQString strGt (">"); + +// 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> |