2022-09-08 21:40:48 +02:00
import jsPDF from "jspdf"
import { UIEventSource } from "../Logic/UIEventSource"
import Minimap , { MinimapObj } from "./Base/Minimap"
import Loc from "../Models/Loc"
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"
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline"
import ShowDataLayer from "./ShowDataLayer/ShowDataLayer"
import { BBox } from "../Logic/BBox"
2021-11-07 16:34:51 +01:00
2021-09-21 02:10:42 +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
* /
2021-07-28 02:51:07 +02:00
export default class ExportPDF {
// dimensions of the map in milimeter
2021-09-09 00:05:51 +02:00
public isRunning = new UIEventSource ( true )
2021-07-28 02:51:07 +02:00
// A4: 297 * 210mm
2022-09-08 21:40:48 +02:00
private readonly mapW = 297
private readonly mapH = 210
2021-07-28 02:51:07 +02:00
private readonly scaling = 2
2022-09-08 21:40:48 +02:00
private readonly freeDivId : string
private readonly _layout : LayoutConfig
private _screenhotTaken = false
constructor ( options : {
freeDivId : string
location : UIEventSource < Loc >
background? : UIEventSource < BaseLayer >
features : FeaturePipeline
layout : LayoutConfig
} ) {
this . freeDivId = options . freeDivId
this . _layout = options . layout
const self = this
2021-07-28 02:51:07 +02:00
// We create a minimap at the given location and attach it to the given 'hidden' element
2022-09-08 21:40:48 +02:00
const l = options . location . data
2021-07-28 12:36:39 +02:00
const loc = {
2021-07-28 15:14:13 +02:00
lat : l.lat ,
2021-07-28 12:36:39 +02:00
lon : l.lon ,
2022-09-08 21:40:48 +02:00
zoom : l.zoom + 1 ,
2021-07-28 12:36:39 +02:00
}
2021-07-28 15:14:13 +02:00
2021-09-21 02:10:42 +02:00
const minimap = Minimap . createMiniMap ( {
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 ,
2022-09-08 21:40:48 +02:00
onFullyLoaded : ( _ ) = >
window . setTimeout ( ( ) = > {
if ( self . _screenhotTaken ) {
return
}
try {
self . CreatePdf ( minimap )
. then ( ( ) = > self . cleanup ( ) )
. catch ( ( ) = > self . cleanup ( ) )
} catch ( e ) {
console . error ( e )
self . cleanup ( )
}
} , 500 ) ,
2021-07-28 02:51:07 +02:00
} )
2022-09-08 21:40:48 +02:00
minimap . SetStyle (
` width: ${ this . mapW * this . scaling } mm; height: ${ this . mapH * this . scaling } mm; `
)
2021-07-28 02:51:07 +02:00
minimap . AttachTo ( options . freeDivId )
// Next: we prepare the features. Only fully contained features are shown
2022-09-08 21:40:48 +02:00
minimap . leafletMap . addCallbackAndRunD ( ( leaflet ) = > {
2021-07-28 02:51:07 +02:00
const bounds = BBox . fromLeafletBounds ( leaflet . getBounds ( ) . pad ( 0.2 ) )
2022-09-08 21:40:48 +02:00
options . features . GetTilesPerLayerWithin ( bounds , ( tile ) = > {
2021-11-07 16:34:51 +01:00
if ( tile . layer . layerDef . minzoom > l . zoom ) {
2021-10-14 17:54:43 +02:00
return
}
2022-09-08 21:40:48 +02:00
if ( tile . layer . layerDef . id . startsWith ( "note_import" ) ) {
2022-01-31 11:01:05 +01:00
// Don't export notes to import
2022-09-08 21:40:48 +02:00
return
2022-01-31 11:01:05 +01:00
}
2022-09-08 21:40:48 +02:00
new ShowDataLayer ( {
features : tile ,
leafletMap : minimap.leafletMap ,
layerToShow : tile.layer.layerDef ,
doShowLayer : tile.layer.isDisplayed ,
state : undefined ,
} )
2021-09-21 02:10:42 +02:00
} )
} )
2021-07-28 02:51:07 +02:00
2021-10-15 05:20:02 +02:00
State . state . AddAllOverlaysToMap ( minimap . leafletMap )
2021-07-28 02:51:07 +02:00
}
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 )
2022-09-08 21:40:48 +02:00
this . _screenhotTaken = true
2021-07-28 02:51:07 +02:00
}
2021-11-03 00:44:53 +01:00
private async CreatePdf ( minimap : MinimapObj ) {
2021-10-03 01:38:57 +02:00
console . log ( "PDF creation started" )
2022-09-08 21:40:48 +02:00
const t = Translations . t . general . pdf
2021-10-03 01:38:57 +02:00
const layout = this . _layout
2021-07-28 02:51:07 +02:00
2022-09-08 21:40:48 +02:00
let doc = new jsPDF ( "landscape" )
2021-07-28 02:51:07 +02:00
2021-11-03 00:44:53 +01:00
const image = await minimap . TakeScreenshot ( )
2021-07-28 02:51:07 +02:00
// @ts-ignore
2022-09-08 21:40:48 +02:00
doc . addImage ( image , "PNG" , 0 , 0 , this . mapW , this . mapH )
2021-07-28 02:51:07 +02:00
doc . setDrawColor ( 255 , 255 , 255 )
doc . setFillColor ( 255 , 255 , 255 )
2022-09-08 21:40:48 +02:00
doc . roundedRect ( 12 , 10 , 145 , 25 , 5 , 5 , "FD" )
2021-07-28 02:51:07 +02:00
doc . setFontSize ( 20 )
2021-07-29 01:57:45 +02:00
doc . textWithLink ( layout . title . txt , 40 , 18.5 , {
maxWidth : 125 ,
2022-09-08 21:40:48 +02:00
url : window.location.href ,
2021-07-28 02:51:07 +02:00
} )
doc . setFontSize ( 10 )
2021-07-29 01:57:45 +02:00
doc . text ( t . generatedWith . txt , 40 , 23 , {
2022-09-08 21:40:48 +02:00
maxWidth : 125 ,
2021-07-28 02:51:07 +02:00
} )
2021-08-22 20:10:19 +02:00
const backgroundLayer : BaseLayer = State . state . backgroundLayer . data
2022-09-08 21:40:48 +02:00
const attribution = new FixedUiElement (
backgroundLayer . layer ( ) . getAttribution ( ) ? ? backgroundLayer . name
) . ConstructElement ( ) . textContent
2021-07-29 01:57:45 +02:00
doc . textWithLink ( t . attr . txt , 40 , 26.5 , {
maxWidth : 125 ,
2022-09-08 21:40:48 +02:00
url : "https://www.openstreetmap.org/copyright" ,
2021-07-29 01:57:45 +02:00
} )
2022-09-08 21:40:48 +02:00
doc . text (
t . attrBackground . Subs ( {
background : attribution ,
} ) . txt ,
40 ,
30
)
2021-07-29 01:57:45 +02:00
2021-08-22 20:10:19 +02:00
let date = new Date ( ) . toISOString ( ) . substr ( 0 , 16 )
2021-07-29 01:57:45 +02:00
doc . setFontSize ( 7 )
2022-09-08 21:40:48 +02:00
doc . text (
t . versionInfo . Subs ( {
version : Constants.vNumber ,
date : date ,
} ) . txt ,
40 ,
34 ,
{
maxWidth : 125 ,
}
)
2021-08-22 20:10:19 +02:00
2021-07-28 02:51:07 +02:00
// Add the logo of the layout
2022-09-08 21:40:48 +02:00
let img = document . createElement ( "img" )
2021-07-28 02:51:07 +02:00
const imgSource = layout . icon
2022-09-08 21:40:48 +02:00
const imgType = imgSource . substr ( imgSource . lastIndexOf ( "." ) + 1 )
2021-07-28 02:51:07 +02:00
img . src = imgSource
2021-07-29 01:57:45 +02:00
if ( imgType . toLowerCase ( ) === "svg" ) {
new FixedUiElement ( "" ) . AttachTo ( this . freeDivId )
// This is an svg image, we use the canvas to convert it to a png
2022-09-08 21:40:48 +02:00
const canvas = document . createElement ( "canvas" )
const ctx = canvas . getContext ( "2d" )
2021-07-29 01:57:45 +02:00
canvas . width = 500
canvas . height = 500
img . style . width = "100%"
img . style . height = "100%"
2022-09-08 21:40:48 +02:00
ctx . drawImage ( img , 0 , 0 , 500 , 500 )
2021-07-29 01:57:45 +02:00
const base64img = canvas . toDataURL ( "image/png" )
2022-09-08 21:40:48 +02:00
doc . addImage ( base64img , "png" , 15 , 12 , 20 , 20 )
2021-07-29 01:57:45 +02:00
} else {
try {
2022-09-08 21:40:48 +02:00
doc . addImage ( img , imgType , 15 , 12 , 20 , 20 )
2021-07-29 01:57:45 +02:00
} catch ( e ) {
console . error ( e )
}
2021-07-28 02:51:07 +02:00
}
2022-09-08 21:40:48 +02:00
doc . save ( ` MapComplete_ ${ layout . title . txt } _ ${ date } .pdf ` )
2021-07-28 02:51:07 +02:00
2021-08-22 20:10:19 +02:00
this . isRunning . setData ( false )
2021-07-28 02:51:07 +02:00
}
}