export function convertPageThumbnails(storyboard: ViewerContentStoryboard): PageThumbnail[] { const pageThumbnails: PageThumbnail[] = [] storyboard.framesets.forEach(frameSet => { // Work out how big the total image is, based on the size of the individual frames. let imageWidth: number let imageHeight: number if (storyboard.frameWidth && storyboard.frameHeight) { // The storyboard claims a frame size at the top level. All the frames are the same size and the overall image will // include empty space if there are too few frames to fill the grid. imageWidth = storyboard.frameWidth * storyboard.framesX imageHeight = storyboard.frameHeight * storyboard.framesY } else if (storyboard.framesX === 1 && frameSet.rect && frameSet.rect.length > 0) { // The frameset is a single column of images that may be of different heights. There is no empty space in the image. imageWidth = 0 imageHeight = 0 for (const rect of frameSet.rect) { imageWidth = Math.max(imageWidth, rect.w) imageHeight += rect.h } } else { // An unsupported frameset. Should never get here - nothing we can do. // Returning zeros yields no thumbnails, which is better than wrong thumbnails. imageWidth = 0 imageHeight = 0 } if (frameSet.rect) { frameSet.rect.forEach(rect => { pageThumbnails!.push({ height: rect.h, imageHeight, imageWidth, left: rect.x, top: rect.y, url: frameSet.url, width: rect.w, }) }) } }) return pageThumbnails } export function initPageThumbnailCanvas(pageThumbnail: PageThumbnail, canvas: HTMLCanvasElement): void { canvas.width = pageThumbnail.width canvas.height = pageThumbnail.height const context = canvas.getContext('2d') if (!context) { return } const image = new Image() image.onload = () => { context.drawImage(image, pageThumbnail.left, pageThumbnail.top, pageThumbnail.width, pageThumbnail.height, 0, 0, pageThumbnail.width, pageThumbnail.height) } image.src = pageThumbnail.url } export function pageThumbnailBackgroundImageStyle(pageThumbnail: PageThumbnail, isReact: false, maxWidth: number, maxHeight?: number): { [key: string]: string } export function pageThumbnailBackgroundImageStyle(pageThumbnail: PageThumbnail, isReact: true, maxWidth: number, maxHeight?: number): StoryboardCSS export function pageThumbnailBackgroundImageStyle(pageThumbnail: PageThumbnail, isReact: boolean, maxWidth: number, maxHeight?: number): StoryboardCSS | { [key: string]: string } { const backgroundImageUrl = pageThumbnail.url const aspectRatio = pageThumbnail.width / pageThumbnail.height let width: number if (maxWidth && maxHeight) { if (aspectRatio < maxWidth / maxHeight) { width = maxHeight * aspectRatio } else { width = maxWidth } } else if (maxWidth) { width = maxWidth } else if (maxHeight) { width = maxHeight * aspectRatio } else { width = pageThumbnail.width } const scale = width / pageThumbnail.width const backgroundImage = `url(${backgroundImageUrl})` const backgroundPosition = `${scale * pageThumbnail.left * -1}px ${scale * pageThumbnail.top * -1}px` const backgroundSize = `${scale * pageThumbnail.imageWidth}px` const height = scale * pageThumbnail.height if (isReact) { // We love React... return { backgroundImage, backgroundPosition, backgroundSize, height: `${height}px`, width: `${width}px`, } } // ... but we must also tolerate AngularJS return { 'background-image': backgroundImage, 'background-position': backgroundPosition, 'background-size': backgroundSize, 'height': `${height}px`, 'width': `${width}px`, } } export interface PageThumbnail { height: number imageWidth: number imageHeight: number left: number top: number url: string width: number } export interface StoryboardCSS { width: string height: string backgroundImage: string backgroundPosition: string backgroundSize: string } // Duplicated from viewer-api-typings, which we don't want to reference in this repo export interface ViewerContentStoryboard { frameHeight?: number framesets: ViewerFrameset[] framesTotal: number framesX: number framesY: number frameUrls?: string[] frameWidth?: number } interface ViewerFrameset { end: number rect?: ViewerRect[] start: number url: string } interface ViewerRect { h: number w: number x: number y: number }