bug fixes & improvements & missing features & font loader

This commit is contained in:
2026-04-07 00:36:21 +03:00
parent ad0d2fda0a
commit b6287906a9
50 changed files with 4087 additions and 1843 deletions

View File

@@ -1,38 +1,126 @@
/// Layout Engine Web Worker
/// Template JSON + Data JSON → Layout WASM → LayoutResult
/// Font loading is dynamic — fetches from backend API based on template needs.
import init, { loadFonts, computeLayout, generateBarcode } from '../core/wasm-layout/dreport_layout.js'
import init, { loadFonts, addFonts, computeLayout, generateBarcode } from '../core/wasm-layout/dreport_layout.js'
import type { LayoutResult } from '../core/layout-types'
let initPromise: Promise<void> | null = null
const FONT_FILES = [
{ path: '/fonts/NotoSans-Regular.ttf', family: 'Noto Sans' },
{ path: '/fonts/NotoSans-Bold.ttf', family: 'Noto Sans' },
{ path: '/fonts/NotoSans-Italic.ttf', family: 'Noto Sans' },
{ path: '/fonts/NotoSans-BoldItalic.ttf', family: 'Noto Sans' },
{ path: '/fonts/NotoSansMono-Regular.ttf', family: 'Noto Sans Mono' },
]
/** Configurable font API base URL. Default: same origin /api/fonts */
let fontApiBase = '/api/fonts'
/** Font catalog from backend API */
interface FontVariantInfo {
weight: number
italic: boolean
}
interface FontFamilyInfo {
family: string
variants: FontVariantInfo[]
}
let fontCatalog: FontFamilyInfo[] = []
/** Track which font families are already loaded into WASM */
const loadedFamilies = new Set<string>()
async function doInit() {
console.log('[layout-worker] WASM başlatılıyor...')
await init({ module_or_path: '/wasm/dreport_layout_bg.wasm' })
console.log('[layout-worker] Fontlar yükleniyor...')
const families: string[] = []
const buffers: Uint8Array[] = []
// Fetch font catalog from backend
try {
const res = await fetch(fontApiBase)
if (res.ok) {
fontCatalog = await res.json()
console.log(`[layout-worker] Font kataloğu yüklendi (${fontCatalog.length} aile)`)
} else {
console.warn(`[layout-worker] Font kataloğu alınamadı (HTTP ${res.status}), static fallback deneniyor`)
await loadStaticFallback()
return
}
} catch {
console.warn('[layout-worker] Font API erişilemedi, static fallback deneniyor')
await loadStaticFallback()
return
}
// Load default fonts (Noto Sans + Noto Sans Mono)
await ensureFamiliesLoaded(['Noto Sans', 'Noto Sans Mono'])
console.log('[layout-worker] Hazır')
}
/** Fallback: load fonts from static /fonts/ directory (backwards compat) */
async function loadStaticFallback() {
const STATIC_FONTS = [
'/fonts/NotoSans-Regular.ttf',
'/fonts/NotoSans-Bold.ttf',
'/fonts/NotoSans-Italic.ttf',
'/fonts/NotoSans-BoldItalic.ttf',
'/fonts/NotoSansMono-Regular.ttf',
]
const buffers: Uint8Array[] = []
await Promise.all(
FONT_FILES.map(async (f) => {
const res = await fetch(new URL(f.path, self.location.origin).href)
const buf = await res.arrayBuffer()
families.push(f.family)
buffers.push(new Uint8Array(buf))
STATIC_FONTS.map(async (path) => {
const url = new URL(path, self.location.origin).href
const res = await fetch(url)
if (res.ok) {
buffers.push(new Uint8Array(await res.arrayBuffer()))
}
})
)
loadFonts(JSON.stringify(families), buffers)
console.log('[layout-worker] Hazır')
if (buffers.length > 0) {
loadFonts(buffers)
loadedFamilies.add('noto sans')
loadedFamilies.add('noto sans mono')
console.log(`[layout-worker] Static fallback: ${buffers.length} font yüklendi`)
}
}
/** Load all variants of given families from the API into WASM */
async function ensureFamiliesLoaded(families: string[]): Promise<void> {
const toLoad = families.filter(f => !loadedFamilies.has(f.toLowerCase()))
if (toLoad.length === 0) return
const buffers: Uint8Array[] = []
for (const family of toLoad) {
const info = fontCatalog.find(f => f.family.toLowerCase() === family.toLowerCase())
if (!info) {
console.warn(`[layout-worker] Font ailesi bulunamadı: ${family}`)
continue
}
const fetches = info.variants.map(async (v) => {
const url = `${fontApiBase}/${encodeURIComponent(info.family)}/${v.weight}/${v.italic}`
const res = await fetch(url)
if (res.ok) {
return new Uint8Array(await res.arrayBuffer())
}
return null
})
const results = await Promise.all(fetches)
for (const buf of results) {
if (buf && buf.byteLength > 0) {
buffers.push(buf)
}
}
loadedFamilies.add(family.toLowerCase())
}
if (buffers.length > 0) {
if (loadedFamilies.size <= toLoad.length) {
// First load — use loadFonts
loadFonts(buffers)
} else {
// Subsequent loads — use addFonts
addFonts(buffers)
}
console.log(`[layout-worker] ${toLoad.join(', ')} yüklendi (${buffers.length} variant)`)
}
}
function ensureInit(): Promise<void> {
@@ -45,14 +133,32 @@ function ensureInit(): Promise<void> {
type WorkerMessage =
| { type: 'compile'; templateJson: string; dataJson: string; id: number }
| { type: 'barcode'; format: string; value: string; width: number; height: number; includeText: boolean; id: number }
| { type: 'configure'; fontApiBase?: string }
self.onmessage = async (e: MessageEvent<WorkerMessage>) => {
const msg = e.data
if (msg.type === 'configure') {
if (msg.fontApiBase) {
fontApiBase = msg.fontApiBase
}
return
}
if (msg.type === 'compile') {
try {
await ensureInit()
// Extract font families from template and ensure they're loaded
try {
const tpl = JSON.parse(msg.templateJson)
if (Array.isArray(tpl.fonts) && tpl.fonts.length > 0) {
await ensureFamiliesLoaded(tpl.fonts)
}
} catch {
// Template parse failure will be caught by computeLayout below
}
const t0 = performance.now()
const resultJson = computeLayout(msg.templateJson, msg.dataJson)
const layout: LayoutResult = JSON.parse(resultJson)