NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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://raw.githubusercontent.com/MrRio/jsPDF/master/dist/jspdf.min.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")); })();