Chefkoch PDF export

Erzeugt aus einem Rezept ein PDF Dokument zum Herunterladen oder Drucken

  1. // ==UserScript==
  2. // @name Chefkoch PDF export
  3. // @description Erzeugt aus einem Rezept ein PDF Dokument zum Herunterladen oder Drucken
  4. // @namespace cuzi
  5. // @oujs:author cuzi
  6. // @version 2
  7. // @include http://www.chefkoch.de/rezepte/*
  8. // @grant GM_xmlhttpRequest
  9. // @require https://gf.qytechs.cn/scripts/15924-jspdf/code/jsPDF.js
  10. // ==/UserScript==
  11.  
  12. function convertImgToDataURL(url, callback){
  13. GM_xmlhttpRequest({
  14. method: "GET",
  15. responseType : "blob",
  16. url: url,
  17. onload: function(response) {
  18. var reader = new FileReader();
  19. reader.onloadend = function () {
  20. var img = new Image();
  21. img.onload = function(){
  22. callback(reader.result, parseInt(this.width,10), parseInt(this.height,10));
  23. };
  24. img.src = reader.result;
  25. };
  26. reader.readAsDataURL(response.response);
  27. },
  28. });
  29. }
  30.  
  31. function trimArray(arr) {
  32. return arr.map((e) => e.trim());
  33. }
  34. function trimMultiline(s) {
  35. return trimArray(s.split("\n")).join("\n").trim();
  36. }
  37.  
  38. function splitText(doc, s, size) {
  39. size = size?size:500;
  40. var p = s.split("\n");
  41. var r = [];
  42. for(var i = 0; i < p.length; i++) {
  43. var t = p[i].trim();
  44. if(t) {
  45. r.push(t);
  46. }
  47. }
  48. s = r.join("\n").trim();
  49. return doc.splitTextToSize(s, size);
  50. }
  51.  
  52. function makeColums(doc, x, y, fontSize, columnSep, rowSep, data) {
  53. /* Write text to pdf in columns
  54. data = [
  55. [text1, text2, text2],
  56. [text3, text4, text5],
  57. ....
  58. ]
  59. */
  60. doc.setFontSize(fontSize);
  61. var columnWidth = [];
  62. for(var i = 0; i < data.length; i++) {
  63. for(var j = 0; j < data[i].length; j++) {
  64. var textWidth = doc.getStringUnitWidth(data[i][j]);
  65. if(columnWidth[j]) {
  66. columnWidth[j] = Math.max(columnWidth[j], fontSize * textWidth);
  67. } else {
  68. columnWidth.push(fontSize * textWidth);
  69. }
  70. }
  71. }
  72. var start_x = x;
  73. for(var i = 0; i < data.length; i++) {
  74. for(var j = 0; j < data[i].length; j++) {
  75. doc.text(x, y, data[i][j]);
  76. x += columnWidth[j] + columnSep;
  77. }
  78. x = start_x;
  79. y += doc.getLineHeight() + rowSep;
  80. }
  81. // Return total width and height
  82. var total_width = columnWidth.reduce((a, b) => a+b) + columnWidth.length * columnSep;
  83. var total_height = data.length * doc.getLineHeight() + data.length * rowSep;
  84. return [total_width, total_height];
  85. }
  86.  
  87. function Layout(doc, x, y) {
  88. var lineSep = 0;
  89. var width = 595; // A4 = 495pt x 842pt
  90. var height = 842;
  91. var start_x = x;
  92. var start_y = y;
  93. this.move = function move(toX,toY) {
  94. x = toX;
  95. y = toY;
  96. };
  97. this.pos = function pos() {
  98. return {"x" : x, "y" : y};
  99. };
  100. this.pageWidth = function pageWidth() {
  101. return width;
  102. };
  103. this.setLineSep = function setLineSep(newlineSep) {
  104. lineSep = newlineSep
  105. return this;
  106. };
  107. this.text = function text(fontSize, str) {
  108. doc.setFontSize(fontSize);
  109. doc.text(x, y, doc.splitTextToSize(str, width-x));
  110. x += doc.getStringUnitWidth(str) * fontSize;
  111. return this;
  112. };
  113. this.line = function line(fontSize, str) {
  114. doc.setFontSize(fontSize);
  115. x = start_x;
  116. var textHeight = doc.splitTextToSize(str, width-x).length * doc.getLineHeight();
  117. this.text(fontSize, str);
  118. y += textHeight + lineSep;
  119. return this;
  120. };
  121. this.r = function r() {
  122. x = start_x;
  123. };
  124. this.br = function br(numberOfNewLines) {
  125. if(numberOfNewLines === -1) {
  126. x = start_x;
  127. y -= doc.getLineHeight() - lineSep;
  128. } else if(numberOfNewLines > 1) {
  129. for(var i = 0; i < numberOfNewLines; i ++) {
  130. br(1);
  131. }
  132. } else if(numberOfNewLines < 0) {
  133. for(var i = 0; i < -numberOfNewLines; i ++) {
  134. br(-1);
  135. }
  136. } else {
  137. x = start_x;
  138. y += doc.getLineHeight() + lineSep;
  139. }
  140. return this;
  141. };
  142. this.columns = function columns(fontSize, columnSep, rowSep, data) {
  143. var res = makeColums(doc, x, y, fontSize, columnSep, rowSep, data);
  144. x += res[0];
  145. y += res[1] + lineSep;
  146. return this;
  147. };
  148. }
  149.  
  150.  
  151. function RecipePage() {
  152. if(!document.querySelector("#recipe-incredients")) {
  153. throw Error("RecipePage() needs a recipe page");
  154. }
  155.  
  156. this.title = function getTitle() {
  157. return document.querySelector("h1").textContent.trim();
  158. };
  159.  
  160. this.summary = function getSummary() {
  161. return document.querySelector(".summary")?document.querySelector(".summary").textContent.trim():"";
  162. };
  163.  
  164. this.ingredients = function getIngredients() {
  165. var ingredients = [];
  166. var tr = document.querySelectorAll("#recipe-incredients tr");
  167. for(var i = 0; i < tr.length; i++) {
  168. var td = tr[i].getElementsByTagName("td");
  169.  
  170. var c = [];
  171. for(var j = 0; j < td.length; j++) {
  172. c.push(td[j].textContent.trim());
  173. }
  174. if(c) {
  175. ingredients.push(c);
  176. }
  177. }
  178. return ingredients;
  179. };
  180.  
  181. this.servings = function getServings() {
  182. return (document.querySelector("#divisor").value + ' ' + document.querySelector("#divisor").nextElementSibling.firstChild.data).trim();
  183. };
  184.  
  185. this.details = function getDetails() {
  186. return trimMultiline(document.querySelector("#rezept-zubereitung").previousElementSibling.textContent.trim().replace(/(\s)\s*/g,"$1").replace(/\n/g," "));
  187. };
  188.  
  189. this.instructions = function getInstructions() {
  190. return trimMultiline(document.querySelector("#rezept-zubereitung").textContent);
  191. };
  192.  
  193. this.imageURL = function getImageURL() {
  194. if(document.querySelectorAll("#slideshow a")[0].href) {
  195. var imgs = Array.from(document.querySelectorAll("#slideshow a")).filter((e) => e.style.display == 'block');
  196. if(imgs.length && imgs[0] && imgs[0].href) {
  197. return imgs[0].href.toString();
  198. }
  199. }
  200. return false;
  201. };
  202.  
  203. }
  204.  
  205. function makePdf(cb, recipe, imageData, imgWidth, imgHeight) {
  206.  
  207. // Generate PDF
  208. var doc = new jsPDF("portrait", 'pt', 'a4');
  209. var layout = new Layout(doc, 20, 20);
  210.  
  211. layout.setLineSep(5);
  212.  
  213. layout.line(14,recipe.title());
  214. layout.line(11, recipe.summary());
  215. var image_y_start = layout.pos().y;
  216. layout.br();
  217. layout.line(13, "Zutaten (für "+recipe.servings()+")").r();
  218. layout.columns(12, 20, 5, recipe.ingredients());
  219. var image_x_start = layout.pos().x;
  220. var image_y_end = layout.pos().y;
  221. layout.line(13, "Zubereitung");
  222. layout.line(12, recipe.details());
  223. layout.line(12, recipe.instructions());
  224. if(imageData) {
  225. // Insert Image
  226. var paddingLeft = 10;
  227. var paddingRight = 10;
  228. var newImageWidth = layout.pageWidth() - image_x_start - paddingLeft - paddingRight;
  229. var newImageHeight = image_y_end - image_y_start;
  230. if(Math.min(newImageWidth, newImageHeight) > 20) { // Do not include images, if there's less than 20pt available
  231. var scale = Math.min(newImageWidth/imgWidth, newImageHeight/imgHeight);
  232. newImageWidth = Math.floor(scale * imgWidth);
  233. newImageHeight = Math.floor(scale * imgHeight);
  234. doc.addImage(imageData, 'JPEG', image_x_start+paddingLeft, image_y_start, newImageWidth, newImageHeight);
  235. }
  236. }
  237. // Show PDF
  238. var datauristring = doc.output('datauristring');
  239. var div = document.createElement("div");
  240. div.style = "background:#90b262; position:absolute; top:15px; left:2px; padding:3px 5px;";
  241. document.body.appendChild(div);
  242. var head = document.createElement("div");
  243. head.style = "color:White; height:30px;";
  244. head.appendChild(document.createTextNode("PDF Dokument: "+(parseInt((datauristring.length-28)*0.75/102.4)/10)+"kB"));
  245. var a = document.createElement("a");
  246. a.style = "margin-left:10px; color:white; text-decoration:underline";
  247. a.href = datauristring;
  248. a.target = '_blank';
  249. a.appendChild(document.createTextNode("Download"));
  250. head.appendChild(a);
  251. var close = document.createElement("a");
  252. head.appendChild(close);
  253. close.innerHTML = '<button id="cboxClose" style="top: -15px;" type="button"><span>x</span></button>';
  254. close.style = "cursor:pointer;";
  255. close.addEventListener("click",function() {document.body.removeChild(div);});
  256. div.appendChild(head);
  257. var iframe = document.createElement("iframe");
  258. iframe.style = "width:400px; height:600px; ";
  259. div.appendChild(iframe);
  260. iframe.src = datauristring;
  261. cb(doc);
  262. }
  263.  
  264. function downloadImageAndMakePdf(cb) {
  265. // Download the currently selected image and then create the PDF document
  266. var recipe = new RecipePage();
  267. if(recipe.imageURL()) {
  268. convertImgToDataURL(recipe.imageURL(), function(dataURI, width, height) {
  269. makePdf(cb, recipe, dataURI, width, height);
  270. });
  271. } else {
  272. makePdf(cb, recipe, false);
  273. }
  274. };
  275.  
  276. (function () {
  277. // Show Button
  278. var a = document.querySelector("#recipe-buttons a").cloneNode();
  279. a.innerHTML = "PDF";
  280. a.href = "javascript:void(0)";
  281. a.title = "PDF Dokument erzeugen";
  282. a.className = "button-green button-file-export";
  283. var click = function() {
  284. a.innerHTML = "Warten auf PDF...";
  285. window.setTimeout(function() {
  286. downloadImageAndMakePdf(function(doc) {
  287. window.setTimeout(function() {
  288. a.innerHTML = "PDF erstellt.";
  289. a.title = "Hier klicken um PDF zu öffnen. Rechtsklick zum Speichern.";
  290. a.removeEventListener("click", click);
  291. a.href = doc.output('datauristring');
  292. }, 5000);
  293. });
  294. },1);
  295. };
  296. a.addEventListener("click", click);
  297. document.querySelector("#recipe-buttons").insertBefore(a, document.querySelector("#recipe-buttons a"));
  298. })();
  299.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址