Chefkoch PDF export

Erzeugt aus einem Rezept ein PDF Dokument zum Herunterladen oder Drucken

当前为 2016-01-04 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Chefkoch PDF export
// @description Erzeugt aus einem Rezept ein PDF Dokument zum Herunterladen oder Drucken
// @namespace   cuzi
// @oujs:author cuzi
// @version     1
// @include     http://www.chefkoch.de/rezepte/*
// @grant       GM_xmlhttpRequest
// @require https://greasyfork.org/scripts/15924-jspdf/code/jsPDF.js
// ==/UserScript==

function convertImgToDataURLviaCanvas(url, outputFormat, callback){
  var img = new Image();
  img.crossOrigin = 'Anonymous';
  img.onload = function(){
    var canvas = document.createElement('CANVAS');
    var ctx = canvas.getContext('2d');
    var dataURL;
    canvas.height = this.height;
    canvas.width = this.width;
    ctx.drawImage(this, 0, 0);
    dataURL = canvas.toDataURL(outputFormat);
    callback(dataURL, this.height, this.width);
    canvas = null; 
  };
  img.src = url;
}

function trimArray(arr) {
  return arr.map((e) => e.trim());
}
function trimMultiline(s) {
  return trimArray(s.split("\n")).join("\n").trim();
}

function splitText(doc, s, size) {
  size = size?size:500;
  var p = s.split("\n");
  var r = [];
  for(var i = 0; i < p.length; i++) {
    var t = p[i].trim();
    if(t) {
      r.push(t);
    }
  }
  s = r.join("\n").trim();
  return doc.splitTextToSize(s, size);
}

function makeColums(doc, x, y, fontSize, columnSep, rowSep, data) {
  /* Write text to pdf in columns
  data = [
             [text1, text2, text2],
             [text3, text4, text5],
             ....
         ]
  */
  doc.setFontSize(fontSize);
  
  var columnWidth = [];
  for(var i = 0; i < data.length; i++) {
    for(var j = 0; j < data[i].length; j++) {
      var textWidth = doc.getStringUnitWidth(data[i][j]);
      if(columnWidth[j]) {
        columnWidth[j] = Math.max(columnWidth[j], fontSize * textWidth);
      } else {
        columnWidth.push(fontSize * textWidth);
      }
    }
  }
  
  var start_x = x;
  
  for(var i = 0; i < data.length; i++) {
    for(var j = 0; j < data[i].length; j++) {
      doc.text(x, y, data[i][j]);
      x += columnWidth[j] + columnSep;
    }
    x = start_x;
    y += doc.getLineHeight() + rowSep;
  }
  
  // Return total width and height
  var total_width = columnWidth.length * Math.max(...columnWidth) + columnWidth.length * columnSep;
  var total_height = data.length * doc.getLineHeight() + data.length * rowSep;
  return [total_width, total_height];
}

function Layout(doc, x, y) {
  var lineSep = 0;
  var width = 500;
  var start_x = x;
  var start_y = y;
  
  this.move = function move(toX,toY) {
    x = toX;
    y = toY;
  };
  
  this.pos = function pos() {
    return {"x" : x, "y" : y};
  };
  
  this.pageWidth = function pageWidth() {
    return width;
  };
  
  this.setLineSep = function setLineSep(newlineSep) {
    lineSep = newlineSep
    return this;
  };
  
  this.text = function text(fontSize, str) {
    doc.setFontSize(fontSize);
  
    doc.text(x, y, doc.splitTextToSize(str, width-x));
    
    x += doc.getStringUnitWidth(str) * fontSize;
    return this;
  };
  
  this.line = function line(fontSize, str) {
    doc.setFontSize(fontSize);
        
    x = start_x;
    var textHeight = doc.splitTextToSize(str, width-x).length * doc.getLineHeight();
    this.text(fontSize, str);
    y += textHeight + lineSep;
    return this;
  };
  
  this.r = function r() {
    x = start_x;
  };
  
  this.br = function br(numberOfNewLines) {
    if(numberOfNewLines === -1) {
      x = start_x;
      y -= doc.getLineHeight() - lineSep;
    } else if(numberOfNewLines > 1) {
      for(var i = 0; i < numberOfNewLines; i ++) {
        br(1);
      }
    } else if(numberOfNewLines < 0) {
      for(var i = 0; i < -numberOfNewLines; i ++) {
        br(-1);
      }
    } else {
      x = start_x;
      y += doc.getLineHeight() + lineSep;
    }
    return this;
  };
  
  this.columns = function columns(fontSize, columnSep, rowSep, data) {
    var res = makeColums(doc, x, y, fontSize, columnSep, rowSep, data);
    x += res[0];
    y += res[1] + lineSep;
    return this;
  };
  
  
}


