1/* 2 * image.ts 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 16import { kPixelUnit, kPercentUnit } from './css'; 17 18const kDpi = 96; 19 20// https://github.com/jgm/pandoc/blob/master/src/Text/Pandoc/ImageSize.hs 21export const kValidUnits = [kPixelUnit, 'in', 'cm', 'mm', kPercentUnit]; 22 23export enum ImageType { 24 Image, 25 Figure, 26} 27 28export interface ImageDimensions { 29 naturalWidth: number | null; 30 naturalHeight: number | null; 31 containerWidth: number; 32} 33 34export function isValidImageSizeUnit(unit: string) { 35 return kValidUnits.includes(unit); 36} 37 38export function imageSizePropWithUnit(prop: string | null) { 39 if (prop) { 40 const match = prop.match(/(^\d*\.?\d*)(.*)$/); 41 if (match) { 42 return { 43 size: parseFloat(match[1]), 44 unit: match[2], 45 }; 46 } else { 47 return null; 48 } 49 } else { 50 return null; 51 } 52} 53 54export function isNaturalAspectRatio(width: number, height: number, dims: ImageDimensions, defaultValue: boolean) { 55 if (dims.naturalWidth && dims.naturalHeight) { 56 const diff = Math.abs(width / height - dims.naturalWidth / dims.naturalHeight); 57 return diff <= 0.01 * (width / height); 58 } else { 59 // no naturalWidth or naturalHeight, return default 60 return defaultValue; 61 } 62} 63 64export function unitToPixels(value: number, unit: string, containerWidth: number) { 65 let pixels; 66 switch (unit) { 67 case 'in': 68 pixels = value * kDpi; 69 break; 70 case 'mm': 71 pixels = value * (kDpi / 25.4); 72 break; 73 case 'cm': 74 pixels = value * (kDpi / 2.54); 75 break; 76 case kPercentUnit: 77 pixels = (value / 100) * ensureContainerWidth(containerWidth); 78 break; 79 case kPixelUnit: 80 default: 81 pixels = value; 82 break; 83 } 84 return Math.round(pixels); 85} 86 87export function pixelsToUnit(pixels: number, unit: string, containerWidth: number) { 88 switch (unit) { 89 case 'in': 90 return pixels / kDpi; 91 case 'mm': 92 return (pixels / kDpi) * 25.4; 93 case 'cm': 94 return (pixels / kDpi) * 2.54; 95 case kPercentUnit: 96 return (pixels / ensureContainerWidth(containerWidth)) * 100; 97 case kPixelUnit: 98 default: 99 return pixels; 100 } 101} 102 103export function roundUnit(value: number, unit: string) { 104 switch (unit) { 105 case 'in': 106 return value.toFixed(2); 107 case 'cm': 108 return value.toFixed(1); 109 default: 110 return Math.round(value).toString(); 111 } 112} 113 114// sometime when we are called before the DOM renders the containerWidth 115// is 0, in this case provide a default of 1000 116export function ensureContainerWidth(containerWidth: number) { 117 return containerWidth || 1000; 118} 119