| |
| |
| |
|
|
| import { IMG_BROKEN } from './core-enums' |
| import { PresSlide, SlideLayout, ISlideRelMedia } from './core-interfaces' |
|
|
| |
| |
| |
| |
| |
| export function encodeSlideMediaRels(layout: PresSlide | SlideLayout): Array<Promise<string>> { |
| |
| const isNode = typeof process !== 'undefined' && !!process.versions?.node && process.release?.name === 'node' |
| |
| let fs: typeof import('node:fs') | undefined |
| let https: typeof import('node:https') | undefined |
|
|
| |
| const loadNodeDeps = isNode |
| ? async () => { |
| ; ({ default: fs } = await import( 'node:fs')); ({ default: https } = await import( 'node:https')) |
| } |
| : async () => { } |
| |
| if (isNode) loadNodeDeps() |
|
|
| |
| const imageProms: Array<Promise<string>> = [] |
|
|
| |
| const candidateRels = layout._relsMedia.filter( |
| rel => rel.type !== 'online' && !rel.data && (!rel.path || (rel.path && !rel.path.includes('preencoded'))) |
| ) |
|
|
| |
| const unqPaths: string[] = [] |
| candidateRels.forEach(rel => { |
| if (!unqPaths.includes(rel.path)) { |
| rel.isDuplicate = false |
| unqPaths.push(rel.path) |
| } else { |
| rel.isDuplicate = true |
| } |
| }) |
|
|
| |
| candidateRels |
| .filter(rel => !rel.isDuplicate) |
| .forEach(rel => { |
| imageProms.push( |
| (async () => { |
| if (!https) await loadNodeDeps() |
|
|
| |
| if (isNode && fs && rel.path.indexOf('http') !== 0) { |
| try { |
| const bitmap = fs.readFileSync(rel.path) |
| rel.data = Buffer.from(bitmap).toString('base64') |
| candidateRels |
| .filter(dupe => dupe.isDuplicate && dupe.path === rel.path) |
| .forEach(dupe => (dupe.data = rel.data)) |
| return 'done' |
| } catch (ex) { |
| rel.data = IMG_BROKEN |
| candidateRels |
| .filter(dupe => dupe.isDuplicate && dupe.path === rel.path) |
| .forEach(dupe => (dupe.data = rel.data)) |
| throw new Error(`ERROR: Unable to read media: "${rel.path}"\n${String(ex)}`) |
| } |
| } |
|
|
| |
| if (isNode && https && rel.path.startsWith('http')) { |
| return await new Promise<string>((resolve, reject) => { |
| https.get(rel.path, res => { |
| let raw = '' |
| res.setEncoding('binary') |
| res.on('data', chunk => (raw += chunk)) |
| res.on('end', () => { |
| rel.data = Buffer.from(raw, 'binary').toString('base64') |
| candidateRels |
| .filter(dupe => dupe.isDuplicate && dupe.path === rel.path) |
| .forEach(dupe => (dupe.data = rel.data)) |
| resolve('done') |
| }) |
| res.on('error', () => { |
| rel.data = IMG_BROKEN |
| candidateRels |
| .filter(dupe => dupe.isDuplicate && dupe.path === rel.path) |
| .forEach(dupe => (dupe.data = rel.data)) |
| reject(new Error(`ERROR! Unable to load image (https.get): ${rel.path}`)) |
| }) |
| }) |
| }) |
| } |
|
|
| |
| return await new Promise<string>((resolve, reject) => { |
| |
| const xhr = new XMLHttpRequest() |
| xhr.onload = () => { |
| const reader = new FileReader() |
| reader.onloadend = () => { |
| rel.data = reader.result as string |
| candidateRels |
| .filter(dupe => dupe.isDuplicate && dupe.path === rel.path) |
| .forEach(dupe => (dupe.data = rel.data)) |
| if (!rel.isSvgPng) { |
| resolve('done') |
| } else { |
| createSvgPngPreview(rel) |
| .then(() => resolve('done')) |
| .catch(reject) |
| } |
| } |
| reader.readAsDataURL(xhr.response) |
| } |
| xhr.onerror = () => { |
| rel.data = IMG_BROKEN |
| candidateRels |
| .filter(dupe => dupe.isDuplicate && dupe.path === rel.path) |
| .forEach(dupe => (dupe.data = rel.data)) |
| reject(new Error(`ERROR! Unable to load image (xhr.onerror): ${rel.path}`)) |
| } |
| |
| xhr.open('GET', rel.path) |
| xhr.responseType = 'blob' |
| xhr.send() |
| }) |
| })(), |
| ) |
| }) |
|
|
| |
| |
| |
| layout._relsMedia |
| .filter(rel => rel.isSvgPng && rel.data) |
| .forEach(rel => { |
| (async () => { |
| if (isNode && !fs) await loadNodeDeps() |
| if (isNode && fs) { |
| |
| rel.data = IMG_BROKEN |
| imageProms.push(Promise.resolve('done')) |
| } else { |
| imageProms.push(createSvgPngPreview(rel)) |
| } |
| })() |
| }) |
|
|
| return imageProms |
| } |
|
|
| |
| |
| |
| |
| |
| async function createSvgPngPreview(rel: ISlideRelMedia): Promise<string> { |
| return await new Promise((resolve, reject) => { |
| |
| const image = new Image() |
|
|
| |
| image.onload = () => { |
| |
| if (image.width + image.height === 0) { |
| image.onerror('h/w=0') |
| } |
| let canvas: HTMLCanvasElement = document.createElement('CANVAS') as HTMLCanvasElement |
| const ctx = canvas.getContext('2d') |
| canvas.width = image.width |
| canvas.height = image.height |
| ctx.drawImage(image, 0, 0) |
| |
| |
| |
| try { |
| rel.data = canvas.toDataURL(rel.type) |
| resolve('done') |
| } catch (ex) { |
| image.onerror(ex.toString()) |
| } |
| canvas = null |
| } |
| image.onerror = () => { |
| rel.data = IMG_BROKEN |
| reject(new Error(`ERROR! Unable to load image (image.onerror): ${rel.path}`)) |
| } |
|
|
| |
| image.src = typeof rel.data === 'string' ? rel.data : IMG_BROKEN |
| }) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|