Chefkoch PDF export

Erzeugt aus einem Rezept ein PDF Dokument zum Herunterladen oder Drucken

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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"));
})();