mapcomplete/UI/ExportPDF.ts

197 lines
6.4 KiB
TypeScript
Raw Normal View History

2021-07-28 02:51:07 +02:00
/**
* Creates screenshoter to take png screenshot
* Creates jspdf and downloads it
* - landscape pdf
*
* To add new layout:
* - add new possible layout name in constructor
* - add new layout in "PDFLayout"
* -> in there are more instructions
*/
import jsPDF from "jspdf";
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
import {UIEventSource} from "../Logic/UIEventSource";
import Minimap from "./Base/Minimap";
import Loc from "../Models/Loc";
import {BBox} from "../Logic/GeoOperations";
import ShowDataLayer from "./ShowDataLayer";
import BaseLayer from "../Models/BaseLayer";
import {FixedUiElement} from "./Base/FixedUiElement";
import Translations from "./i18n/Translations";
import State from "../State";
import Constants from "../Models/Constants";
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
2021-07-28 02:51:07 +02:00
export default class ExportPDF {
// dimensions of the map in milimeter
public isRunning = new UIEventSource(true)
2021-07-28 02:51:07 +02:00
// A4: 297 * 210mm
private readonly mapW = 297;
private readonly mapH = 210;
private readonly scaling = 2
private readonly freeDivId: string;
private readonly _layout: UIEventSource<LayoutConfig>;
2021-07-28 15:14:13 +02:00
private _screenhotTaken = false;
2021-07-28 02:51:07 +02:00
constructor(
options: {
freeDivId: string,
location: UIEventSource<Loc>,
background?: UIEventSource<BaseLayer>
features: UIEventSource<{ feature: any }[]>,
layout: UIEventSource<LayoutConfig>
}
) {
this.freeDivId = options.freeDivId;
this._layout = options.layout;
const self = this;
// We create a minimap at the given location and attach it to the given 'hidden' element
2021-07-28 12:36:39 +02:00
const l = options.location.data;
const loc = {
2021-07-28 15:14:13 +02:00
lat: l.lat,
2021-07-28 12:36:39 +02:00
lon: l.lon,
zoom: l.zoom + 1
}
2021-07-28 15:14:13 +02:00
2021-07-28 02:51:07 +02:00
const minimap = new Minimap({
2021-07-28 12:36:39 +02:00
location: new UIEventSource<Loc>(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot
2021-07-28 02:51:07 +02:00
background: options.background,
2021-07-28 12:36:39 +02:00
allowMoving: false,
2021-07-28 02:51:07 +02:00
onFullyLoaded: leaflet => window.setTimeout(() => {
2021-07-28 15:14:13 +02:00
if (self._screenhotTaken) {
return;
}
try {
self.CreatePdf(leaflet)
.then(() => self.cleanup())
.catch(() => self.cleanup())
} catch (e) {
2021-07-28 02:51:07 +02:00
console.error(e)
self.cleanup()
}
}, 500)
})
minimap.SetStyle(`width: ${this.mapW * this.scaling}mm; height: ${this.mapH * this.scaling}mm;`)
minimap.AttachTo(options.freeDivId)
// Next: we prepare the features. Only fully contained features are shown
const bounded = options.features.map(feats => {
const leaflet = minimap.leafletMap.data;
if (leaflet === undefined) {
return feats
}
const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.2))
return feats.filter(f => BBox.get(f.feature).isContainedIn(bounds))
}, [minimap.leafletMap])
// Add the features to the minimap
new ShowDataLayer(
bounded,
minimap.leafletMap,
options.layout,
false
)
}
2021-07-28 15:14:13 +02:00
private cleanup() {
2021-09-09 23:24:21 +02:00
new FixedUiElement("Screenshot taken!").AttachTo(this.freeDivId)
2021-07-28 15:14:13 +02:00
this._screenhotTaken = true;
2021-07-28 02:51:07 +02:00
}
private async CreatePdf(leaflet: L.Map) {
const t = Translations.t.general.pdf;
const layout = this._layout.data
const screenshotter = new SimpleMapScreenshoter();
//minimap op index.html -> hidden daar alles op doen en dan weg
//minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline
screenshotter.addTo(leaflet);
console.log("Taking screenshot")
let doc = new jsPDF('landscape');
const image = (await screenshotter.takeScreen('image'))
// @ts-ignore
doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH);
doc.setDrawColor(255, 255, 255)
doc.setFillColor(255, 255, 255)
doc.roundedRect(12, 10, 145, 25, 5, 5, 'FD')
2021-07-28 02:51:07 +02:00
doc.setFontSize(20)
doc.textWithLink(layout.title.txt, 40, 18.5, {
maxWidth: 125,
url: window.location.href
2021-07-28 02:51:07 +02:00
})
doc.setFontSize(10)
doc.text(t.generatedWith.txt, 40, 23, {
maxWidth: 125
2021-07-28 02:51:07 +02:00
})
const backgroundLayer: BaseLayer = State.state.backgroundLayer.data
const attribution = new FixedUiElement(backgroundLayer.layer().getAttribution() ?? backgroundLayer.name).ConstructElement().innerText
doc.textWithLink(t.attr.txt, 40, 26.5, {
maxWidth: 125,
url: "https://www.openstreetmap.org/copyright"
})
doc.text(t.attrBackground.Subs({
background: attribution
}).txt, 40, 30)
let date = new Date().toISOString().substr(0, 16)
doc.setFontSize(7)
doc.text(t.versionInfo.Subs({
version: Constants.vNumber,
date: date
}).txt, 40, 34, {
maxWidth: 125
})
2021-07-28 02:51:07 +02:00
// Add the logo of the layout
let img = document.createElement('img');
const imgSource = layout.icon
const imgType = imgSource.substr(imgSource.lastIndexOf(".") + 1);
2021-07-28 02:51:07 +02:00
img.src = imgSource
console.log(imgType)
if (imgType.toLowerCase() === "svg") {
new FixedUiElement("").AttachTo(this.freeDivId)
// This is an svg image, we use the canvas to convert it to a png
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d');
canvas.width = 500
canvas.height = 500
img.style.width = "100%"
img.style.height = "100%"
ctx.drawImage(img, 0, 0, 500, 500);
const base64img = canvas.toDataURL("image/png")
doc.addImage(base64img, 'png', 15, 12, 20, 20);
} else {
try {
doc.addImage(img, imgType, 15, 12, 20, 20);
} catch (e) {
console.error(e)
}
2021-07-28 02:51:07 +02:00
}
doc.save(`MapComplete_${layout.title.txt}_${date}.pdf`);
2021-07-28 02:51:07 +02:00
this.isRunning.setData(false)
2021-07-28 02:51:07 +02:00
}
}