changeset 2215:82867a69e10e pdf-export

merged default in pdf-export
author Markus Kottlaender <markus@intevation.de>
date Thu, 07 Feb 2019 11:27:07 +0100
parents 5a4b0c85e7a8 (diff) 55bedb39295a (current diff)
children 585373d33f8f
files client/src/components/Pdftool.vue client/src/store/map.js
diffstat 7 files changed, 441 insertions(+), 211 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/Pdftool.vue	Thu Feb 07 10:30:44 2019 +0100
+++ b/client/src/components/Pdftool.vue	Thu Feb 07 11:27:07 2019 +0100
@@ -21,10 +21,9 @@
           v-model="form.template"
           class="form-control d-block mb-2 w-100 font-weight-bold"
         >
-          <option :value="null"><translate>Chose preset</translate></option>
           <option
-            v-for="template in templates"
-            :value="template.name"
+            v-for="template in pdfTemplates"
+            :value="template"
             :key="template.name"
           >
             <translate>{{ template.name }}</translate>
@@ -139,30 +138,12 @@
         downloadType: "download",
         resolution: "120"
       },
-      templates: [
-        {
-          name: "Template 1",
-          properties: {
-            format: "landscape",
-            resolution: "80",
-            paperSize: "a4"
-          }
-        },
-        {
-          name: "Template 2",
-          properties: {
-            format: "portrait",
-            resolution: "120",
-            paperSize: "a3"
-          }
-        }
-      ],
       logoImageForPDF: null, // a HTMLImageElement instance
       readyToGenerate: true // if the user is allowed to press the button
     };
   },
   computed: {
-    ...mapState("application", ["showPdfTool", "logoForPDF"]),
+    ...mapState("application", ["showPdfTool", "logoForPDF", "pdfTemplates"]),
     ...mapState("bottlenecks", ["selectedBottleneck", "selectedSurvey"]),
     ...mapState("map", ["openLayersMap", "isolinesLegendImgDataURL"]),
     ...mapGetters("map", ["getLayerByName"]),
@@ -172,28 +153,29 @@
     // When a template is chosen from the dropdown, its propoerties are
     // applied to the rest of the form.
     applyTemplateToForm() {
-      let template = this.templates.find(t => t.name === this.form.template);
-      if (template) {
-        this.form.format = template.properties.format;
-        this.form.paperSize = template.properties.paperSize;
-        this.form.resolution = template.properties.resolution;
+      if (this.form.template) {
+        this.form.format = this.form.template.properties.format;
+        this.form.paperSize = this.form.template.properties.paperSize;
+        this.form.resolution = this.form.template.properties.resolution;
       }
     },
     // If there's a template that matches all the form values, this template
     // will be set in the dropdown.
     compareFormWithTemplates() {
       this.form.template = null;
-      this.templates.forEach(t => {
+      this.pdfTemplates.forEach(t => {
         if (
           this.form.format === t.properties.format &&
           this.form.paperSize === t.properties.paperSize &&
           this.form.resolution === t.properties.resolution
         ) {
-          this.form.template = t.name;
+          this.form.template = t;
         }
       });
     },
     download() {
+      let template = this.form.template;
+
       // disable button while working on it
       this.readyToGenerate = false;
 
@@ -266,17 +248,88 @@
 
         var data = canvas.toDataURL("image/jpeg");
         pdf.addImage(data, "JPEG", 0, 0, width, height);
-        self.addScaleBar(pdf, width, height, scaleNominator);
+        //self.addScaleBar(pdf, width, height, scaleNominator);
         self.addNorthArrow(pdf, 15, 9, northarrowSize);
-        self.addPageInfo(pdf);
-        self.addAboutBox(pdf, width, height);
+        //self.addPageInfo(pdf);
+        //self.addAboutBox(pdf, width, height);
 
         if (self.getLayerByName("Bottleneck isolines").isVisible) {
           self.addBottleneckInfo(pdf, 13, width, height);
           self.addLegend(pdf, 14, width, height);
         }
+        if (template) {
+          template.elements.forEach(t => {
+            switch (t.type) {
+              case "image": {
+                if (t.imageUrl.length > 0) {
+                  pdf.addImage(
+                    t.imageUrl,
+                    t.imageType,
+                    t.x_coordinate,
+                    t.y_coordinate,
+                    t.imageWidth,
+                    t.imageHeight
+                  );
+                }
+                break;
+              }
+              case "scalebar": {
+                self.addScaleBar(
+                  pdf,
+                  width,
+                  height,
+                  scaleNominator,
+                  t.x_coordinate,
+                  t.y_coordinate
+                );
+                break;
+              }
+              case "textbox": {
+                self.addText(
+                  pdf,
+                  t.x_coordinate,
+                  t.y_coordinate,
+                  t.elementSize,
+                  t.color,
+                  100,
+                  t.text
+                );
+                break;
+              }
+              case "docinfo": {
+                self.addPageInfo(
+                  pdf,
+                  t.x_coordinate,
+                  t.y_coordinate,
+                  t.elementWidth,
+                  t.elementHeight,
+                  t.textSize
+                );
+                break;
+              }
+              case "aboutbox": {
+                self.addAboutBox(pdf, width, height);
+                break;
+              }
+              /*  case "docinfo": {
+                self.addAboutBox(
+                  pdf,
+                  t.x_coordinate,
+                  t.y_coordinate,
+                  t.elementWidth,
+                  t.elementHeight
+                );
+              } */
+            }
+          });
 
-        pdf.save("map.pdf");
+          pdf.save("map.pdf");
+        } else {
+          self.addScaleBar(pdf, width, height, scaleNominator, 226.5, 204);
+          self.addPageInfo(pdf, 0, 0, 110, 8, 9);
+          self.addAboutBox(pdf, width, height);
+          pdf.save("map.pdf");
+        }
         // reset to original size
         map.setSize(mapSize);
         map.getView().fit(mapExtent, { size: mapSize });
@@ -325,7 +378,7 @@
       doc.setFillColor(255, 255, 255);
       doc.roundedRect(x, y, w, h, 3, 3, "FD");
     },
-    addScaleBar(doc, docWidth, docHeight, scaleNominator) {
+    addScaleBar(doc, docWidth, docHeight, scaleNominator, x, y) {
       // scaleNominator is the x in 1:x of the map scale
 
       // hardcode maximal width for now and place in lower right corner
@@ -384,8 +437,8 @@
 
       let size = (length * unitConversionFactor) / scaleNominator / 4;
 
-      let x = docWidth - (size * 4 + 8);
-      let y = docHeight - 6;
+      //let x = docWidth - (size * 4 + 8);
+      //let y = docHeight - 6;
 
       this.addRoundedBox(doc, x - 4, y - 4, size * 4 + 12, 10);
 
@@ -443,8 +496,8 @@
       doc.setFontSize(size);
       doc.text(postitionX, positionY, textLines);
     },
-    addPageInfo(doc) {
-      this.addRoundedBox(doc, 0, 0, 110, 8);
+    addPageInfo(doc, x, y, width, height, textSize) {
+      this.addRoundedBox(doc, x, y, width, height);
       let str =
         this.$gettext("Date of publication:") +
         " " +
@@ -453,7 +506,7 @@
         this.$gettext("– generated by:") +
         " " +
         this.user;
-      this.addText(doc, 5, 5, 9, "black", 100, str);
+      this.addText(doc, x + 5, y + 5, textSize, "black", 100, str);
     },
     addAboutBox(doc, docWidth, docHeight) {
       let top = docHeight - 20;
@@ -516,6 +569,11 @@
       str = this.selectedSurvey.gauge_objname;
       this.addText(doc, docWidth - 51 + w, 11, 8, "black", 46, str);
     }
+  },
+  mounted() {
+    this.$store.dispatch("application/loadPdfTemplates").then(() => {
+      this.form.template = this.pdfTemplates[0];
+    });
   }
 };
 </script>
--- a/client/src/components/Systemconfiguration.vue	Thu Feb 07 10:30:44 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-<template>
-  <div class="d-flex flex-row">
-    <Spacer></Spacer>
-    <div class="card sysconfig mt-3 shadow-xs">
-      <h6
-        class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center"
-      >
-        <font-awesome-icon icon="wrench" class="mr-2"></font-awesome-icon>
-        <translate class="headline">Systemconfiguration</translate>
-      </h6>
-      <div class="card-body config">
-        <section class="configsection">
-          <h4 class="card-title">
-            <translate>Bottleneck Areas stroke-color</translate>
-          </h4>
-          <compact-picker v-model="strokeColor" />
-        </section>
-        <section>
-          <h4 class="card-title">
-            <translate>Bottleneck Areas fill-color</translate>
-          </h4>
-          <chrome-picker v-model="fillColor" />
-        </section>
-        <div class="sendbutton">
-          <a @click.prevent="submit" class="btn btn-info text-white">
-            <translate>Send</translate>
-          </a>
-        </div>
-      </div>
-      <!-- card-body -->
-    </div>
-  </div>
-</template>
-
-<style scoped lang="scss">
-.config {
-  text-align: left;
-}
-
-.configsection {
-  margin-bottom: $large-offset;
-}
-
-.sendbutton {
-  position: absolute;
-  right: $offset;
-  bottom: $offset;
-}
-
-.inputs {
-  margin-left: auto;
-  margin-right: auto;
-}
-
-.sysconfig {
-  margin-right: $offset;
-  width: 100%;
-  height: 100%;
-}
-</style>
-
-<script>
-/* This is Free Software under GNU Affero General Public License v >= 3.0
- * without warranty, see README.md and license for details.
- *
- * SPDX-License-Identifier: AGPL-3.0-or-later
- * License-Filename: LICENSES/AGPL-3.0.txt
- *
- * Copyright (C) 2018 by via donau
- *   – Österreichische Wasserstraßen-Gesellschaft mbH
- * Software engineering by Intevation GmbH
- *
- * Author(s):
- * Thomas Junk <thomas.junk@intevation.de>
- * Bernhard Reiter <bernhard@intevation.de>
- */
-import { Chrome } from "vue-color";
-import { Compact } from "vue-color";
-
-import { HTTP } from "@/lib/http";
-import { displayError } from "@/lib/errors.js";
-import { mapState } from "vuex";
-
-export default {
-  name: "systemconfiguration",
-  data() {
-    return {
-      sent: false,
-      strokeColor: { r: 0, g: 0, b: 0, a: 1.0 },
-      fillColor: { r: 0, g: 0, b: 0, a: 1.0 },
-      currentConfig: null
-    };
-  },
-  components: {
-    "chrome-picker": Chrome,
-    "compact-picker": Compact,
-    Spacer: () => import("./Spacer")
-  },
-  computed: {
-    ...mapState("application", ["showSidebar"])
-  },
-  methods: {
-    submit() {
-      HTTP.put("/system/style/Bottlenecks/stroke", this.strokeColor.rgba, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "application/json"
-        }
-      })
-        .then()
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-
-      HTTP.put("/system/style/Bottlenecks/fill", this.fillColor.rgba, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "application/json"
-        }
-      })
-        .then()
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    }
-  },
-  mounted() {
-    HTTP.get("/system/style/Bottlenecks/stroke", {
-      headers: {
-        "X-Gemma-Auth": localStorage.getItem("token"),
-        "Content-type": "application/json"
-      }
-    })
-      .then(response => {
-        this.strokeColor = response.data.colour;
-      })
-      .catch(error => {
-        const { status, data } = error.response;
-        displayError({
-          title: this.$gettext("Backend Error"),
-          message: `${status}: ${data.message || data}`
-        });
-      });
-
-    HTTP.get("/system/style/Bottlenecks/fill", {
-      headers: {
-        "X-Gemma-Auth": localStorage.getItem("token"),
-        "Content-type": "application/json"
-      }
-    })
-      .then(response => {
-        this.fillColor = response.data.colour;
-      })
-      .catch(error => {
-        const { status, data } = error.response;
-        displayError({
-          title: this.$gettext("Backend Error"),
-          message: `${status}: ${data.message || data}`
-        });
-      });
-  }
-};
-</script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/systemconfiguration/PDFTemplates.vue	Thu Feb 07 11:27:07 2019 +0100
@@ -0,0 +1,72 @@
+<template>
+  <div class="d-flex flex-column mt-4">
+    <div class="d-flex flex-row justify-content-between">
+      <h5><translate>PDF-Templates</translate></h5>
+      <input
+        id="uploadPDFTemplate"
+        ref="uploadPDFTemplate"
+        type="file"
+        style="visibility:hidden"
+      />
+      <button
+        class="btn btn-sm btn-info"
+        @click="$refs.uploadPDFTemplate.click()"
+      >
+        <font-awesome-icon icon="plus" />
+      </button>
+    </div>
+    <div class="d-flex mt-1">
+      <table class="table table-sm">
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Description</th>
+            <th>Date</th>
+            <th>Country</th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="template in pdfTemplates" :key="template.name">
+            <td>{{ template.name }}</td>
+            <td>{{ template.description }}</td>
+            <td>{{ template.date }}</td>
+            <td></td>
+            <td class="text-right">
+              <button class="btn btn-sm btn-info mr-2">
+                <font-awesome-icon icon="download" />
+              </button>
+              <button class="btn btn-sm btn-danger">
+                <font-awesome-icon icon="trash" />
+              </button>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+</template>
+
+<script>
+/* This is Free Software under GNU Affero General Public License v >= 3.0
+ * without warranty, see README.md and license for details.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ * License-Filename: LICENSES/AGPL-3.0.txt
+ *
+ * Copyright (C) 2018 by via donau
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ *
+ * Author(s):
+ * Markus Kottländer <markus@intevation.de>
+ */
+import { mapState } from "vuex";
+
+export default {
+  name: "pdftemplates",
+  computed: {
+    ...mapState("application", ["pdfTemplates"])
+  }
+};
+</script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/systemconfiguration/Systemconfiguration.vue	Thu Feb 07 11:27:07 2019 +0100
@@ -0,0 +1,159 @@
+<template>
+  <div class="d-flex flex-row">
+    <Spacer></Spacer>
+    <div class="card sysconfig mt-3 shadow-xs">
+      <h6
+        class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center"
+      >
+        <font-awesome-icon icon="wrench" class="mr-2"></font-awesome-icon>
+        <translate class="headline">Systemconfiguration</translate>
+      </h6>
+      <div class="card-body text-left">
+        <h5 class="border-bottom pb-3 mb-3">
+          <translate>Color settings</translate>
+        </h5>
+        <div class="d-flex">
+          <div>
+            <h6 class="card-title">
+              <translate>Bottleneck Areas fill-color</translate>
+            </h6>
+            <chrome-picker v-model="fillColor" />
+          </div>
+          <div class="ml-4">
+            <h6 class="card-title">
+              <translate>Bottleneck Areas stroke-color</translate>
+            </h6>
+            <compact-picker v-model="strokeColor" />
+          </div>
+        </div>
+        <div class="mt-4">
+          <a @click.prevent="submit" class="btn btn-info text-white">
+            <translate>Send</translate>
+          </a>
+        </div>
+        <PDFTemplates />
+      </div>
+      <!-- card-body -->
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.sysconfig {
+  margin-right: $offset;
+  width: 100%;
+  height: 100%;
+}
+</style>
+
+<script>
+/* This is Free Software under GNU Affero General Public License v >= 3.0
+ * without warranty, see README.md and license for details.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ * License-Filename: LICENSES/AGPL-3.0.txt
+ *
+ * Copyright (C) 2018 by via donau
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ *
+ * Author(s):
+ * Thomas Junk <thomas.junk@intevation.de>
+ * Bernhard Reiter <bernhard@intevation.de>
+ */
+import { Chrome } from "vue-color";
+import { Compact } from "vue-color";
+
+import { HTTP } from "@/lib/http";
+import { displayError } from "@/lib/errors.js";
+import { mapState } from "vuex";
+
+export default {
+  name: "systemconfiguration",
+  data() {
+    return {
+      sent: false,
+      strokeColor: { r: 0, g: 0, b: 0, a: 1.0 },
+      fillColor: { r: 0, g: 0, b: 0, a: 1.0 },
+      currentConfig: null
+    };
+  },
+  components: {
+    "chrome-picker": Chrome,
+    "compact-picker": Compact,
+    Spacer: () => import("../Spacer"),
+    PDFTemplates: () => import("./PDFTemplates")
+  },
+  computed: {
+    ...mapState("application", ["showSidebar"])
+  },
+  methods: {
+    submit() {
+      HTTP.put("/system/style/Bottlenecks/stroke", this.strokeColor.rgba, {
+        headers: {
+          "X-Gemma-Auth": localStorage.getItem("token"),
+          "Content-type": "application/json"
+        }
+      })
+        .then()
+        .catch(error => {
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
+          });
+        });
+
+      HTTP.put("/system/style/Bottlenecks/fill", this.fillColor.rgba, {
+        headers: {
+          "X-Gemma-Auth": localStorage.getItem("token"),
+          "Content-type": "application/json"
+        }
+      })
+        .then()
+        .catch(error => {
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
+          });
+        });
+    }
+  },
+  mounted() {
+    HTTP.get("/system/style/Bottlenecks/stroke", {
+      headers: {
+        "X-Gemma-Auth": localStorage.getItem("token"),
+        "Content-type": "application/json"
+      }
+    })
+      .then(response => {
+        this.strokeColor = response.data.colour;
+      })
+      .catch(error => {
+        const { status, data } = error.response;
+        displayError({
+          title: this.$gettext("Backend Error"),
+          message: `${status}: ${data.message || data}`
+        });
+      });
+
+    HTTP.get("/system/style/Bottlenecks/fill", {
+      headers: {
+        "X-Gemma-Auth": localStorage.getItem("token"),
+        "Content-type": "application/json"
+      }
+    })
+      .then(response => {
+        this.fillColor = response.data.colour;
+      })
+      .catch(error => {
+        const { status, data } = error.response;
+        displayError({
+          title: this.$gettext("Backend Error"),
+          message: `${status}: ${data.message || data}`
+        });
+      });
+  }
+};
+</script>
--- a/client/src/main.js	Thu Feb 07 10:30:44 2019 +0100
+++ b/client/src/main.js	Thu Feb 07 11:27:07 2019 +0100
@@ -43,6 +43,7 @@
   faClock,
   faCloudUploadAlt,
   faCopy,
