Chefkoch PDF export

Erzeugt aus einem Rezept ein PDF Dokument zum Herunterladen oder Drucken

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

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

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

您需要先安装一个扩展,例如 篡改猴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     2
// @include     http://www.chefkoch.de/rezepte/*
// @grant       GM_xmlhttpRequest
// @require     https://greasyfork.org/scripts/15924-jspdf/code/jsPDF.js
// ==/UserScript==

function convertImgToDataURL(url, callback){
  GM_xmlhttpRequest({
    method: "GET",
    responseType : "blob",
    url: url,
    onload: function(response) {
      var reader  = new FileReader();
      reader.onloadend = function () {
        var img = new Image();
        img.onload = function(){
          callback(reader.result, parseInt(this.width,10), parseInt(this.height,10));
        };
        img.src = reader.result;
      };
      reader.readAsDataURL(response.response);
    },
  });
}

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.reduce((a, b) => a+b) + 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 = 595; // A4 = 495pt x 842pt
  var height = 842;
  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) {
      var imgs = Array.from(document.querySelectorAll("#slideshow a")).filter((e) => e.style.display == 'block');
      if(imgs.length && imgs[0] && imgs[0].href) {
        return imgs[0].href.toString();
      }
    }
    return false;
  };

}

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

  // Generate PDF
  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) {
    // Insert Image
    var paddingLeft = 10;
    var paddingRight = 10;
    var newImageWidth = layout.pageWidth() - image_x_start - paddingLeft - paddingRight;
    var newImageHeight = image_y_end - image_y_start;
    if(Math.min(newImageWidth, newImageHeight) > 20) { // Do not include images, if there's less than 20pt available
      var scale = Math.min(newImageWidth/imgWidth, newImageHeight/imgHeight);
      newImageWidth  = Math.floor(scale * imgWidth);
      newImageHeight = Math.floor(scale * imgHeight);
      doc.addImage(imageData, 'JPEG', image_x_start+paddingLeft, image_y_start, newImageWidth, newImageHeight);
    }
  }
  
  // Show PDF
  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: "+(parseInt((datauristring.length-28)*0.75/102.4)/10)+"kB"));
  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) {
  // Download the currently selected image and then create the PDF document
  var recipe = new RecipePage();
  
  if(recipe.imageURL()) {
    convertImgToDataURL(recipe.imageURL(), function(dataURI, width, height) {
      makePdf(cb, recipe, dataURI, width, height);
    });
  } else {
    makePdf(cb, recipe, false);
  }
};

(function () {
  // Show Button
  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"));
})();