diff options
author | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-10-13 11:56:14 +0900 |
---|---|---|
committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-10-29 21:58:42 +0900 |
commit | 2879ff70be9271550477982a1a6371714db38562 (patch) | |
tree | c2054149dba923ab080fe7093432c7663a990111 /src/exporters/htmlexporter.cpp | |
parent | 3eb38d2556f676d1027746f20bf12a1dd74451ef (diff) | |
download | krecipes-2879ff70.tar.gz krecipes-2879ff70.zip |
Rearrange folders structure to remove unnecessary 'krecipes' second level subfolder
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
(cherry picked from commit 0c8ed6c9a4000af8f48581a81c4b5c2f5b9fd502)
Diffstat (limited to 'src/exporters/htmlexporter.cpp')
-rw-r--r-- | src/exporters/htmlexporter.cpp | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/src/exporters/htmlexporter.cpp b/src/exporters/htmlexporter.cpp new file mode 100644 index 0000000..b4c44f4 --- /dev/null +++ b/src/exporters/htmlexporter.cpp @@ -0,0 +1,570 @@ +/*************************************************************************** +* Copyright (C) 2003 by * +* Jason Kivlighn (jkivlighn@gmail.com) * +* Unai Garro (ugarro@users.sourceforge.net) * +* Cyril Bosselut (bosselut@b1project.com) * +* * +* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +***************************************************************************/ + +#include "htmlexporter.h" + +#include <tqptrdict.h> +#include <tqimage.h> +#include <tqfileinfo.h> +#include <tqdir.h> +#include <tqstylesheet.h> //for TQStyleSheet::escape() to escape for HTML +#include <dom/dom_element.h> +#include <tqpainter.h> +#include <tqfileinfo.h> + +#include <tdeconfig.h> +#include <kdebug.h> +#include <tdelocale.h> +#include <tdeglobal.h> +#include <tdehtml_part.h> +#include <tdehtmlview.h> +#include <kprogress.h> +#include <kstandarddirs.h> +#include <kurl.h> +#include <kiconloader.h> + +#include "datablocks/mixednumber.h" +#include "backends/recipedb.h" +#include "dialogs/setupdisplay.h" +#include "image.h" +#include "krepagelayout.h" + +#include <cmath> //for ceil() + +HTMLExporter::HTMLExporter( const TQString& filename, const TQString &format ) : + BaseExporter( filename, format ) +{ + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "Page Setup" ); + + //let's do everything we can to be sure at least some layout is loaded + TQString template_filename = config->readEntry( "Template", locate( "appdata", "layouts/Default.template" ) ); + if ( template_filename.isEmpty() || !TQFile::exists( template_filename ) ) + template_filename = locate( "appdata", "layouts/Default.template" ); + kdDebug() << "Using template file: " << template_filename << endl; + + setTemplate( template_filename ); + + //let's do everything we can to be sure at least some layout is loaded + m_layoutFilename = config->readEntry( "Layout", locate( "appdata", "layouts/Default.klo" ) ); + if ( m_layoutFilename.isEmpty() || !TQFile::exists( m_layoutFilename ) ) + m_layoutFilename = locate( "appdata", "layouts/Default.klo" ); + kdDebug() << "Using layout file: " << m_layoutFilename << endl; +} + +HTMLExporter::~HTMLExporter() +{ +} + +void HTMLExporter::setTemplate( const TQString &filename ) +{ + TQFile templateFile( filename ); + if ( templateFile.open( IO_ReadOnly ) ) { + m_templateFilename = filename; + m_templateContent = TQString( templateFile.readAll() ); + } + else + kdDebug()<<"couldn't find/open template file"<<endl; +} + +void HTMLExporter::setStyle( const TQString &filename ) +{ + m_layoutFilename = filename; +} + +int HTMLExporter::supportedItems() const +{ + int items = RecipeDB::All ^ RecipeDB::Properties; + + TQMap<TQString,bool>::const_iterator it = m_visibilityMap.find("properties"); + if ( it == m_visibilityMap.end() || it.data() == true ) + items |= RecipeDB::Properties; + + return RecipeDB::All; +} + +TQString HTMLExporter::createContent( const Recipe& recipe ) +{ + TQString templateCopy = m_templateContent; + + storePhoto( recipe ); + + populateTemplate( recipe, templateCopy ); + return templateCopy; +} + +TQString HTMLExporter::createContent( const RecipeList& recipes ) +{ + TQString fileContent; + + RecipeList::const_iterator recipe_it; + for ( recipe_it = recipes.begin(); recipe_it != recipes.end(); ++recipe_it ) { + fileContent += createContent(*recipe_it); + } + + return fileContent; +} + +TQString HTMLExporter::createHeader( const RecipeList & ) +{ + m_visibilityMap.clear(); + m_columnsMap.clear(); + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "Page Setup" ); + + m_error = false; + + if ( m_templateContent.isEmpty() ) { + TQString errorStr = i18n("<html><body>\n" + "<p><b>Error: </b>Unable to find a layout file, which is" + " needed to view the recipe.</p>" + "<p>Krecipes was probably not properly installed.</p>" + "</body></html>"); + m_error = true; + return errorStr; + } + + TQFile layoutFile( m_layoutFilename ); + TQString error; int line; int column; + TQDomDocument doc; + if ( !doc.setContent( &layoutFile, &error, &line, &column ) ) { + kdDebug()<<"Unable to load style information. Will create HTML without it..."<<endl; + } + else + processDocument(doc); + + //put all the recipe photos into this directory + TQDir dir; + TQFileInfo fi(fileName()); + dir.mkdir( fi.dirPath(true) + "/" + fi.baseName() + "_photos" ); + + RecipeList::const_iterator recipe_it; + + TDELocale*loc = TDEGlobal::locale(); + TQString encoding = loc->encoding(); + + TQString output = "<html>"; + output += "<head>"; + output += "<meta name=\"lang\" content=\"" + loc->language() + "\">\n"; + output += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"; + output += TQString( "<title>%1</title>" ).arg( i18n( "Krecipes Recipes" ) ); + + output += "<style type=\"text/css\">\n"; + + TQString cssContent; + TQFileInfo info(m_templateFilename); + TQFile cssFile(info.dirPath(true) + "/" + info.baseName() + ".css"); + kdDebug()<<info.dirPath(true) + "/" + info.baseName() + ".css"<<endl; + if ( cssFile.open( IO_ReadOnly ) ) { + cssContent = TQString( cssFile.readAll() ); + } + output += cssContent; + + output += m_cachedCSS; + m_cachedCSS = TQString::null; + output += "</style>"; + output += "</head>"; + output += "<body class=\"background\">"; + + return output; +} + +void HTMLExporter::beginObject( const TQString &object ) +{ + m_cachedCSS += "."+object+" { \n"; +} + +void HTMLExporter::endObject() +{ + m_cachedCSS += " } \n"; +} + +void HTMLExporter::loadBackgroundColor( const TQString &/*object*/, const TQColor& color ) +{ + m_cachedCSS += bgColorAsCSS(color); +} + +void HTMLExporter::loadFont( const TQString &/*object*/, const TQFont& font ) +{ + m_cachedCSS += fontAsCSS(font); +} + +void HTMLExporter::loadTextColor( const TQString &/*object*/, const TQColor& color ) +{ + m_cachedCSS += textColorAsCSS(color); +} + +void HTMLExporter::loadVisibility( const TQString &object, bool visible ) +{ + m_cachedCSS += visibilityAsCSS(visible); + m_visibilityMap.insert(object,visible); +} + +void HTMLExporter::loadAlignment( const TQString &/*object*/, int alignment ) +{ + m_cachedCSS += alignmentAsCSS(alignment); +} + +void HTMLExporter::loadBorder( const TQString &/*object*/, const KreBorder& border ) +{ + m_cachedCSS += borderAsCSS(border); +} + +void HTMLExporter::loadColumns( const TQString & object, int cols ) +{ + m_columnsMap.insert(object,cols); +kdDebug()<<object<<" has "<<cols<<" columns"<<endl; +} + +TQString HTMLExporter::createFooter() +{ + if ( m_error ) + return TQString::null; + + return "</body></html>"; +} + +void HTMLExporter::storePhoto( const Recipe &recipe ) +{ + TQImage image; + TQString photo_name; + if ( recipe.photo.isNull() ) { + image = TQImage( defaultPhoto ); + photo_name = "default_photo"; + } + else { + image = recipe.photo.convertToImage(); + photo_name = TQString::number(recipe.recipeID); + } + + TQPixmap pm = image;//image.smoothScale( phwidth, 0, TQImage::ScaleMax ); + + TQFileInfo fi(fileName()); + TQString photo_path = fi.dirPath(true) + "/" + fi.baseName() + "_photos/" + photo_name + ".png"; + if ( !TQFile::exists( photo_path ) ) { + pm.save( photo_path, "PNG" ); + } +} + +TQString HTMLExporter::HTMLIfVisible( const TQString &name, const TQString &html ) +{ + TQMap<TQString,bool>::const_iterator it = m_visibilityMap.find(name); + if ( it == m_visibilityMap.end() || it.data() == true ) + return html; + else + return TQString::null; +} + +void HTMLExporter::populateTemplate( const Recipe &recipe, TQString &content ) +{ + TDEConfig * config = TDEGlobal::config(); + + //=======================TITLE======================// + content = content.replace("**TITLE**",HTMLIfVisible("title",recipe.title)); + + //=======================INSTRUCTIONS======================// + TQString instr_html = TQStyleSheet::escape( recipe.instructions ); + instr_html.replace( "\n", "<br />" ); + content = content.replace( "**INSTRUCTIONS**", HTMLIfVisible("instructions",instr_html) ); + + //=======================SERVINGS======================// + TQString yield_html = TQString( "<b>%1: </b>%2" ).arg( i18n( "Yield" ) ).arg( recipe.yield.toString() ); + content = content.replace( "**YIELD**", HTMLIfVisible("yield",yield_html) ); + + //=======================PREP TIME======================// + TQString preptime_html; + if ( !recipe.prepTime.isNull() && recipe.prepTime.isValid() ) + preptime_html = TQString( "<b>%1: </b>%2" ).arg( i18n( "Preparation Time" ) ).arg( recipe.prepTime.toString( "h:mm" ) ); + content = content.replace( "**PREP_TIME**", HTMLIfVisible("prep_time",preptime_html) ); + + //========================PHOTO========================// + TQString photo_name; + if ( recipe.photo.isNull() ) + photo_name = "default_photo"; + else + photo_name = TQString::number(recipe.recipeID); + + TQFileInfo fi(fileName()); + TQString image_url = fi.baseName() + "_photos/" + escape( photo_name ) + ".png"; + image_url = KURL::encode_string( image_url ); + content = content.replace( "**PHOTO**", HTMLIfVisible("photo",image_url) ); + + //=======================AUTHORS======================// + TQString authors_html; + + int counter = 0; + for ( ElementList::const_iterator author_it = recipe.authorList.begin(); author_it != recipe.authorList.end(); ++author_it ) { + if ( counter ) + authors_html += ", "; + authors_html += TQStyleSheet::escape( ( *author_it ).name ); + counter++; + } + if ( !authors_html.isEmpty() ) + authors_html.prepend( TQString( "<b>%1: </b>" ).arg( i18n( "Authors" ) ) ); + content = content.replace( "**AUTHORS**", HTMLIfVisible("authors",authors_html) ); + + //=======================CATEGORIES======================// + TQString categories_html; + + counter = 0; + for ( ElementList::const_iterator cat_it = recipe.categoryList.begin(); cat_it != recipe.categoryList.end(); ++cat_it ) { + if ( counter ) + categories_html += ", "; + categories_html += TQStyleSheet::escape( ( *cat_it ).name ); + counter++; + } + if ( !categories_html.isEmpty() ) + categories_html.prepend( TQString( "<b>%1: </b>" ).arg( i18n( "Categories" ) ) ); + + content = content.replace( "**CATEGORIES**", HTMLIfVisible("categories",categories_html) ); + + //=======================INGREDIENTS======================// + TQString ingredients_html; + config->setGroup( "Formatting" ); + + bool useAbbreviations = config->readBoolEntry("AbbreviateUnits"); + + MixedNumber::Format number_format = ( config->readBoolEntry( "Fraction" ) ) ? MixedNumber::MixedNumberFormat : MixedNumber::DecimalFormat; + + TQString ingredient_format = config->readEntry( "Ingredient", "%n%p: %a %u" ); + + TQMap<TQString,int>::const_iterator cols_it = m_columnsMap.find("ingredients"); + int cols = 1; + if ( cols_it != m_columnsMap.end() ) + cols = cols_it.data(); + int per_col = recipe.ingList.count() / cols; + if ( recipe.ingList.count() % cols != 0 ) //round up if division is not exact + per_col++; + + int count = 0; + IngredientList list_copy = recipe.ingList; //simple workaround until I fix iterating over the list dealing with groups + for ( IngredientList group_list = list_copy.firstGroup(); group_list.count() != 0; group_list = list_copy.nextGroup() ) { + TQString group = group_list[ 0 ].group; //just use the first's name... they're all the same + + bool loneHeader = false; + if ( count != 0 && count % per_col == 0 ) { + loneHeader = true; + if ( !group.isEmpty() ) + ingredients_html += "</ul>"; + ingredients_html.append("</ul></td><td valign=\"top\"><ul>"); + if ( !group.isEmpty() ) + ingredients_html += "<li style=\"page-break-after: avoid\">" + group + ":</li><ul>"; + } + else { + if ( !group.isEmpty() ) + ingredients_html += "<li style=\"page-break-after: avoid\">" + group + ":</li><ul>"; + } + + for ( IngredientList::const_iterator ing_it = group_list.begin(); ing_it != group_list.end(); ++ing_it, ++count ) { + if ( count != 0 && count % per_col == 0 && !loneHeader ) { + if ( !group.isEmpty() ) + ingredients_html += "</ul>"; + ingredients_html.append("</ul></td><td valign=\"top\"><ul>"); + if ( !group.isEmpty() ) + ingredients_html += "<ul>"; + } + + TQString amount_str = MixedNumber( ( *ing_it ).amount ).toString( number_format ); + + if ( (*ing_it).amount_offset > 0 ) + amount_str += "-"+MixedNumber( ( *ing_it ).amount + ( *ing_it ).amount_offset ).toString( number_format ); + else if ( ( *ing_it ).amount <= 1e-10 ) + amount_str = ""; + + TQString unit = ( *ing_it ).units.determineName( ( *ing_it ).amount + ( *ing_it ).amount_offset, useAbbreviations ); + + TQString tmp_format( ingredient_format ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%n" ) ), TQStyleSheet::escape( ( *ing_it ).name ) ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%a" ) ), amount_str ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%u" ) ), TQStyleSheet::escape(unit) ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%p" ) ), ( ( *ing_it ).prepMethodList.count() == 0 ) ? + TQString::fromLatin1( "" ) : TQString::fromLatin1( "; " ) + TQStyleSheet::escape( ( *ing_it ).prepMethodList.join(",") ) ); + + if ( (*ing_it).substitutes.count() > 0 ) + tmp_format += ", "+i18n("or"); + + ingredients_html += TQString( "<li>%1</li>" ).arg( tmp_format ); + + for ( TQValueList<IngredientData>::const_iterator sub_it = (*ing_it).substitutes.begin(); sub_it != (*ing_it).substitutes.end(); ) { + TQString amount_str = MixedNumber( ( *sub_it ).amount ).toString( number_format ); + + if ( (*ing_it).amount_offset > 0 ) + amount_str += "-"+MixedNumber( ( *sub_it ).amount + ( *sub_it ).amount_offset ).toString( number_format ); + else if ( ( *sub_it ).amount <= 1e-10 ) + amount_str = ""; + + TQString unit = ( *sub_it ).units.determineName( ( *sub_it ).amount + ( *sub_it ).amount_offset, config->readBoolEntry("AbbreviateUnits") ); + + TQString tmp_format( ingredient_format ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%n" ) ), TQStyleSheet::escape( ( *sub_it ).name ) ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%a" ) ), amount_str ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%u" ) ), TQStyleSheet::escape(unit) ); + tmp_format.replace( TQRegExp( TQString::fromLatin1( "%p" ) ), ( ( *sub_it ).prepMethodList.count() == 0 ) ? + TQString::fromLatin1( "" ) : TQString::fromLatin1( "; " ) + TQStyleSheet::escape( ( *sub_it ).prepMethodList.join(",") ) ); + + ++sub_it; + if ( sub_it != (*ing_it).substitutes.end() ) + tmp_format += ", "+i18n("or"); + ingredients_html += TQString( "<li>%1</li>" ).arg( tmp_format ); + } + } + + if ( !group.isEmpty() ) + ingredients_html += "</ul>"; + } + if ( !ingredients_html.isEmpty() ) { + ingredients_html.prepend( "<table><tr><td valign=\"top\"><ul>" ); + ingredients_html.append( "</ul></td></tr></table>" ); + } + content = content.replace( "**INGREDIENTS**", HTMLIfVisible("ingredients",ingredients_html) ); + + //=======================PROPERTIES======================// + TQString properties_html; + + TQStringList hiddenList = config->readListEntry("HiddenProperties"); + IngredientPropertyList visibleProperties; + for ( IngredientPropertyList::const_iterator prop_it = recipe.properties.begin(); prop_it != recipe.properties.end(); ++prop_it ) { + if ( hiddenList.find((*prop_it).name) == hiddenList.end() ) + visibleProperties.append( *prop_it ); + } + + cols_it = m_columnsMap.find("properties"); + cols = 1; + if ( cols_it != m_columnsMap.end() ) + cols = cols_it.data(); + per_col = visibleProperties.count() / cols; + if ( visibleProperties.count() % cols != 0 ) //round up if division is not exact + per_col++; + + count = 0; + for ( IngredientPropertyList::const_iterator prop_it = visibleProperties.begin(); prop_it != visibleProperties.end(); ++prop_it ) { + if ( count != 0 && count % per_col == 0 ) + properties_html.append("</ul></td><td valign=\"top\"><ul>"); + + // if the amount given is <0, it means the property calculator found that the property was undefined for some ingredients, so the amount will be actually bigger + + TQString amount_str; + + double prop_amount = (*prop_it).amount; + if ( prop_amount > 0 ) { //TODO: make the precision configuratble + prop_amount = double( tqRound( prop_amount * 10.0 ) ) / 10.0; //not a "chemistry experiment" ;) Let's only have one decimal place + amount_str = beautify( TDEGlobal::locale() ->formatNumber( prop_amount, 5 ) ); + } + else + amount_str = "0"; + + properties_html += TQString( "<li>%1: <nobr>%2 %3</nobr></li>" ) + .arg( TQStyleSheet::escape( (*prop_it).name ) ) + .arg( amount_str ) + .arg( TQStyleSheet::escape( (*prop_it).units ) ); + + ++count; + } + + if ( !properties_html.isEmpty() ) { + properties_html.prepend( "<table><tr><td valign=\"top\"><ul>" ); + properties_html.append( "</ul></td></tr></table>" ); + } + content = content.replace( "**PROPERTIES**", HTMLIfVisible("properties",properties_html) ); + + //=======================RATINGS======================// + TQString ratings_html; + if ( recipe.ratingList.count() > 0 ) + ratings_html += TQString("<b>%1:</b>").arg(i18n("Ratings")); + + int rating_total = 0; + double rating_sum = 0; + for ( RatingList::const_iterator rating_it = recipe.ratingList.begin(); rating_it != recipe.ratingList.end(); ++rating_it ) { + ratings_html += "<hr />"; + + if ( !( *rating_it ).rater.isEmpty() ) + ratings_html += "<p><b>"+( *rating_it ).rater+"</b></p>"; + + if ( (*rating_it).ratingCriteriaList.count() > 0 ) + ratings_html += "<table>"; + for ( RatingCriteriaList::const_iterator rc_it = (*rating_it).ratingCriteriaList.begin(); rc_it != (*rating_it).ratingCriteriaList.end(); ++rc_it ) { + TQString image_url = fi.baseName() + "_photos/" + TQString::number((*rc_it).stars) + "-stars.png"; + image_url = KURL::encode_string( image_url ); + ratings_html += "<tr><td>"+(*rc_it).name+":</td><td><img src=\""+image_url+"\" /></td></tr>"; + if ( !TQFile::exists( fi.dirPath(true) + "/" + image_url ) ) { + TQPixmap starPixmap = Rating::starsPixmap((*rc_it).stars,true); + starPixmap.save( fi.dirPath(true) + "/" + image_url, "PNG" ); + } + + rating_total++; + rating_sum += (*rc_it).stars; + } + if ( (*rating_it).ratingCriteriaList.count() > 0 ) + ratings_html += "</table>"; + + if ( !( *rating_it ).comment.isEmpty() ) + ratings_html += "<p><i>"+( *rating_it ).comment+"</i></p>"; + } + content = content.replace( "**RATINGS**", HTMLIfVisible("ratings",ratings_html) ); + + TQString overall_html; + if ( rating_total > 0 ) { + double average = int(2*rating_sum/rating_total)/2; + overall_html += TQString("<b>%1:</b>").arg(i18n("Overall Rating")); + TQString image_url = fi.baseName() + "_photos/" + TQString::number(average) + "-stars.png"; + image_url = KURL::encode_string( image_url ); + overall_html += "<img src=\""+image_url+"\" />"; + if ( !TQFile::exists( fi.dirPath(true) + "/" + image_url ) ) { + TQPixmap starPixmap = Rating::starsPixmap(average,true); + starPixmap.save( fi.dirPath(true) + "/" + image_url, "PNG" ); + } + } + content = content.replace( "**OVERALL_RATING**", HTMLIfVisible("overall_rating",overall_html) ); +} + +void HTMLExporter::removeHTMLFiles( const TQString &filename, int recipe_id ) +{ + TQValueList<int> id; + id << recipe_id; + removeHTMLFiles( filename, id ); +} + +void HTMLExporter::removeHTMLFiles( const TQString &filename, const TQValueList<int> &recipe_ids ) +{ + //remove HTML file + TQFile old_file( filename + ".html" ); + if ( old_file.exists() ) + old_file.remove(); + + //remove photos + for ( TQValueList<int>::const_iterator it = recipe_ids.begin(); it != recipe_ids.end(); ++it ) { + TQFile photo( filename + "_photos/" + TQString::number(*it) + ".png" ); + if ( photo.exists() ) + photo.remove(); //remove photos in directory before removing it + } + + //take care of the default photo + TQFile photo( filename + "_photos/default_photo.png" ); + if ( photo.exists() ) photo.remove(); + + //remove photo directory + TQDir photo_dir; + photo_dir.rmdir( filename + "_photos" ); + + for ( double d = 0.5; d < 5.5; d += 0.5 ) { + if ( TQFile::exists(filename+"_photos/"+TQString::number(d)+"-stars.png") ) photo.remove(filename+"_photos/"+TQString::number(d)+"-stars.png"); + } +} + +TQString HTMLExporter::escape( const TQString & str ) +{ + TQString tmp( str ); + return tmp.replace( '/', "_" ); +} |