UNPKG

@flexis/favicons

Version:

A tool for generating icons for the modern web.

1,060 lines (940 loc) 30.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise'); var _Reflect$deleteProperty = require('@babel/runtime-corejs3/core-js-stable/reflect/delete-property'); var _concatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/concat'); var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter'); var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries'); var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map'); var _Array$isArray = require('@babel/runtime-corejs3/core-js-stable/array/is-array'); var _Object$assign = require('@babel/runtime-corejs3/core-js-stable/object/assign'); var _awaitAsyncGenerator = require('@babel/runtime-corejs3/helpers/awaitAsyncGenerator'); var _wrapAsyncGenerator = require('@babel/runtime-corejs3/helpers/wrapAsyncGenerator'); var _asyncIterator = require('@babel/runtime-corejs3/helpers/asyncIterator'); var Vinyl = require('vinyl'); var toIco = require('to-ico'); var path = require('path'); var _sortInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/sort'); var _indexOfInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/index-of'); var _reduceInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/reduce'); var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find'); var Sharp = require('sharp'); var xmldom = require('xmldom'); var canvas = require('canvas'); var fetch = require('node-fetch'); var Canvg = require('canvg'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n['default'] = e; return Object.freeze(n); } var _Promise__default = /*#__PURE__*/_interopDefaultLegacy(_Promise); var _Reflect$deleteProperty__default = /*#__PURE__*/_interopDefaultLegacy(_Reflect$deleteProperty); var _concatInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_concatInstanceProperty); var _filterInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_filterInstanceProperty); var _Object$entries__default = /*#__PURE__*/_interopDefaultLegacy(_Object$entries); var _mapInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_mapInstanceProperty); var _Array$isArray__default = /*#__PURE__*/_interopDefaultLegacy(_Array$isArray); var _Object$assign__default = /*#__PURE__*/_interopDefaultLegacy(_Object$assign); var _awaitAsyncGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_awaitAsyncGenerator); var _wrapAsyncGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_wrapAsyncGenerator); var _asyncIterator__default = /*#__PURE__*/_interopDefaultLegacy(_asyncIterator); var Vinyl__default = /*#__PURE__*/_interopDefaultLegacy(Vinyl); var toIco__default = /*#__PURE__*/_interopDefaultLegacy(toIco); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var _sortInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_sortInstanceProperty); var _indexOfInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_indexOfInstanceProperty); var _reduceInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_reduceInstanceProperty); var _findInstanceProperty__default = /*#__PURE__*/_interopDefaultLegacy(_findInstanceProperty); var Sharp__default = /*#__PURE__*/_interopDefaultLegacy(Sharp); var canvas__namespace = /*#__PURE__*/_interopNamespace(canvas); var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch); var Canvg__default = /*#__PURE__*/_interopDefaultLegacy(Canvg); /** * Add path to file name. * @param path - Path to destination folder. * @param filename - Target file name. * @returns Full path to file. */ function applyPath(path$1, filename) { return path$1 ? path.join(path$1, filename) : filename; } /** * Get complete icon config from shirt config. * @param iconsType - Icons type name. * @param iconsConfig - Icons shirt config. * @param manifestConfig - Config to get background color. * @returns Complete config. */ function getCompleteIconConfig(iconsType, iconsConfig, { background_color: background }) { const iconConfigSource = iconsConfig[iconsType]; if (!iconConfigSource) { return null; } const { offset: iconOffsetSource, background: iconBackgroundSource } = iconConfigSource === true ? {} : iconConfigSource; const completeOffset = typeof iconOffsetSource !== 'undefined' ? iconOffsetSource : 0; let completeBackground = typeof iconBackgroundSource !== 'undefined' ? iconBackgroundSource : false; if (typeof completeBackground === 'boolean') { if (iconsTransparency[iconsType] && !completeBackground) { completeBackground = 'transparent'; } else { completeBackground = background; } } return { offset: completeOffset, background: completeBackground }; } const iconsTransparency = { favicon: true, android: true, apple: false, appleStartup: false }; const iconsToGenerate = { favicon: { 'favicon.ico': { sizes: [{ width: 16, height: 16 }, { width: 24, height: 24 }, { width: 32, height: 32 }, { width: 48, height: 48 }, { width: 64, height: 64 }] }, 'favicon-16x16.png': { width: 16, height: 16 }, 'favicon-32x32.png': { width: 32, height: 32 } }, android: { 'android-chrome-36x36.png': { width: 36, height: 36 }, 'android-chrome-48x48.png': { width: 48, height: 48 }, 'android-chrome-72x72.png': { width: 72, height: 72 }, 'android-chrome-96x96.png': { width: 96, height: 96 }, 'android-chrome-144x144.png': { width: 144, height: 144 }, 'android-chrome-192x192.png': { width: 192, height: 192 }, 'android-chrome-256x256.png': { width: 256, height: 256 }, 'android-chrome-384x384.png': { width: 384, height: 384 }, 'android-chrome-512x512.png': { width: 512, height: 512 } }, // Sources of information: // - https://stackoverflow.com/a/19933647 // - https://docs.axway.com/bundle/Titanium_SDK_allOS_en/page/icons_and_splash_screens.html // - https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/ apple: { // 'apple-touch-icon-57x57.png' - non-retina iPhone/iPod touch // 'apple-touch-icon-60x60.png' - unknown or notification icon for native apps // 'apple-touch-icon-72x72.png' - non-retina iPad // 'apple-touch-icon-76x76.png' - also non-retina iPad // 'apple-touch-icon-114x114.png' - retina iPhone with iOS < 7 // modern retina iPhone 'apple-touch-icon-120x120.png': { width: 120, height: 120 }, // 'apple-touch-icon-144x144.png' - retina iPad with iOS < 7 // modern retina iPad 'apple-touch-icon-152x152.png': { width: 152, height: 152 }, // iPad Pro 'apple-touch-icon-167x167.png': { width: 167, height: 167 }, // iPhone X, iPhone [N] Plus 'apple-touch-icon-180x180.png': { width: 180, height: 180 }, // generic? 'apple-touch-icon.png': { width: 180, height: 180 } // 'apple-touch-icon-precomposed.png' - iOS < 7 }, // Sources of information: // - https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions // - https://docs.axway.com/bundle/Titanium_SDK_allOS_en/page/icons_and_splash_screens.html // - https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/launch-screen/ // - https://appsco.pe/developer/splash-screens appleStartup: { // iPhone 5 'apple-touch-startup-image-640x1136.png': { width: 640, height: 1136, pixelRatio: 2 }, // iPhone 'apple-touch-startup-image-750x1334.png': { width: 750, height: 1334, pixelRatio: 2 }, // iPhone [N] Plus 'apple-touch-startup-image-1242x2208.png': { width: 1242, height: 2208, pixelRatio: 3, rotate: false }, 'apple-touch-startup-image-2208x1242.png': { width: 2208, height: 1242, pixelRatio: 3, rotate: true }, // iPhone X 'apple-touch-startup-image-1125x2436.png': { width: 1125, height: 2436, pixelRatio: 3, rotate: false }, 'apple-touch-startup-image-2436x1125.png': { width: 2436, height: 1125, pixelRatio: 3, rotate: true }, // iPhone Xr 'apple-touch-startup-image-828x1792.png': { width: 828, height: 1792, pixelRatio: 2, rotate: false }, 'apple-touch-startup-image-1792x828.png': { width: 1792, height: 828, pixelRatio: 2, rotate: true }, // iPhone Xs Max 'apple-touch-startup-image-1242x2688.png': { width: 1242, height: 2688, pixelRatio: 3, rotate: false }, 'apple-touch-startup-image-2688x1242.png': { width: 2688, height: 1242, pixelRatio: 3, rotate: true }, // iPad Mini, Air 'apple-touch-startup-image-1536x2048.png': { width: 1536, height: 2048, pixelRatio: 2, rotate: false }, 'apple-touch-startup-image-2048x1536.png': { width: 2048, height: 1536, pixelRatio: 2, rotate: true }, // iPad Pro 10.5" 'apple-touch-startup-image-1668x2224.png': { width: 1668, height: 2224, pixelRatio: 2, rotate: false }, 'apple-touch-startup-image-2224x1668.png': { width: 2224, height: 1668, pixelRatio: 2, rotate: true }, // iPad Pro 11" 'apple-touch-startup-image-1668x2388.png': { width: 1668, height: 2388, pixelRatio: 2, rotate: false }, 'apple-touch-startup-image-2388x1668.png': { width: 2224, height: 2388, pixelRatio: 2, rotate: true }, // iPad Pro 12.9" 'apple-touch-startup-image-2048x2732.png': { width: 2048, height: 2732, pixelRatio: 2, rotate: false }, 'apple-touch-startup-image-2732x2048.png': { width: 2732, height: 2048, pixelRatio: 2, rotate: true } } }; var htmlHeaders = { favicon: getFaviconHeaders, android: getAndroidHeaders, apple: getAppleHeaders, appleStartup: getAppleStartupHeaders }; /** * Get "favicon" headers. * @param headersConfig - Config params. * @returns Array of headers objects. */ function getFaviconHeaders({ path }) { var _context; return _mapInstanceProperty__default['default'](_context = _Object$entries__default['default'](iconsToGenerate.favicon)).call(_context, ([filename, { width, height }]) => { const header = { tagName: 'link', rel: 'icon', href: applyPath(path, filename) }; if (width && height) { header.type = 'image/png'; header.sizes = `${width}x${height}`; } return header; }); } /** * Get "android" headers. * @param headersConfig - Config params. * @returns Array of headers objects. */ function getAndroidHeaders({ path, webAppCapable = 'yes', manifest: { name: applicationName = null, theme_color: themeColor = null } = {} }) { const headers = [{ tagName: 'link', rel: 'manifest', href: applyPath(path, 'manifest.json') }, { tagName: 'meta', name: 'mobile-web-app-capable', content: webAppCapable }]; if (typeof applicationName === 'string') { headers.push({ tagName: 'meta', name: 'application-name', content: applicationName }); } if (typeof themeColor === 'string') { headers.push({ tagName: 'meta', name: 'theme-color', content: themeColor }); } return headers; } /** * Get "apple" headers. * @param headersConfig - Config params. * @returns Array of headers objects. */ function getAppleHeaders({ path, webAppCapable = 'yes', webAppStatusBarStyle = 'black-translucent', manifest: { name: applicationName = null } = {} }) { var _context2; const headers = [{ tagName: 'meta', name: 'apple-mobile-web-app-capable', content: webAppCapable }, { tagName: 'meta', name: 'apple-mobile-web-app-status-bar-style', content: webAppStatusBarStyle }]; if (typeof applicationName === 'string') { headers.push({ tagName: 'meta', name: 'apple-mobile-web-app-title', content: applicationName }); } const iconsHeaders = _mapInstanceProperty__default['default'](_context2 = _Object$entries__default['default'](iconsToGenerate.apple)).call(_context2, ([filename, { width, height }]) => ({ tagName: 'link', rel: 'apple-touch-icon', sizes: `${width}x${height}`, href: applyPath(path, filename) })); headers.push(...iconsHeaders); return headers; } /** * Calculate media query for apple startup image. * @param iconToGenerateConfig - Config with icon info. * @returns Media query for device. */ function getAppleStartupMediaQuery({ width, height, pixelRatio: maybePixelRatio, rotate }) { const pixelRatio = typeof maybePixelRatio === 'number' ? maybePixelRatio : 1; const screenWidth = width / pixelRatio; const screenHeight = height / pixelRatio; const deviceWidth = rotate ? screenHeight : screenWidth; const deviceHeight = rotate ? screenWidth : screenHeight; let query = `(device-width: ${deviceWidth}px) and (device-height: ${deviceHeight}px)`; if (typeof rotate === 'boolean') { query += ` and (orientation: ${rotate ? 'landscape' : 'portrait'})`; } if (pixelRatio > 1) { query += ` and (-webkit-device-pixel-ratio: ${pixelRatio})`; } return query; } /** * Get "apple startup" headers. * @param headersConfig - Config params. * @returns Array of headers objects. */ function getAppleStartupHeaders({ path }) { var _context3; return _mapInstanceProperty__default['default'](_context3 = _Object$entries__default['default'](iconsToGenerate.appleStartup)).call(_context3, ([filename, config]) => ({ tagName: 'link', rel: 'apple-touch-startup-image', media: getAppleStartupMediaQuery(config), href: applyPath(path, filename) })); } const extensions = { png: /^png$/, svg: /^svg$/ }; /** * Check image type * @param type - Image extension without dot. * @returns Image type is supported or not. */ function isSupportedType(type) { return extensions.hasOwnProperty(type); } /** * Check is "ico" or not. * @param filename - File name to check. * @returns Result of checking. */ function isIco(filename) { return path__default['default'].extname(filename) === '.ico'; } /** * Check is "svg" or not. * @param filename - File name to check. * @returns Result of checking. */ function isSvg(filename) { return path__default['default'].extname(filename) === '.svg'; } const icons = { favicon: true, android: true, apple: true, appleStartup: true }; class FaviconsGenerator { constructor({ path, manifest, icons: icons$1 } = {}) { this.config = {}; _Object$assign__default['default'](this.config, { path, manifest: { background_color: '#fff', ...manifest }, icons: icons$1 || icons }); } /** * Create favicons form sources. * @param sources - Favicons sources. * @returns Results of handling. */ generateIcons(source) { var _this = this; return _wrapAsyncGenerator__default['default'](function* () { const sources = _Array$isArray__default['default'](source) ? source : [source]; if (!sources.length) { throw new Error('No sources provided.'); } for (const source of sources) { if (!Vinyl__default['default'].isVinyl(source) || source.isNull() || source.isStream()) { throw new Error('Invalid source.'); } const sourceType = source.extname.replace(/^\./, ''); if (!isSupportedType(sourceType)) { throw new Error(`"${sourceType}" is not supported.`); } yield _awaitAsyncGenerator__default['default'](_this.attachMetadata(source)); } const { icons } = _this.config; for (const iconsType in icons) { const iconsOfType = _this.generateIconsOfType(iconsType, sources); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError; try { for (var _iterator = _asyncIterator__default['default'](iconsOfType), _step, _value; _step = yield _awaitAsyncGenerator__default['default'](_iterator.next()), _iteratorNormalCompletion = _step.done, _value = yield _awaitAsyncGenerator__default['default'](_step.value), !_iteratorNormalCompletion; _iteratorNormalCompletion = true) { const icon = _value; yield icon; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { yield _awaitAsyncGenerator__default['default'](_iterator.return()); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } })(); } /** * Create full manifest object with icons. * @returns Manifest object. */ generateManifset() { const { path, manifest: manifestConfig, icons: { android } } = this.config; const manifest = { ...manifestConfig }; if (android) { var _context; manifest.icons = _mapInstanceProperty__default['default'](_context = _Object$entries__default['default'](iconsToGenerate.android)).call(_context, ([filename, { width, height }]) => ({ src: applyPath(path, filename), sizes: `${width}x${height}`, type: 'image/png' })); } return manifest; } /** * Create HTML-headers for target icons. * @param headersConfig - Custom headers config. * @returns Array of headers objects. You can get HTML-markup with `getHtmlHeadersMarkup` helper. */ generateHtmlHeaders(headersConfig = {}) { var _context2, _context3, _context4; const { icons, ...config } = { ...this.config, ...headersConfig, manifest: { ...this.config.manifest, ...headersConfig.manifest } }; const headers = _mapInstanceProperty__default['default'](_context2 = _filterInstanceProperty__default['default'](_context3 = _Object$entries__default['default'](icons)).call(_context3, ([, value]) => value)).call(_context2, ([type]) => htmlHeaders[type](config)); return _concatInstanceProperty__default['default'](_context4 = []).call(_context4, ...headers); } /** * Create icons of given type. * @param iconsType - Type of needed icons. * @param sources - Icons sources. * @returns Icons of given type. */ generateIconsOfType(iconsType, sources) { var _this2 = this; return _wrapAsyncGenerator__default['default'](function* () { const { manifest, icons } = _this2.config; const iconConfig = getCompleteIconConfig(iconsType, icons, manifest); if (iconConfig === null) { return; } const iconsOfTypeToGenerate = iconsToGenerate[iconsType]; for (const filename in iconsOfTypeToGenerate) { yield _this2.generateIcon(filename, sources, iconConfig, iconsOfTypeToGenerate[filename]); } })(); } /** * Create icon. * @param filename - File name of icon. * @param sources - Sources of icon. * @param iconConfig - Icon config. * @param iconToGenerateConfig - Icon to generate config. */ async generateIcon(filename, sources, iconConfig, iconToGenerateConfig) { if (isIco(filename)) { return this.generateIco(filename, sources, iconConfig, iconToGenerateConfig); } const target = sources[0].clone({ contents: false }); const { offset, background } = iconConfig; const { width, height } = iconToGenerateConfig; const icon = await this.renderIcon(sources, { width, height, background: background, offset }); target.basename = filename; target.contents = icon; _Reflect$deleteProperty__default['default'](target, 'metadata'); return target; } /** * Create ".ico" icon. * @param filename - File name of icon. * @param sources - Sources of icon. * @param iconConfig - Icon config. * @param iconToGenerateConfig - Icon to generate config. */ async generateIco(filename, sources, iconConfig, iconToGenerateConfig) { var _context5; const target = sources[0].clone({ contents: false }); const icons = await _Promise__default['default'].all(_mapInstanceProperty__default['default'](_context5 = iconToGenerateConfig.sizes).call(_context5, ({ width, height }) => this.generateIcon(`${width}x${height}.png`, sources, iconConfig, { ...iconToGenerateConfig, width, height }))); const ico = await toIco__default['default'](_mapInstanceProperty__default['default'](icons).call(icons, ({ contents }) => contents)); target.basename = filename; target.contents = ico; _Reflect$deleteProperty__default['default'](target, 'metadata'); return target; } } const attributeNamesOrder = ['name', 'content', 'rel', 'type', 'sizes', 'media', 'href']; /** * Get order index of attribute name. * @param attributeName - Target attribute name. * @returns Index. */ function getAttributeOrder(attributeName) { const index = _indexOfInstanceProperty__default['default'](attributeNamesOrder).call(attributeNamesOrder, attributeName); return index === -1 ? attributeNamesOrder.length : index; } /** * Comparator function to sort attributes entries. * @param a - Left attribute entry. * @param b - Right attribute entry. * @returns Result of comparation. */ function comparator([a], [b]) { return getAttributeOrder(a) - getAttributeOrder(b); } /** * Make attribute string from entry. * @param entry - Entry with attribute name and value. * @returns Attribute string. */ function entryToString([key, value]) { return `${key}="${String(value)}"`; } /** * Get HTML-markup from objects. * @param header - Object or array of objects with header info. * @returns HTML-markup. */ function getHtmlHeadersMarkup(header) { var _context, _context2; if (_Array$isArray__default['default'](header)) { return _mapInstanceProperty__default['default'](header).call(header, getHtmlHeadersMarkup).join('\n'); } const { tagName, ...attributes } = header; const attributesString = _mapInstanceProperty__default['default'](_context = _sortInstanceProperty__default['default'](_context2 = _Object$entries__default['default'](attributes)).call(_context2, comparator)).call(_context, entryToString).join(' '); const markup = `<${tagName} ${attributesString}>`; return markup; } const nodeCanvgPreset = Canvg.presets.node({ DOMParser: xmldom.DOMParser, canvas: canvas__namespace, fetch: fetch__default['default'] }); /** * Attach image metadata to the vinyl file. * @param source - Image file. * @returns Source image file with attached metadata. */ async function attachMetadata(source) { if (typeof source.metadata === 'object') { return source; } if (isIco(source.basename)) { source.metadata = { format: 'ico', width: 16, height: 16 }; } else { source.metadata = await Sharp__default['default'](source.contents).metadata(); } return source; } /** * Render icon. * @param sources - Array of sources. * @param renderConfig - Render config. * @returns Rendered icon. */ async function renderIcon(sources, { width, height, background, offset }) { const maximumSide = Math.max(width, height); const offsetPx = Math.round(maximumSide / 100 * offset) || 0; const canvas = await createCanvas(width, height, background); const sprite = await createSprite(sources, width, height, offsetPx); const renderedSprite = await sprite.png().toBuffer(); canvas.composite([{ input: renderedSprite }]); return canvas.png().toBuffer(); } /** * Create canvas for rendering. * @param width - Width of canvas. * @param height - Height of canvas. * @param background - Background color of canvas. * @returns Canvas. */ function createCanvas(width, height, background) { return Sharp__default['default']({ create: { width, height, background, channels: 4 } }); } /** * Create sprite. * @param sources - Sprite sources. * @param width - Width of sprite. * @param height - Height of sprite. * @param offset - Offset from canvas edges. * @returns Sprite. */ async function createSprite(sources, width, height, offset) { const spriteWidth = width - offset * 2; const spriteHeight = height - offset * 2; const source = await getSuitableSourceBuffer(sources, spriteWidth, spriteHeight); const sprite = Sharp__default['default'](source); sprite.resize(spriteWidth, spriteHeight, { fit: 'inside' }); return sprite; } /** * Get suitable source by size. * @param sources - Array of sources. * @param width - Needed width. * @param height - Needed height. * @returns Siutable source. */ async function getSuitableSourceBuffer(sources, width, height) { const svgSource = _findInstanceProperty__default['default'](sources).call(sources, ({ basename }) => isSvg(basename)); if (svgSource) { await attachMetadata(svgSource); const svg = svgSource.contents.toString('utf8'); const { width: desiredWidth, height: desiredHeight } = getContainSize({ width, height }, svgSource.metadata); const c = nodeCanvgPreset.createCanvas(0, 0); const ctx = c.getContext('2d'); const v = Canvg__default['default'].fromString(ctx, svg, nodeCanvgPreset); v.resize(desiredWidth, desiredHeight, 'xMidYMid meet'); await v.render(); return c.toBuffer(); } for (const source of sources) { await attachMetadata(source); } const maximumSide = Math.max(width, height); const nearestIcon = _reduceInstanceProperty__default['default'](sources).call(sources, (nearestIcon, source) => { const { width: nearestIconWidth, height: nearestIconHeight } = nearestIcon.metadata; const { width: sourceWidth, height: sourceHeight } = source.metadata; const nearestIconMaximumSide = Math.max(nearestIconWidth, nearestIconHeight); const sourceMaximumSide = Math.max(sourceWidth, sourceHeight); if ((nearestIconMaximumSide > sourceMaximumSide || nearestIconMaximumSide < maximumSide) && sourceMaximumSide >= maximumSide) { return source; } return nearestIcon; }, sources[0]); return nearestIcon.contents; } /** * Get size of icon to contain in box. * @param boxSize - Size of box. * @param iconsSize - Size of icon. */ function getContainSize({ width: boxWidth, height: boxHeight }, { width: iconWidth, height: iconHeight }) { const minBoxSide = Math.min(boxWidth, boxHeight); const isWidthMin = minBoxSide === boxWidth; return isWidthMin ? { width: boxWidth, height: Math.round(iconHeight / iconWidth * boxWidth) } : { height: boxHeight, width: Math.round(iconWidth / iconHeight * boxHeight) }; } class FaviconsGenerator$1 extends FaviconsGenerator { constructor() { super(...arguments); this.attachMetadata = attachMetadata; this.renderIcon = renderIcon; } } exports.FaviconsGenerator = FaviconsGenerator; exports.default = FaviconsGenerator$1; exports.extensions = extensions; exports.getHtmlHeadersMarkup = getHtmlHeadersMarkup; exports.isIco = isIco; exports.isSupportedType = isSupportedType; exports.isSvg = isSvg; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=