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 };