+  faDownload,
   faDrawPolygon,
   faExclamationTriangle,
   faEye,
@@ -95,6 +96,7 @@
   faClock,
   faCloudUploadAlt,
   faCopy,
+  faDownload,
   faDrawPolygon,
   faExclamationTriangle,
   faEye,
--- a/client/src/router.js	Thu Feb 07 10:30:44 2019 +0100
+++ b/client/src/router.js	Thu Feb 07 11:27:07 2019 +0100
@@ -65,7 +65,8 @@
     {
       path: "/systemconfiguration",
       name: "systemconfiguration",
-      component: () => import("./components/Systemconfiguration.vue"),
+      component: () =>
+        import("./components/systemconfiguration/Systemconfiguration.vue"),
       meta: {
         requiresAuth: true
       },
--- a/client/src/store/application.js	Thu Feb 07 10:30:44 2019 +0100
+++ b/client/src/store/application.js	Thu Feb 07 11:27:07 2019 +0100
@@ -15,6 +15,7 @@
  */
 
 import { version } from "../../package.json";
+// import { HTTP } from "../lib/http";
 
 // initial state
 const init = () => {
@@ -22,6 +23,7 @@
     appTitle: process.env.VUE_APP_TITLE,
     secondaryLogo: process.env.VUE_APP_SECONDARY_LOGO_URL,
     logoForPDF: process.env.VUE_APP_LOGO_FOR_PDF_URL,
+    pdfTemplates: [],
     showSidebar: false,
     showUsermenu: false,
     showSplitscreen: false,
@@ -103,6 +105,113 @@
     },
     searchQuery: (state, searchQuery) => {
       state.searchQuery = searchQuery;
+    },
+    pdfTemplates: (state, pdfTemplates) => {
+      state.pdfTemplates = pdfTemplates;
+    }
+  },
+  actions: {
+    loadPdfTemplates({ commit }) {
+      return new Promise(resolve => {
+        // pretend we do something async
+        setTimeout(function() {
+          commit("pdfTemplates", [
+            {
+              name: "Default",
+              properties: {
+                format: "landscape",
+                resolution: "80",
+                paperSize: "a4"
+              },
+              elements: [
+                {
+                  type: "docinfo",
+                  x_coordinate: 0,
+                  y_coordinate: 0,
+                  elementWidth: 118,
+                  elementHeight: 8,
+                  textSize: 9
+                },
+                {
+                  type: "image",
+                  imageType: "PNG",
+                  imageUrl: "",
+                  x_coordinate: 30,
+                  y_coordinate: 297,
+                  imageWidth: 50,
+                  imageHeight: 23
+                },
+                {
+                  type: "scalebar",
+                  x_coordinate: 226.5,
+                  y_coordinate: 204
+                  //elementsize: 50
+                },
+                {
+                  type: "textbox",
+                  x_coordinate: 50,
+                  y_coordinate: 190,
+                  elementSize: 8,
+                  text: "textfrom template",
+                  color: "black"
+                },
+                {
+                  type: "aboutbox"
+                  //x_coordinate: 0,
+                  //y_coordinate: 210 - 20
+                }
+              ]
+            },
+            {
+              name: "DE Querformat, groß",
+              properties: {
+                format: "landscape",
+                resolution: "120",
+                paperSize: "a3"
+              },
+              elements: [
+                {
+                  type: "docinfo",
+                  x_coordinate: 0,
+                  y_coordinate: 0,
+                  elementWidth: 118,
+                  elementHeight: 8,
+                  textSize: 9
+                },
+                {
+                  type: "image",
+                  imageType: "PNG",
+                  imageUrl: "",
+                  x_coordinate: 30,
+                  y_coordinate: 297,
+                  imageWidth: 50,
+                  imageHeight: 23
+                },
+                {
+                  type: "scalebar",
+                  x_coordinate: 247.3,
+                  y_coordinate: 414
+                  //elementsize: 50
+                },
+                {
+                  type: "textbox",
+                  x_coordinate: 50,
+                  y_coordinate: 50,
+                  elementSize: 22,
+                  text: "from template",
+                  color: "black"
+                },
+                {
+                  type: "aboutbox"
+                  //x_coordinate: 0,
+                  //y_coordinate: 210 - 20
+                }
+              ]
+            }
+          ]);
+          resolve();
+        }, 500);
+      });
     }
   }
 };