Mercurial > gemma
view client/src/lib/mixins.js @ 4017:639bdb17c3f2
Fixed offset for fairway box
This was broken by
changeset: 4080:bf86f9a08733
user: Thomas Junk <thomas.junk@intevation.de>
Date: Thu Jul 18 15:04:30 2019 +0200
summary: improve fairwaydiagram printing positioning
For the record: I think the current implementation exceptionally
flawed. Instead of adding extra offset parameters to the diagram
elements the whole building block with all contained elements should
be translated in one step, that would be less cluttered and less error
prone...
author | Sascha Wilde <wilde@intevation.de> |
---|---|
date | Fri, 19 Jul 2019 16:59:25 +0200 |
parents | bf86f9a08733 |
children | 7959f62d3051 |
line wrap: on
line source
/* 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, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * * Markus Kottländer <markus.kottlaender@intevation.de> * * Fadi Abbud <fadi.abbud@intevation.de> * * Bernhard Reiter <bernhard.reiter@intevation.de> */ import jsPDF from "jspdf-yworks"; import svg2pdf from "svg2pdf.js"; import locale2 from "locale2"; import { mapState } from "vuex"; import { HTTP } from "@/lib/http"; import * as d3 from "d3"; /*eslint no-unused-vars: ["error", { "varsIgnorePattern": "debugSVG" }]*/ const debugSVG = ({ svg, svgWidth, svgHeight }) => { d3.select(svg) .append("rect") .attr("width", svgWidth) .attr("height", svgHeight) .attr("fill-opacity", 0) .attr("stroke", "#ff0000"); }; export const sortTable = { data() { return { sortColumn: "", sortDirection: "ASC", pageSize: 20, page: 1 }; }, methods: { sortTable(sorting) { this.sortColumn = sorting.sortColumn; this.sortDirection = sorting.sortDirection; } } }; export const diagram = { methods: { getDimensions({ svgWidth, svgHeight, main, nav }) { const mainMargin = main || { top: 20, right: 80, bottom: 60, left: 80 }; const navMargin = nav || { top: svgHeight - mainMargin.top - 65, right: 20, bottom: 30, left: 80 }; const width = Number(svgWidth) - mainMargin.left - mainMargin.right; const mainHeight = Number(svgHeight) - mainMargin.top - mainMargin.bottom; const navHeight = Number(svgHeight) - navMargin.top - navMargin.bottom; return { width, mainHeight, navHeight, mainMargin, navMargin }; } } }; export const pane = { computed: { paneId() { return this.$parent.pane.id; } } }; export const templateLoader = { methods: { loadTemplates(url) { return new Promise((resolve, reject) => { HTTP.get(url, { headers: { "X-Gemma-Auth": localStorage.getItem("token"), "Content-type": "text/xml; charset=UTF-8" } }) .then(response => { resolve(response); }) .catch(error => { reject(error); }); }); }, prepareImages(elements) { /** * In order to render the images from the template, we need to convert * each image to dataURIs. Since this happens asynchronous, * we need to wrap each image into its own promise and only after all are * finished, we continue with the flow. */ return new Promise(resolve => { const imageElementLoaders = elements.reduce((o, n, i) => { if (n.type === "image") { o.push( new Promise(resolve => { const image = new Image(); image.onload = function() { var canvas = document.createElement("canvas"); canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size canvas.getContext("2d").drawImage(this, 0, 0); resolve({ index: i, url: canvas.toDataURL("image/png") }); }; let URL = n.URL; if (!URL) { URL = this.logoForPDF ? this.logoForPDF : "/img/gemma-logo-for-pdf.png"; } image.src = URL; }) ); } return o; }, []); Promise.all(imageElementLoaders).then(values => { resolve(values); }); }); } } }; export const pdfgen = { computed: { ...mapState("application", ["logoForPDF"]), ...mapState("user", ["user"]) }, methods: { addDiagram(position, offset, width, height) { let x = offset.x, y = offset.y; const svgWidth = this.millimeter2pixels(width, 80); const svgHeight = this.millimeter2pixels(height, 80); // draw the diagram in a separated html element to get the full size const offScreen = document.querySelector("#offScreen"); offScreen.style.width = `${svgWidth}px`; offScreen.style.height = `${svgHeight}px`; const layout = this.getPrintLayout(svgHeight); this.renderTo({ element: offScreen, dimensions: this.getDimensions({ svgWidth: svgWidth, svgHeight: svgHeight, ...layout }) }); var svg = offScreen.querySelector("svg"); 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; } svg2pdf(svg, this.pdf.doc, { xOffset: x, yOffset: y, scale: 0.354 }); offScreen.removeChild(svg); }, getPaperDimensions(format) { const dims = { A3: { height: 297, width: 420 }, A4: { height: 210, width: 297 } }; return dims[format.toUpperCase()]; }, millimeter2pixels(length, dpi) { return (dpi * length) / 25.4; }, pixel2millimeter(pixels, dpi) { return (pixels * 25.4) / dpi; }, gaugeInfo(selectedGauge) { // returns string with formatted gauge info return ( selectedGauge.properties.objname + " (" + selectedGauge.id .split(".")[1] .replace(/[()]/g, "") .split(",")[3] + ")" ); }, generatePDF(params) { // creates a new jsPDF object into this.pdf.doc // will call functions that the calling context has to provide // as specified in the templateData let templateData = params["templateData"]; let diagramTitle = params["diagramTitle"]; this.pdf.doc = new jsPDF("l", "mm", templateData.properties.paperSize); // pdf width and height in millimeter (landscape) if (templateData.properties.paperSize === "a3") { this.pdf.width = 420; this.pdf.height = 297; } else { this.pdf.width = 297; this.pdf.height = 210; } // check the template elements if (templateData) { this.pdf.doc.setFont("linbiolinum", "normal"); let defaultFontSize = 11, defaultColor = "black", defaultTextColor = "black", defaultBorderColor = "white", defaultBgColor = "white", defaultRounding = 2, defaultPadding = 2, defaultOffset = { x: 0, y: 0 }; templateData.elements.forEach(e => { switch (e.type) { case "diagram": { this.addDiagram( e.position, e.offset || defaultOffset, // use default width,height if they are missing in template definition e.width || (this.templateData.properties.paperSize === "a3" ? 318 : 230), e.height || (this.templateData.properties.paperSize === "a3" ? 104 : 110) ); break; } case "diagramlegend": { this.addDiagramLegend( e.position, e.offset || defaultOffset, e.color || defaultColor ); break; } case "diagramtitle": { this.addDiagramTitle( e.position, e.offset || defaultOffset, e.fontsize || defaultFontSize, e.color || defaultColor, diagramTitle ); break; } case "text": { this.addText( e.position, e.offset || defaultOffset, e.width, e.fontsize || defaultFontSize, e.color || defaultTextColor, e.text || "" ); break; } case "image": { this.addImage( e.url, e.format || "", e.position, e.offset || defaultOffset, e.width || 90, e.height || 60 ); break; } case "box": { this.addBox( e.position, e.offset || defaultOffset, e.width || 90, e.height || 60, e.rounding === 0 || e.rounding ? e.rounding : defaultRounding, e.color || defaultBgColor, e.brcolor || defaultBorderColor ); break; } case "textbox": { this.addTextBox( e.position, e.offset || defaultOffset, e.width, e.height, e.rounding === 0 || e.rounding ? e.rounding : defaultRounding, e.padding || defaultPadding, e.fontsize || defaultFontSize, e.color || defaultTextColor, e.background || defaultBgColor, e.text || "", e.brcolor || defaultBorderColor ); break; } } }); } }, // add text at specific coordinates and do line breaks 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.setFontStyle("normal"); this.pdf.doc.setTextColor(color); this.pdf.doc.setFontSize(fontSize); // 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 (!width) { width = this.pdf.doc.getTextWidth(text); } var textLines = this.pdf.doc.splitTextToSize(text, width); if ( ["topright", "topleft"].indexOf(position) !== -1 && y < this.getTextHeight(1) ) { y = this.getTextHeight(1); } 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(); image.src = url; if (format === "") { let tmp = image.src.split("."); format = tmp[tmp.length - 1].toUpperCase(); } this.pdf.doc.addImage(image, format, x, y, width, height); }, // add text at specific coordinates with a background box addBox(position, offset, width, height, rounding, color, brcolor) { // 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 - height; } this.addRoundedBox(x, y, width, height, color, rounding, brcolor); }, getTextHeight(numberOfLines) { // Return estimated height in mm. // FontSize is given in desktop publishing points defined as 1/72 inch. // aka 25.4 / 72 mm let fontSize = this.pdf.doc.getFontSize(); let lineHeightFactor = 1.15; // default from jspdf-yworks 2.0.2 if (typeof this.pdf.doc.getLineHeightFactor !== "undefined") { lineHeightFactor = this.pdf.doc.getLineHeightFactor(); } return numberOfLines * fontSize * (25.4 / 72) * lineHeightFactor; }, // title for diagram addDiagramTitle(position, offset, size, color, text) { let x = offset.x, y = offset.y; this.pdf.doc.setFontSize(size); this.pdf.doc.setFontStyle("bold"); this.pdf.doc.setTextColor(color); let width = (this.pdf.doc.getStringUnitWidth(text) * size) / (72 / 25.6) + size / 2; // 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(1); } this.pdf.doc.text(text, x, y, { baseline: "hanging" }); }, addRoundedBox(x, y, w, h, color, rounding, brcolor) { this.pdf.doc.setDrawColor(brcolor); this.pdf.doc.setFillColor(color); this.pdf.doc.roundedRect(x, y, w, h, rounding, rounding, "FD"); }, addTextBox( position, offset, width, height, rounding, padding, fontSize, color, background, text, brcolor ) { this.pdf.doc.setFontSize(fontSize); text = this.replacePlaceholders(text); if (!width) { width = this.pdf.doc.getTextWidth(text) + 2 * padding; } let textWidth = width - 2 * padding; if (!height) { let textLines = this.pdf.doc.splitTextToSize(text, textWidth); height = this.getTextHeight(textLines.length) + 2 * padding; } this.addBox( position, offset, width, height, rounding, background, brcolor ); if (["bottomright", "bottomleft"].indexOf(position) !== -1) { this.addText( position, { x: offset.x + padding, y: offset.y - padding / 2 }, textWidth, fontSize, color, text ); } else { this.addText( position, { x: offset.x + padding, y: offset.y + padding * 2 }, textWidth, fontSize, color, text ); } } } };