function RecipePage() {
  if(!document.querySelector("#recipe-incredients")) {
    throw Error("RecipePage() needs a recipe page");
  }

  this.title = function getTitle() {
    return document.querySelector("h1").textContent.trim();
  };

  this.summary = function getSummary() {
    return document.querySelector(".summary")?document.querySelector(".summary").textContent.trim():"";
  };

  this.ingredients = function getIngredients() {
    var ingredients = [];
    var tr = document.querySelectorAll("#recipe-incredients tr");
    for(var i = 0; i < tr.length; i++) {
      var td = tr[i].getElementsByTagName("td");

      var c = [];
      for(var j = 0; j < td.length; j++) {
        c.push(td[j].textContent.trim());
      }
      if(c) {
        ingredients.push(c);
      }
    }
    return ingredients;
  };

  this.servings = function getServings() {
    return (document.querySelector("#divisor").value + ' ' + document.querySelector("#divisor").nextElementSibling.firstChild.data).trim();
  };

  this.details = function getDetails() {
    return trimMultiline(document.querySelector("#rezept-zubereitung").previousElementSibling.textContent.trim().replace(/(\s)\s*/g,"$1").replace(/\n/g," "));
  };

  this.instructions = function getInstructions() {
    return trimMultiline(document.querySelector("#rezept-zubereitung").textContent);
  };

  this.imageURL = function getImageURL() {
    if(document.querySelectorAll("#slideshow a")[0].href) {
      return Array.from(document.querySelectorAll("#slideshow a")).filter((e) => e.style.display == 'block')[0].href.toString();
    } else {
      return false;
    }
  };

}

function makePdf(cb, recipe, imageData, imgWidth, imgHeight) {

  var doc = new jsPDF("portrait", 'pt', 'a4');
  var layout = new Layout(doc, 20, 20);

  layout.setLineSep(5);

  layout.line(14,recipe.title());
  layout.line(11, recipe.summary());
  var image_y_start = layout.pos().y;
  layout.br();
  layout.line(13, "Zutaten (für "+recipe.servings()+")").r();
  layout.columns(12, 20, 5, recipe.ingredients());
  var image_x_start = layout.pos().x;
  var image_y_end = layout.pos().y;
  layout.line(13, "Zubereitung");
  layout.line(12, recipe.details());
  layout.line(12, recipe.instructions());
  
  if(imageData) {
    var newImageWidth = layout.pageWidth() - image_x_start;
    var newImageHeight = image_y_end - image_y_start;
    var scale = Math.min(newImageWidth/imgWidth, newImageHeight/imgHeight);
    newImageWidth  = Math.ceil(scale * imgWidth);
    newImageHeight = Math.ceil(scale * imgHeight);

    doc.addImage(imageData, 'JPEG', image_x_start, image_y_start, newImageHeight, newImageWidth);
  }
  
  var datauristring = doc.output('datauristring');
  
  var div = document.createElement("div");
  div.style = "background:#90b262; position:absolute; top:15px; left:2px; padding:3px 5px;";
  document.body.appendChild(div);
  var head = document.createElement("div");
  head.style = "color:White; height:30px;";
  head.appendChild(document.createTextNode("PDF Dokument: "));
  var a = document.createElement("a");
  a.style = "margin-left:10px; color:white; text-decoration:underline";
  a.href = datauristring;
  a.target = '_blank';
  a.appendChild(document.createTextNode("Download"));
  head.appendChild(a);
  var close = document.createElement("a");
  head.appendChild(close);
  close.innerHTML = '<button id="cboxClose" style="top: -15px;" type="button"><span>x</span></button>';
  close.style = "cursor:pointer;";
  close.addEventListener("click",function() {document.body.removeChild(div);});
  div.appendChild(head);
  var iframe = document.createElement("iframe");
  iframe.style = "width:400px; height:600px; ";
  div.appendChild(iframe);
  iframe.src = datauristring;
  cb(doc);
}

function downloadImageAndMakePdf(cb) {
  var recipe = new RecipePage();
  
  if(recipe.imageURL()) {
    convertImgToDataURLviaCanvas(recipe.imageURL(), "jpeg", function(dataURI, width, height) {
      makePdf(cb, recipe, dataURI, width, height);
    });
  } else {
    makePdf(cb, recipe, false);
  }
};

(function () {
  var a = document.querySelector("#recipe-buttons a").cloneNode();
  a.innerHTML = "PDF";
  a.href = "javascript:void(0)";
  a.title = "PDF Dokument erzeugen";
  a.className = "button-green  button-file-export";
  var click = function() {
    a.innerHTML = "Warten auf PDF..."; 
    window.setTimeout(function() {
      downloadImageAndMakePdf(function(doc) {
        window.setTimeout(function() {
        a.innerHTML = "PDF erstellt."; 
        a.title = "Hier klicken um PDF zu öffnen. Rechtsklick zum Speichern.";
        a.removeEventListener("click", click); 
        a.href = doc.output('datauristring');
        }, 5000);
      });
    },1); 
  };
  a.addEventListener("click", click);
  document.querySelector("#recipe-buttons").insertBefore(a, document.querySelector("#recipe-buttons a"));
})();