forked from MapComplete/MapComplete
Print: add QR-code to output
This commit is contained in:
parent
24b9f045c8
commit
c21e88415d
16 changed files with 544 additions and 185 deletions
|
@ -11,14 +11,11 @@
|
|||
import Locale from "../i18n/Locale"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import DownloadHelper from "./DownloadHelper"
|
||||
import Qr from "../../Utils/Qr";
|
||||
|
||||
export let templateName: string
|
||||
export let state: ThemeViewState
|
||||
const template: PdfTemplateInfo = SvgToPdf.templates[templateName]
|
||||
let mainText: Translation =
|
||||
typeof template.description === "string"
|
||||
? new Translation(template.description)
|
||||
: template.description
|
||||
let t = Translations.t.general.download
|
||||
const downloadHelper = new DownloadHelper(state)
|
||||
|
||||
|
@ -32,8 +29,14 @@
|
|||
const creator = new SvgToPdf(title, templates, {
|
||||
state,
|
||||
freeComponentId: "belowmap",
|
||||
createImage: (key: string, width: string, height: string) =>
|
||||
downloadHelper.createImage(key, width, height),
|
||||
createImage: (key: string, width: string, height: string) => {
|
||||
console.log("Creating an image for key", key)
|
||||
if(key === "qr"){
|
||||
const toShare = window.location.href.split("#")[0]
|
||||
return new Qr(toShare).toImageElement(parseFloat(width), parseFloat(height))
|
||||
}
|
||||
return downloadHelper.createImage(key, width, height);
|
||||
},
|
||||
textSubstitutions: <Record<string, string>>{
|
||||
"layout.title": state.layout.title,
|
||||
layoutid: state.layout.id,
|
||||
|
@ -59,7 +62,7 @@
|
|||
extension="pdf"
|
||||
helperText={t.downloadAsPdfHelper}
|
||||
metaIsIncluded={false}
|
||||
{mainText}
|
||||
mainText={t.pdf.current_view_generic.Subs({orientation: template.orientation, paper_size: template.format.toUpperCase()})}
|
||||
mimetype="application/pdf"
|
||||
{state}
|
||||
/>
|
||||
|
|
26
src/Utils/Qr.ts
Normal file
26
src/Utils/Qr.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import Qrcode from "qrcode-generator"
|
||||
|
||||
/**
|
||||
* Creates a QR-code as Blob
|
||||
*/
|
||||
export default class Qr {
|
||||
private _textToShow: string
|
||||
|
||||
constructor(textToShow: string) {
|
||||
this._textToShow = textToShow
|
||||
}
|
||||
|
||||
public toImageElement(totalSize: number): string {
|
||||
console.log("Creating a QR code for", this._textToShow)
|
||||
const typeNumber = 0
|
||||
const errorCorrectionLevel = "L"
|
||||
const qr = Qrcode(typeNumber, errorCorrectionLevel)
|
||||
qr.addData(this._textToShow)
|
||||
qr.make()
|
||||
const moduleCount = qr.getModuleCount()
|
||||
const img = document.createElement("img")
|
||||
const cellSize = Math.round(totalSize / moduleCount)
|
||||
console.log("Cellsize", cellSize)
|
||||
return qr.createDataURL(cellSize)
|
||||
}
|
||||
}
|
|
@ -73,6 +73,8 @@ export class PngMapCreator {
|
|||
pixelRatio,
|
||||
})
|
||||
|
||||
console.log("Creating a map with size", this._options.width, this._options.height)
|
||||
|
||||
const map = new UIEventSource<MlMap>(mapElem)
|
||||
const mla = new MapLibreAdaptor(map)
|
||||
mla.zoom.setData(newZoom)
|
||||
|
@ -81,10 +83,10 @@ export class PngMapCreator {
|
|||
mla.allowZooming.setData(false)
|
||||
mla.allowMoving.setData(false)
|
||||
|
||||
this._state?.showNormalDataOn(map)
|
||||
console.log("Creating a map with size", this._options.width, this._options.height)
|
||||
|
||||
setState("Waiting for the data")
|
||||
this._state?.showNormalDataOn(map)
|
||||
setState("Waiting for the data")
|
||||
|
||||
await this._state.dataIsLoading.AsPromise((loading) => !loading)
|
||||
setState("Waiting for styles to be fully loaded")
|
||||
while (!map?.data?.isStyleLoaded()) {
|
||||
|
|
|
@ -313,6 +313,9 @@ class SvgToPdfInternals {
|
|||
console.log("Creating image with key", key, "searching rect in", x, y)
|
||||
const rectangle: SVGRectElement = this.page.findSmallestRectContaining(x, y, false)
|
||||
console.log("Got rect", rectangle)
|
||||
if (!rectangle) {
|
||||
throw new Error("No rectangle found for tspan with text:" + txt)
|
||||
}
|
||||
const w = SvgToPdfInternals.attrNumber(rectangle, "width")
|
||||
const h = SvgToPdfInternals.attrNumber(rectangle, "height")
|
||||
x = SvgToPdfInternals.attrNumber(rectangle, "x")
|
||||
|
@ -320,23 +323,25 @@ class SvgToPdfInternals {
|
|||
|
||||
// Actually, dots per mm, not dots per inch ;)
|
||||
const dpi = 60
|
||||
|
||||
const img = this.page.options.createImage(key, dpi * w + "px", dpi * h + "px")
|
||||
|
||||
const canvas = document.createElement("canvas")
|
||||
const ctx = canvas.getContext("2d")
|
||||
|
||||
canvas.width = w * dpi
|
||||
canvas.height = h * dpi
|
||||
img.style.width = `${w * dpi}px`
|
||||
img.style.height = `${h * dpi}px`
|
||||
|
||||
ctx.drawImage(img, 0, 0, w * dpi, h * dpi)
|
||||
const base64img = canvas.toDataURL("image/png")
|
||||
// Don't ask me why this magicFactor transformation is needed - but it works
|
||||
const magicFactor = 3.8
|
||||
this.addMatrix(this.doc.Matrix(1 / magicFactor, 0, 0, 1 / magicFactor, 0, 0))
|
||||
this.doc.addImage(base64img, "png", x, y, w, h)
|
||||
this.undoTransform()
|
||||
if (typeof img === "string") {
|
||||
this.doc.addImage(img, "png", x, y, w, h)
|
||||
} else {
|
||||
const canvas = document.createElement("canvas")
|
||||
canvas.width = w * dpi
|
||||
canvas.height = h * dpi
|
||||
const ctx = canvas.getContext("2d")
|
||||
img.style.width = `${w * dpi}px`
|
||||
img.style.height = `${h * dpi}px`
|
||||
ctx.drawImage(img, 0, 0, w * dpi, h * dpi)
|
||||
const base64img = canvas.toDataURL("image/png")
|
||||
// Don't ask me why this magicFactor transformation is needed - but it works
|
||||
const magicFactor = 3.8
|
||||
this.addMatrix(this.doc.Matrix(1 / magicFactor, 0, 0, 1 / magicFactor, 0, 0))
|
||||
this.doc.addImage(base64img, "png", x, y, w, h)
|
||||
this.undoTransform()
|
||||
}
|
||||
this.usedRectangles.add(rectangle.id)
|
||||
return
|
||||
}
|
||||
|
@ -557,7 +562,7 @@ export interface SvgToPdfOptions {
|
|||
*/
|
||||
state?: ThemeViewState
|
||||
|
||||
createImage(key: string, width: string, height: string): HTMLImageElement
|
||||
createImage(key: string, width: string, height: string): HTMLImageElement | string
|
||||
}
|
||||
|
||||
class SvgToPdfPage {
|
||||
|
@ -1002,7 +1007,13 @@ export interface PdfTemplateInfo {
|
|||
|
||||
export class SvgToPdf {
|
||||
public static readonly templates: Record<
|
||||
"flyer_a4" | "poster_a3" | "poster_a2" | "current_view_a4" | "current_view_a3",
|
||||
| "flyer_a4"
|
||||
| "poster_a3"
|
||||
| "poster_a2"
|
||||
| "current_view_a4"
|
||||
| "current_view_a3_portrait"
|
||||
| "current_view_a3_landscape"
|
||||
| "current_view_a2_landscape",
|
||||
PdfTemplateInfo
|
||||
> = {
|
||||
flyer_a4: {
|
||||
|
@ -1037,10 +1048,17 @@ export class SvgToPdf {
|
|||
|
||||
isPublic: true,
|
||||
},
|
||||
current_view_a3: {
|
||||
current_view_a3_landscape: {
|
||||
format: "a3",
|
||||
orientation: "landscape",
|
||||
pages: ["./assets/templates/CurrentMapWithHeader_A3_Landscape.svg"],
|
||||
description: Translations.t.general.download.pdf.current_view_a3,
|
||||
isPublic: true,
|
||||
},
|
||||
current_view_a3_portrait: {
|
||||
format: "a3",
|
||||
orientation: "portrait",
|
||||
pages: ["./assets/templates/CurrentMapWithHeaderA3.svg"],
|
||||
pages: ["./assets/templates/CurrentMapWithHeader_A3_Portrait.svg"],
|
||||
description: Translations.t.general.download.pdf.current_view_a3,
|
||||
isPublic: true,
|
||||
},
|
||||
|
|
51
src/test.ts
51
src/test.ts
|
@ -3,44 +3,17 @@ import SvelteUIElement from "./UI/Base/SvelteUIElement"
|
|||
import PointRenderingConfig from "./Models/ThemeConfig/PointRenderingConfig"
|
||||
import { UIEventSource } from "./Logic/UIEventSource"
|
||||
import Marker from "./UI/Map/Marker.svelte"
|
||||
|
||||
class Test {
|
||||
public async test() {
|
||||
await Utils.waitFor(0)
|
||||
const response = await fetch("http://localhost:1235/layers/atm/atm.json", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json;charset=utf-8",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
}
|
||||
import Qrcode from "qrcode-generator"
|
||||
import { FixedUiElement } from "./UI/Base/FixedUiElement"
|
||||
function generateQr(message: string, attachTo: string) {
|
||||
const typeNumber = 0
|
||||
const errorCorrectionLevel = "L"
|
||||
const qr = Qrcode(typeNumber, errorCorrectionLevel)
|
||||
qr.addData(message)
|
||||
qr.make()
|
||||
document.getElementById(attachTo).innerHTML = qr.createImgTag()
|
||||
}
|
||||
|
||||
const tags = new UIEventSource({
|
||||
id: "node/13",
|
||||
amenity: "public_bookcase",
|
||||
})
|
||||
|
||||
const config = new PointRenderingConfig(
|
||||
{
|
||||
location: ["point"],
|
||||
iconSize: "20,20",
|
||||
marker: [
|
||||
{
|
||||
icon: "circle",
|
||||
color: "orange",
|
||||
},
|
||||
{
|
||||
icon: "./assets/layers/atm.atm.svg",
|
||||
},
|
||||
],
|
||||
},
|
||||
"test"
|
||||
generateQr(
|
||||
"http://127.0.0.1:1234/theme.html?layout=cyclofix&z=14&lat=51.21571770000094&lon=3.219866599996749&layer-range=true&layer-gps_location=false#theme-menu:download",
|
||||
"qr"
|
||||
)
|
||||
|
||||
new SvelteUIElement(Marker, {
|
||||
config,
|
||||
tags,
|
||||
}).AttachTo("maindiv")
|
||||
// new Test().test()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue