Print: add QR-code to output

This commit is contained in:
Pieter Vander Vennet 2023-11-15 03:42:37 +01:00
parent 24b9f045c8
commit c21e88415d
16 changed files with 544 additions and 185 deletions

View file

@ -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
View 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)
}
}

View file

@ -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()) {

View file

@ -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,
},

View file

@ -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()