Mercurial > gemma
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); + }); } } };