Mercurial > gemma
diff client/src/components/Pdftool.vue @ 2218:631ca0412db9 pdf-export
adjusted positioning of pdf elements
also cleaned up the code a little bit
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Fri, 08 Feb 2019 16:16:04 +0100 |
parents | 82867a69e10e |
children | 80de3787cdb3 |
line wrap: on
line diff
--- a/client/src/components/Pdftool.vue Thu Feb 07 19:27:27 2019 +0100 +++ b/client/src/components/Pdftool.vue Fri Feb 08 16:16:04 2019 +0100 @@ -138,6 +138,11 @@ downloadType: "download", resolution: "120" }, + pdf: { + doc: null, + width: null, + height: null + }, logoImageForPDF: null, // a HTMLImageElement instance readyToGenerate: true // if the user is allowed to press the button }; @@ -185,16 +190,15 @@ this.form.format, this.form.resolution ); - var width, height; if (this.form.format !== "portrait") { // landscape, default - width = paperSizes[this.form.paperSize][0]; - height = paperSizes[this.form.paperSize][1]; + this.pdf.width = paperSizes[this.form.paperSize][0]; + this.pdf.height = paperSizes[this.form.paperSize][1]; } else { // switch width and height - width = paperSizes[this.form.paperSize][1]; - height = paperSizes[this.form.paperSize][0]; + this.pdf.width = paperSizes[this.form.paperSize][1]; + this.pdf.height = paperSizes[this.form.paperSize][0]; } // FUTURE: consider margins @@ -203,8 +207,8 @@ var pixelsPerMapMillimeter = this.form.resolution / 25.4; var mapSizeForPrint = [ // in pixel - Math.round(width * pixelsPerMapMillimeter), - Math.round(height * pixelsPerMapMillimeter) + Math.round(this.pdf.width * pixelsPerMapMillimeter), + Math.round(this.pdf.height * pixelsPerMapMillimeter) ]; // generate PDF and open it @@ -222,12 +226,10 @@ // extent should fit. var mapExtent = map.getView().calculateExtent(mapSize); - var pdf = new jsPDF(this.form.format, "mm", this.form.paperSize); - var northarrowSize = 3; - var self = this; + this.pdf.doc = new jsPDF(this.form.format, "mm", this.form.paperSize); // set a callback for after the next complete rendering of the map - map.once("rendercomplete", function(event) { + map.once("rendercomplete", event => { let canvas = event.context.canvas; // because we are using Web Mercator, a pixel represents @@ -247,114 +249,72 @@ console.log("scaleNominator = ", scaleNominator); var data = canvas.toDataURL("image/jpeg"); - pdf.addImage(data, "JPEG", 0, 0, width, height); - //self.addScaleBar(pdf, width, height, scaleNominator); - self.addNorthArrow(pdf, 15, 9, northarrowSize); - //self.addPageInfo(pdf); - //self.addAboutBox(pdf, width, height); + this.pdf.doc.addImage(data, "JPEG", 0, 0); - 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 + template.elements.forEach(e => { + switch (e.type) { + case "text": { + this.addText( + e.position, + e.offset, + e.width, + e.height, + e.padding, + e.fontSize, + e.color, + e.text ); 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 + case "image": { + this.addImage( + e.url, + e.format, + e.position, + e.offset, + e.width, + e.height, + e.border ); break; } - case "aboutbox": { - self.addAboutBox(pdf, width, height); + case "bottleneck": { + this.addBottleneckInfo(e.position, e.offset); + break; + } + case "legend": { + this.addLegend(e.position, e.offset); break; } - /* case "docinfo": { - self.addAboutBox( - pdf, - t.x_coordinate, - t.y_coordinate, - t.elementWidth, - t.elementHeight - ); - } */ + case "scalebar": { + this.addScaleBar(scaleNominator, e.position, e.offset); + break; + } + case "northarrow": { + this.addNorthArrow(e.position, e.offset, e.size); + break; + } } }); - pdf.save("map.pdf"); + this.pdf.doc.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"); + this.addScaleBar(scaleNominator, "bottomright", { x: 2, y: 2 }); + this.pdf.doc.save("map.pdf"); } // reset to original size map.setSize(mapSize); map.getView().fit(mapExtent, { size: mapSize }); // as we are done: re-enable button - self.readyToGenerate = true; + this.readyToGenerate = true; }); // trigger rendering this.prepareRendering(function() { map.setSize(mapSizeForPrint); map.getView().fit(mapExtent, { size: mapSizeForPrint }); - - /* - let a = document.createElement("a"); - a.href = src; // need the generated PDF in here (as dataURL?) - - if (this.form.downloadType === "download") - a.download = src.substr(src.lastIndexOf("/") + 1); - else a.target = "_blank"; - - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - */ }); }, prepareRendering(callback) { @@ -371,17 +331,17 @@ this.logoImageForPDF.src = "/img/gemma-logo-for-pdf.png"; } }, - addRoundedBox(doc, x, y, w, h) { + addRoundedBox(x, y, w, h) { // draws a rounded background box at (x,y) width x height // using jsPDF units - doc.setDrawColor(255, 255, 255); - doc.setFillColor(255, 255, 255); - doc.roundedRect(x, y, w, h, 3, 3, "FD"); + this.pdf.doc.setDrawColor(255, 255, 255); + this.pdf.doc.setFillColor(255, 255, 255); + this.pdf.doc.roundedRect(x, y, w, h, 3, 3, "FD"); }, - addScaleBar(doc, docWidth, docHeight, scaleNominator, x, y) { + addScaleBar(scaleNominator, position, offset) { // scaleNominator is the x in 1:x of the map scale - // hardcode maximal width for now and place in lower right corner + // hardcode maximal width for now let maxWidth = 80; // in mm // reduce width until we'll find a nice number for printing @@ -436,138 +396,268 @@ } let size = (length * unitConversionFactor) / scaleNominator / 4; + let fullSize = size * 4; - //let x = docWidth - (size * 4 + 8); - //let y = docHeight - 6; + // x/y defaults to offset for topleft corner (normal x/y coordinates) + let x = offset.x; + let y = offset.y; - this.addRoundedBox(doc, x - 4, y - 4, size * 4 + 12, 10); + // 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 - fullSize - 8; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y = this.pdf.height - offset.y - 10; + } + + // to give the outer white box 4mm padding + let scaleBarX = x + 4; + let scaleBarY = y + 5; // 5 because above the scalebar will be the numbers + + // draw outer white box + this.addRoundedBox(x, y, fullSize + 8, 10); - doc.setDrawColor(0, 0, 0); - doc.setFillColor(0, 0, 0); - doc.rect(x, y, size, 1, "FD"); - doc.setFillColor(255, 255, 255); - doc.setDrawColor(0, 0, 0); - doc.rect(x + size, y, size, 1, "FD"); - doc.setFillColor(0, 0, 0); - doc.setDrawColor(0, 0, 0); - doc.rect(x + size * 2, y, size * 2, 1, "FD"); - doc.setFontSize(5); - doc.text(x, y + 3, "0"); + // draw first part of scalebar + this.pdf.doc.setDrawColor(0, 0, 0); + this.pdf.doc.setFillColor(0, 0, 0); + this.pdf.doc.rect(scaleBarX, scaleBarY, size, 1, "FD"); + + // draw second part of scalebar + this.pdf.doc.setDrawColor(0, 0, 0); + this.pdf.doc.setFillColor(255, 255, 255); + this.pdf.doc.rect(scaleBarX + size, scaleBarY, size, 1, "FD"); + + // draw third part of scalebar + this.pdf.doc.setDrawColor(0, 0, 0); + this.pdf.doc.setFillColor(0, 0, 0); + this.pdf.doc.rect(scaleBarX + size * 2, scaleBarY, size * 2, 1, "FD"); + + // draw numeric labels above scalebar + this.pdf.doc.setFontSize(5); + this.pdf.doc.text(scaleBarX, scaleBarY - 1, "0"); // /4 and could give 2.5. We still round, because of floating point arith - doc.text( - x + size, - y + 3, + this.pdf.doc.text( + scaleBarX + size - 1, + scaleBarY - 1, (Math.round((length * 10) / 4) / 10).toString() ); - doc.text(x + size * 2, y + 3, Math.round(length / 2).toString()); - doc.text(x + size * 4, y + 3, Math.round(length).toString() + " " + unit); + this.pdf.doc.text( + scaleBarX + size * 2 - 2, + scaleBarY - 1, + Math.round(length / 2).toString() + ); + this.pdf.doc.text( + scaleBarX + size * 4 - 4, + scaleBarY - 1, + Math.round(length).toString() + " " + unit + ); }, - addNorthArrow(doc, x1, y1, size) { + addNorthArrow(position, offset, size) { + // TODO: fix positioning + // x/y defaults to offset for topleft corner (normal x/y coordinates) + let x1 = offset.x; + let y1 = 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) { + x1 = this.pdf.width - offset.x - size; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y1 = this.pdf.height - offset.y - size; + } + var y2 = y1 + size * 3; var x3 = x1 - size * 2; var y3 = y1 + size * 5; var x4 = x1 + size * 2; - //white triangle - doc.setFillColor(255, 255, 255); - doc.setDrawColor(255, 255, 255); - doc.triangle(x3 - 0.8, y3 + 1.2, x1, y1 - 1.2, x1, y2 + 0.6, "F"); - doc.triangle(x1, y1 - 1.2, x1, y2 + 0.6, x4 + 0.8, y3 + 1.2, "F"); - //north arrow - doc.setDrawColor(0, 0, 0); - doc.setFillColor(255, 255, 255); - doc.triangle(x3, y3, x1, y1, x1, y2, "FD"); - doc.setFillColor(0, 0, 0); - doc.triangle(x1, y1, x1, y2, x4, y3, "FD"); - doc.setFontSize(size * 3.1); - doc.setTextColor(255, 255, 255); - doc.setFontStyle("bold"); - doc.text(size < 3 ? x1 - 0.5 : x1 - 1.3, y3 + 1, "N"); - doc.setFontSize(size * 3); - doc.setTextColor(0, 0, 0); - doc.setFontStyle("normal"); - doc.text(size < 3 ? x1 - 0.5 : x1 - 1.3, y3 + 1, "N"); + // white triangle + this.pdf.doc.setFillColor(255, 255, 255); + this.pdf.doc.setDrawColor(255, 255, 255); + this.pdf.doc.triangle( + x3 - 0.8, + y3 + 1.2, + x1, + y1 - 1.2, + x1, + y2 + 0.6, + "F" + ); + this.pdf.doc.triangle( + x1, + y1 - 1.2, + x1, + y2 + 0.6, + x4 + 0.8, + y3 + 1.2, + "F" + ); + + // north arrow + this.pdf.doc.setDrawColor(0, 0, 0); + this.pdf.doc.setFillColor(255, 255, 255); + this.pdf.doc.triangle(x3, y3, x1, y1, x1, y2, "FD"); + this.pdf.doc.setFillColor(0, 0, 0); + this.pdf.doc.triangle(x1, y1, x1, y2, x4, y3, "FD"); + this.pdf.doc.setFontSize(size * 3.1); + this.pdf.doc.setTextColor(255, 255, 255); + this.pdf.doc.setFontStyle("bold"); + this.pdf.doc.text(size < 3 ? x1 - 0.5 : x1 - 1.3, y3 + 1, "N"); + this.pdf.doc.setFontSize(size * 3); + this.pdf.doc.setTextColor(0, 0, 0); + this.pdf.doc.setFontStyle("normal"); + this.pdf.doc.text(size < 3 ? x1 - 0.5 : x1 - 1.3, y3 + 1, "N"); }, // add some text at specific coordinates and determine how many wrolds in single line - addText(doc, postitionX, positionY, size, color, lineWidth, text) { - // split the incoming string to an array, each element is a string of words in a single line - var textLines = doc.splitTextToSize(text, lineWidth); - doc.setTextColor(color); - doc.setFontSize(size); - doc.text(postitionX, positionY, textLines); - }, - addPageInfo(doc, x, y, width, height, textSize) { - this.addRoundedBox(doc, x, y, width, height); - let str = - this.$gettext("Date of publication:") + - " " + - new Date().toLocaleString(locale2) + - " " + - this.$gettext("– generated by:") + - " " + - this.user; - this.addText(doc, x + 5, y + 5, textSize, "black", 100, str); - }, - addAboutBox(doc, docWidth, docHeight) { - let top = docHeight - 20; - this.addRoundedBox(doc, 0, top, 120, 20); + addText(position, offset, width, height, padding, fontSize, color, text) { + // 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 - 8; + } + if (["bottomright", "bottomleft"].indexOf(position) !== -1) { + y = this.pdf.height - offset.y - 10; + } + + this.addRoundedBox(x, y, width, height); + + // replace placeholders + if (text.includes("{date}")) { + text = text.replace("{date}", new Date().toLocaleString(locale2)); + } + if (text.includes("{user}")) { + text = text.replace("{user}", this.user); + } - let logoImage = this.logoImageForPDF; - let aspectRatio = logoImage.width / logoImage.height; - doc.addImage(logoImage, "PNG", 5, docHeight - 19, 110, 110 / aspectRatio); - - let str = - "Dislaimer: Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua."; - this.addText(doc, 5, docHeight - 6, 8, "black", 115, str); + // split the incoming string to an array, each element is a string of words in a single line + var textLines = this.pdf.doc.splitTextToSize(text, width - 2 * padding); + this.pdf.doc.setTextColor(color); + this.pdf.doc.setFontSize(fontSize); + this.pdf.doc.text(x + padding, y + padding, textLines); }, - addLegend(doc, fromTop, docWidth) { - // transforming into an HTMLImageElement only to find out - // the width x height of the legend image - // FUTURE: find a better way to get the width and height - let legendImage = new Image(); - legendImage.src = this.isolinesLegendImgDataURL; - let aspectRatio = legendImage.width / legendImage.height; + addImage(url, format, position, offset, width, height, border) { + // x/y defaults to offset for topleft corner (normal x/y coordinates) + let x = offset.x; + let y = offset.y; - this.addRoundedBox(doc, docWidth - 54, fromTop, 54, 50 / aspectRatio + 4); - doc.addImage( - legendImage, - docWidth - 52, - fromTop + 2, - 50, - 50 / aspectRatio + // 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); + + let image = new Image(); + image.src = url; + this.pdf.doc.addImage( + image, + x + border, + y + border, + width - 2 * border, + height - 2 * border ); }, - addBottleneckInfo(doc, height, docWidth) { - this.addRoundedBox(doc, docWidth - 54, 0, 54, height); - - console.log("Fontlist =", doc.getFontList()); - doc.setFont("times", "normal"); - - let name, w, str; + addLegend(position, offset) { + if ( + this.selectedBottleneck && + this.getLayerByName("Bottleneck isolines").isVisible + ) { + // transforming into an HTMLImageElement only to find out + // the width x height of the legend image + // FUTURE: find a better way to get the width and height + let legendImage = new Image(); + legendImage.src = this.isolinesLegendImgDataURL; + let aspectRatio = legendImage.width / legendImage.height; + let width = 54; + let height = width / aspectRatio; + let padding = 2; - doc.setFontStyle("italic"); - name = this.$gettext("Bottleneck") + ": "; - w = doc.getTextWidth(name); - this.addText(doc, docWidth - 51, 4, 8, "black", 46, name); - doc.setFontStyle("bold"); - str = this.selectedBottleneck; - this.addText(doc, docWidth - 51 + w, 4, 8, "black", 46, str); + // 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; + } - doc.setFontStyle("italic"); - name = this.$gettext("Survey date") + ": "; - w = doc.getTextWidth(name); - this.addText(doc, docWidth - 51, 7.5, 8, "black", 46, name); - doc.setFontStyle("normal"); - str = this.selectedSurvey.date_info; - this.addText(doc, docWidth - 51 + w, 7.5, 8, "black", 46, str); + this.addRoundedBox(x, y, width, height); + this.pdf.doc.addImage( + legendImage, + x + padding, + y + padding, + width - 2 * padding, + height - 2 * padding + ); + } + }, + addBottleneckInfo(position, offset) { + if ( + this.selectedBottleneck && + this.getLayerByName("Bottleneck isolines").isVisible + ) { + let width = 54; + let height = 13; + let padding = 5; + + // 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; + } - doc.setFontStyle("italic"); - name = this.$gettext("Ref gauge") + ": "; - w = doc.getTextWidth(name); - this.addText(doc, docWidth - 51, 11, 8, "black", 46, name); - doc.setFontStyle("normal"); - str = this.selectedSurvey.gauge_objname; - this.addText(doc, docWidth - 51 + w, 11, 8, "black", 46, str); + this.addRoundedBox(x, y, width, height); + + this.pdf.doc.setFont("times", "normal"); + this.pdf.doc.setFontSize(9); + + let str, w; + + str = this.$gettext("Bottleneck") + ": "; + w = this.pdf.doc.getTextWidth(str); + this.pdf.doc.setFontStyle("italic"); + this.pdf.doc.text(x + padding, y + padding, str); + str = this.selectedBottleneck; + this.pdf.doc.setFontStyle("bold"); + this.pdf.doc.text(x + padding + w, y + padding, str); + + str = this.$gettext("Survey date") + ": "; + w = this.pdf.doc.getTextWidth(str); + this.pdf.doc.setFontStyle("italic"); + this.pdf.doc.text(x + padding, y + padding + 3, str); + str = this.selectedSurvey.date_info; + this.pdf.doc.setFontStyle("normal"); + this.pdf.doc.text(x + padding + w, y + padding + 3, str); + + str = this.$gettext("Ref gauge") + ": "; + w = this.pdf.doc.getTextWidth(str); + this.pdf.doc.setFontStyle("italic"); + this.pdf.doc.text(x + padding, y + padding + 6, str); + str = this.selectedSurvey.gauge_objname; + this.pdf.doc.setFontStyle("normal"); + this.pdf.doc.text(x + padding + w, y + padding + 6, str); + } } }, mounted() {