71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
import { DOCS_GRAPHIC_DEFAULTS } from "./docs-graphic-types.ts"
|
|
import { GRAPHIC_KEYBOARD_MIN_SIZE } from "./docs-graphic-keyboard.ts"
|
|
|
|
/** Max frame width when inserting large photos into the page body. */
|
|
export const DOCS_IMAGE_INSERT_MAX_WIDTH_PX = 560
|
|
|
|
/** Max frame height when inserting very tall images. */
|
|
export const DOCS_IMAGE_INSERT_MAX_HEIGHT_PX = 720
|
|
|
|
export type ImageNaturalSize = {
|
|
width: number
|
|
height: number
|
|
}
|
|
|
|
/** Frame size that preserves the source aspect ratio, scaled down when needed. */
|
|
export function computeImageInsertFrameSize(
|
|
naturalWidth: number,
|
|
naturalHeight: number,
|
|
options: {
|
|
maxWidth?: number
|
|
maxHeight?: number
|
|
minSize?: number
|
|
} = {}
|
|
): ImageNaturalSize {
|
|
const maxWidth = options.maxWidth ?? DOCS_IMAGE_INSERT_MAX_WIDTH_PX
|
|
const maxHeight = options.maxHeight ?? DOCS_IMAGE_INSERT_MAX_HEIGHT_PX
|
|
const minSize = options.minSize ?? GRAPHIC_KEYBOARD_MIN_SIZE
|
|
|
|
if (naturalWidth <= 0 || naturalHeight <= 0) {
|
|
return {
|
|
width: DOCS_GRAPHIC_DEFAULTS.width,
|
|
height: DOCS_GRAPHIC_DEFAULTS.height,
|
|
}
|
|
}
|
|
|
|
const scale = Math.min(1, maxWidth / naturalWidth, maxHeight / naturalHeight)
|
|
let width = Math.max(minSize, Math.round(naturalWidth * scale))
|
|
let height = Math.max(minSize, Math.round(naturalHeight * scale))
|
|
|
|
// Keep ratio after integer rounding (derive height from width).
|
|
height = Math.max(minSize, Math.round(width * (naturalHeight / naturalWidth)))
|
|
if (height > maxHeight) {
|
|
height = maxHeight
|
|
width = Math.max(minSize, Math.round(height * (naturalWidth / naturalHeight)))
|
|
}
|
|
|
|
return { width, height }
|
|
}
|
|
|
|
/** Load intrinsic pixel dimensions for a data URL or remote image src. */
|
|
export function readImageNaturalSize(src: string): Promise<ImageNaturalSize> {
|
|
if (typeof Image === "undefined") {
|
|
return Promise.resolve({
|
|
width: DOCS_GRAPHIC_DEFAULTS.width,
|
|
height: DOCS_GRAPHIC_DEFAULTS.height,
|
|
})
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image()
|
|
img.onload = () => {
|
|
resolve({
|
|
width: img.naturalWidth,
|
|
height: img.naturalHeight,
|
|
})
|
|
}
|
|
img.onerror = () => reject(new Error("Failed to read image dimensions"))
|
|
img.src = src
|
|
})
|
|
}
|