Mercurial > gemma
changeset 3137:35c0da49eb89
client: pdf-gen: prepare pdf template for waterlevel diagram
* use default template when nothings come from backend
* adjust and add some functions to handle the generation of template elements
author | Fadi Abbud <fadi.abbud@intevation.de> |
---|---|
date | Thu, 02 May 2019 10:46:50 +0200 |
parents | e52ba854bfe8 |
children | 106e1d011a5d |
files | client/src/components/gauge/Waterlevel.vue client/src/lib/mixins.js |
diffstat | 2 files changed, 236 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/client/src/components/gauge/Waterlevel.vue Tue Apr 30 15:33:05 2019 +0200 +++ b/client/src/components/gauge/Waterlevel.vue Thu May 02 10:46:50 2019 +0200 @@ -15,6 +15,18 @@ Range </div> <div> + <select + v-model="form.template" + class="form-control d-block custom-select-sm w-100" + > + <option + v-for="template in templates" + :value="template" + :key="template.name" + > + {{ template.name }} + </option> + </select> <button @click="downloadPDF" type="button" @@ -58,13 +70,14 @@ import debounce from "debounce"; import jsPDF from "jspdf"; import canvg from "canvg"; - +import { pdfgen } from "@/lib/mixins"; // we should load only d3 modules we need but for now we'll go with the lazy way // https://www.giacomodebidda.com/how-to-import-d3-plugins-with-webpack/ // d3-line-chunked plugin: https://github.com/pbeshai/d3-line-chunked const d3 = Object.assign(d3Base, { lineChunked }); export default { + mixins: [pdfgen], components: { DiagramLegend: () => import("@/components/DiagramLegend") }, @@ -76,12 +89,55 @@ dimensions: null, extent: null, scale: null, - axes: null + axes: null, + form: { + template: null + }, + templates: [ + { + name: "Default", + properties: { + format: "landscape", + paperSize: "a3", + resolution: "80" + }, + elements: [ + { + type: "diagramlegend", + position: "topleft", + offset: { x: 60, y: 190 }, + color: "black" + }, + { + type: "diagramtitle", + position: "topleft", + offset: { x: 108, y: 30 }, + fontsize: 22, + color: "steelblue" + }, + { + type: "text", + position: "topleft", + offset: { x: 7, y: 5 }, + fontsize: 14, + width: 90, + text: this.$gettext("Generated by") + " " + "{user}, {date}" + } + ] + } + ], + pdf: { + doc: null, + width: 420, + height: 297 + }, + templateData: null }; }, computed: { ...mapState("gauges", ["dateFrom", "waterlevels", "nashSutcliffe"]), ...mapGetters("gauges", ["selectedGauge"]), + ...mapState("user", ["user"]), dateFrom: { get() { return this.$store.state.gauges.dateFrom; @@ -104,7 +160,7 @@ if (svg) { svg = svg.replace(/\r?\n|\r/g, "").trim(); } - var pdf = new jsPDF("l", "mm", "a3"); + this.pdf.doc = new jsPDF("l", "mm", "a3"); var canvas = document.createElement("canvas"); canvas.width = window.innerWidth; canvas.height = window.innerHeight / 2; @@ -114,15 +170,58 @@ ignoreDimensions: true }); var imgData = canvas.toDataURL("image/png"); - pdf.addImage(imgData, "PNG", 40, 60, 380, 130); - // Gauge info as a title for pdf - this.addDiagramTitle(pdf, 108, 30, 22, "steelblue"); - this.addDiagramLegend(pdf, 60, 190, "black"); - pdf.save( + this.pdf.doc.addImage(imgData, "PNG", 40, 60, 380, 130); + if (this.templateData) { + let defaultFontSize = 11, + defaultColor = "black", + defaultWidth = 70, + defaultTextColor = "black", + defaultOffset = { x: 0, y: 0 }; + this.templateData.elements.forEach(e => { + switch (e.type) { + case "diagramlegend": { + this.addDiagramLegend( + e.position, + e.offset || defaultOffset, + e.color || defaultColor + ); + break; + } + case "diagramtitle": { + this.addDiagramTitle(e.position, e.offset, e.fontsize, e.color); + break; + } + case "text": { + this.addText( + e.position, + e.offset || defaultOffset, + e.width || defaultWidth, + e.fontSize || defaultFontSize, + e.color || defaultTextColor, + e.text + ); + break; + } + case "image": { + this.addImage( + e.url, + e.format, + e.position, + e.offset || defaultOffset, + e.width, + e.height + ); + break; + } + } + }); + } + this.pdf.doc.save( this.selectedGauge.properties.objname + " Waterlevel-Diagram.pdf" ); }, - addDiagramTitle(pdf, x, y, size, color) { + // Gauge info as a title for pdf + addDiagramTitle(position, offset, size, color) { let gaugeInfo = this.selectedGauge.properties.objname + " (" + @@ -135,28 +234,63 @@ this.dateFrom.toLocaleDateString() + " - " + this.dateTo.toLocaleDateString(); - pdf.setTextColor(color); - pdf.setFontSize(size); - pdf.setFontStyle("bold"); - pdf.text(gaugeInfo, x, y); + let x = offset.x; + let y = offset.y; + this.pdf.doc.setTextColor(color); + this.pdf.doc.setFontSize(size); + this.pdf.doc.setFontStyle("bold"); + let width = this.pdf.doc.getTextWidth(gaugeInfo) + size / 2; + console.log("width", width); + + if (["topright", "bottomright"].indexOf(position) !== -1) { + x = this.pdf.width - offset.x - width; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y = this.pdf.height - offset.y - this.getTextHeight(1); + } + //let y1 = y === 0 ? y + this.getTextHeigt(1) : y1; + console.log("y1", y); + console.log("x", x); + console.log("position", position); + this.pdf.doc.text(gaugeInfo, x, y, { + baseline: "hanging" + }); + }, + getTextHeight(numberOfLines) { + return ( + numberOfLines * + ((this.pdf.doc.getFontSize() * 25.4) / 80) * + this.pdf.doc.getLineHeightFactor() + ); }, // Diagram legend - addDiagramLegend(pdf, x, y, color) { - pdf.setFontSize(10); - pdf.setTextColor(color); - pdf.setDrawColor("white"); - pdf.setFillColor("steelblue"); - pdf.circle(x, y, 2, "FD"); - pdf.text(x + 3, y + 1, "Waterlevel"); - pdf.setFillColor("#dae6f0"); - pdf.circle(x, y + 5, 2, "FD"); - pdf.setFillColor("#e5ffe5"); - pdf.circle(x, y + 10, 2, "FD"); - pdf.text(x + 3, y + 11, "Navigable Range"); - pdf.setDrawColor("#90b4d2"); - pdf.setFillColor("#90b4d2"); - pdf.circle(x, y + 5, 0.6, "FD"); - pdf.text(x + 3, y + 6, "Prediction"); + addDiagramLegend(position, offset, color) { + let x = offset.x; + let y = offset.y; + let width = this.pdf.doc.getTextWidth("Navigable Range") + 12; + console.log("width for legend", width); + let height = 15; + if (["topright", "bottomright"].indexOf(position) !== -1) { + x = this.pdf.width - offset.x - width; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y = this.pdf.height - offset.y - height; + } + this.pdf.doc.setFontSize(10); + this.pdf.doc.setTextColor(color); + this.pdf.doc.setDrawColor("white"); + this.pdf.doc.setFillColor("steelblue"); + this.pdf.doc.circle(x, y, 2, "FD"); + this.pdf.doc.text(x + 3, y + 1, "Waterlevel"); + this.pdf.doc.setFillColor("#dae6f0"); + this.pdf.doc.circle(x, y + 5, 2, "FD"); + this.pdf.doc.setFillColor("#e5ffe5"); + this.pdf.doc.circle(x, y + 10, 2, "FD"); + this.pdf.doc.text(x + 3, y + 11, "Navigable Range"); + this.pdf.doc.setDrawColor("#90b4d2"); + this.pdf.doc.setFillColor("#90b4d2"); + this.pdf.doc.circle(x, y + 5, 0.6, "FD"); + this.pdf.doc.text(x + 3, y + 6, "Prediction"); }, drawDiagram() { // remove old diagram and exit if necessary data is missing @@ -907,6 +1041,8 @@ }, mounted() { this.drawDiagram(); + this.form.template = this.templates[0]; + this.templateData = this.form.template; }, updated() { this.drawDiagram();
--- a/client/src/lib/mixins.js Tue Apr 30 15:33:05 2019 +0200 +++ b/client/src/lib/mixins.js Thu May 02 10:46:50 2019 +0200 @@ -11,6 +11,7 @@ * Author(s): * Markus Kottländer <markus.kottlaender@intevation.de> */ +import locale2 from "locale2"; const sortTable = { data() { return { @@ -54,4 +55,73 @@ } }; -export { sortTable, pane }; +const pdfgen = { + methods: { + addText(position, offset, width, fontSize, color, text) { + text = this.replacePlaceholders(text); + // split the incoming string to an array, each element is a string of + // words in a single line + this.pdf.doc.setTextColor(color); + this.pdf.doc.setFontSize(fontSize); + var textLines = this.pdf.doc.splitTextToSize(text, width); + // x/y defaults to offset for topleft corner (normal x/y coordinates) + let x = offset.x; + let y = offset.y; + // if position is on the right, x needs to be calculate with pdf width and + // the size of the element + if (["topright", "bottomright"].indexOf(position) !== -1) { + x = this.pdf.width - offset.x - width; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y = this.pdf.height - offset.y - this.getTextHeight(textLines.length); + } + + this.pdf.doc.text(textLines, x, y, { baseline: "hanging" }); + }, + replacePlaceholders(text) { + if (text.includes("{date}")) { + text = text.replace("{date}", new Date().toLocaleString(locale2)); + } + //get only day,month and year from the Date object + if (text.includes("{date-minor}")) { + var date = new Date(); + var dt = + (date.getDate() < 10 ? "0" : "") + + date.getDate() + + "." + + (date.getMonth() + 1 < 10 ? "0" : "") + + (date.getMonth() + 1) + + "." + + date.getFullYear(); + text = text.replace("{date-minor}", dt.toLocaleString(locale2)); + } + if (text.includes("{user}")) { + text = text.replace("{user}", this.user); + } + return text; + }, + addImage(url, format, position, offset, width, height) { + let x = offset.x; + let y = offset.y; + if (["topright", "bottomright"].indexOf(position) !== -1) { + x = this.pdf.width - offset.x - width; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y = this.pdf.height - offset.y - height; + } + let image = new Image(); + if (url) { + image.src = url; + } else { + if (this.logoForPDF) { + image.src = this.logoForPDF; + } else { + image.src = "/img/gemma-logo-for-pdf.png"; + } + } + this.pdf.doc.addImage(image, x, y, width, height); + } + } +}; + +export { sortTable, pane, pdfgen };