/*************************************************************************** * 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 #include #include #include #include //for TQStyleSheet::escape() to escape for HTML #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datablocks/mixednumber.h" #include "backends/recipedb.h" #include "dialogs/setupdisplay.h" #include "image.h" #include "krepagelayout.h" #include //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"<::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("\n" "

Error: Unable to find a layout file, which is" " needed to view the recipe.

" "

Krecipes was probably not properly installed.

" ""); 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..."<encoding(); TQString output = ""; output += ""; output += "language() + "\">\n"; output += "\n"; output += TQString( "%1" ).arg( i18n( "Krecipes Recipes" ) ); output += ""; output += ""; output += ""; 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()<"; } 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::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", "
" ); content = content.replace( "**INSTRUCTIONS**", HTMLIfVisible("instructions",instr_html) ); //=======================SERVINGS======================// TQString yield_html = TQString( "%1: %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( "%1: %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( "%1: " ).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( "%1: " ).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::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 += ""; ingredients_html.append("
    "); if ( !group.isEmpty() ) ingredients_html += "
  • " + group + ":
    • "; } else { if ( !group.isEmpty() ) ingredients_html += "
    • " + group + ":
      • "; } 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 += "
      "; ingredients_html.append("
      "); if ( !group.isEmpty() ) ingredients_html += "
        "; } 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( "
      • %1
      • " ).arg( tmp_format ); for ( TQValueList::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( "
      • %1
      • " ).arg( tmp_format ); } } if ( !group.isEmpty() ) ingredients_html += "
      "; } if ( !ingredients_html.isEmpty() ) { ingredients_html.prepend( "
        " ); ingredients_html.append( "
      " ); } 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("
      "); // 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( "
    • %1: %2 %3
    • " ) .arg( TQStyleSheet::escape( (*prop_it).name ) ) .arg( amount_str ) .arg( TQStyleSheet::escape( (*prop_it).units ) ); ++count; } if ( !properties_html.isEmpty() ) { properties_html.prepend( "
        " ); properties_html.append( "
      " ); } content = content.replace( "**PROPERTIES**", HTMLIfVisible("properties",properties_html) ); //=======================RATINGS======================// TQString ratings_html; if ( recipe.ratingList.count() > 0 ) ratings_html += TQString("%1:").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 += "
      "; if ( !( *rating_it ).rater.isEmpty() ) ratings_html += "

      "+( *rating_it ).rater+"

      "; if ( (*rating_it).ratingCriteriaList.count() > 0 ) ratings_html += ""; 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 += ""; 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 += "
      "+(*rc_it).name+":
      "; if ( !( *rating_it ).comment.isEmpty() ) ratings_html += "

      "+( *rating_it ).comment+"

      "; } 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("%1:").arg(i18n("Overall Rating")); TQString image_url = fi.baseName() + "_photos/" + TQString::number(average) + "-stars.png"; image_url = KURL::encode_string( image_url ); overall_html += ""; 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 id; id << recipe_id; removeHTMLFiles( filename, id ); } void HTMLExporter::removeHTMLFiles( const TQString &filename, const TQValueList &recipe_ids ) { //remove HTML file TQFile old_file( filename + ".html" ); if ( old_file.exists() ) old_file.remove(); //remove photos for ( TQValueList::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( '/', "_" ); }