@sakitam-gis/sprite-cli 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/QUICK_START.md +34 -80
- package/README.md +1 -13
- package/dist/cli.js +157 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +158 -9
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sprite.ts","../src/core/layout.ts","../src/core/shelf-pack.ts","../src/core/metadata.ts","../src/core/image.ts","../src/utils.ts"],"sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\nimport stringify from 'json-stable-stringify';\nimport { generateLayout, generateLayoutUnique, generateImage } from './core/index.js';\nimport { getFilePaths, loadFile, sortById } from './utils.js';\nimport type { ImageBuffer, Layout } from './types.js';\n\n/**\n * 雪碧图生成选项\n */\nexport interface SpriteOptions {\n /** 输出文件路径(不包含扩展名) */\n output: string;\n /** 输入目录路径 */\n input: string;\n /** 像素比率 (默认: 1) */\n ratio?: number;\n /** 是否使用 unique 模式 (默认: false) */\n unique?: boolean;\n /** 是否格式化 JSON 输出 (默认: true) */\n format?: boolean;\n}\n\n/**\n * 雪碧图生成结果\n */\nexport interface SpriteResult {\n /** JSON 文件路径 */\n jsonPath: string;\n /** PNG 文件路径 */\n pngPath: string;\n /** 布局对象 */\n layout: Layout;\n /** 生成的图片数量 */\n imageCount: number;\n}\n\n/**\n * 确保目录存在\n * @param filePath 文件路径\n */\nasync function ensureDir(filePath: string): Promise<void> {\n const dir = path.dirname(filePath);\n try {\n await fs.access(dir);\n } catch {\n await fs.mkdir(dir, { recursive: true });\n }\n}\n\n/**\n * 保存布局 JSON 文件\n * @param outfile 输出文件路径\n * @param layout 布局对象\n * @param format 是否格式化\n */\nasync function saveLayout(outfile: string, layout: Layout, format = true): Promise<void> {\n const jsonPath = `${outfile}.json`;\n await ensureDir(jsonPath);\n const formattedLayout = format\n ? stringify(layout, { space: ' ' }) || '{}'\n : JSON.stringify(layout);\n await fs.writeFile(jsonPath, formattedLayout, 'utf8');\n}\n\n/**\n * 保存雪碧图图片\n * @param outfile 输出文件路径\n * @param layout 布局对象\n */\nasync function saveImage(outfile: string, layout: Layout): Promise<void> {\n const pngPath = `${outfile}.png`;\n await ensureDir(pngPath);\n const image = await generateImage(layout);\n await fs.writeFile(pngPath, image);\n}\n\n/**\n * 生成布局\n * @param buffers 图片缓冲区数组\n * @param ratio 像素比率\n * @param format 是否格式化\n * @param unique 是否使用 unique 模式\n * @returns 布局对象\n */\nasync function createLayout(\n buffers: ImageBuffer[],\n ratio: number,\n format: boolean,\n unique: boolean,\n): Promise<Layout> {\n const genLayout = unique ? generateLayoutUnique : generateLayout;\n return await genLayout({ imgs: buffers, pixelRatio: ratio, format });\n}\n\n/**\n * 生成雪碧图\n *\n * @param options 生成选项\n * @returns Promise<SpriteResult> 生成结果\n *\n * @example\n * ```typescript\n * import { generateSprite } from '@sakitam-gis/sprite-cli';\n *\n * // 生成 1x 雪碧图\n * const result = await generateSprite({\n * output: 'output/sprite',\n * input: 'icons/',\n * });\n *\n * // 生成 2x 雪碧图\n * const result2x = await generateSprite({\n * output: 'output/sprite@2x',\n * input: 'icons/',\n * ratio: 2,\n * });\n *\n * // 使用 unique 模式\n * const resultUnique = await generateSprite({\n * output: 'output/sprite-unique',\n * input: 'icons/',\n * unique: true,\n * });\n * ```\n */\nexport async function generateSprite(options: SpriteOptions): Promise<SpriteResult> {\n const { output, input, ratio = 1, unique = false, format = true } = options;\n\n // 规范化输出路径\n let outfile = output;\n if (outfile.endsWith('/')) {\n const defaultName = ratio === 1 ? 'sprite' : `sprite@${ratio}x`;\n outfile = outfile + defaultName;\n }\n\n // 加载所有 SVG/PNG 文件\n const filePaths = await getFilePaths(input);\n if (filePaths.length === 0) {\n throw new Error(`No SVG or PNG files found in directory: ${input}`);\n }\n\n const buffers = await Promise.all(filePaths.map((file) => loadFile(file)));\n\n // 排序\n buffers.sort(sortById);\n\n // 生成并保存布局和图片\n const layoutForJson = await createLayout(buffers, ratio, format, unique);\n const layoutForImage = await createLayout(buffers, ratio, false, unique);\n\n await Promise.all([\n saveLayout(outfile, layoutForJson, format),\n saveImage(outfile, layoutForImage),\n ]);\n\n return {\n jsonPath: `${outfile}.json`,\n pngPath: `${outfile}.png`,\n layout: layoutForJson,\n imageCount: buffers.length,\n };\n}\n\n/**\n * 批量生成多个比例的雪碧图\n *\n * @param input 输入目录\n * @param output 输出文件路径(不包含扩展名)\n * @param ratios 像素比率数组 (默认: [1, 2])\n * @param unique 是否使用 unique 模式 (默认: false)\n * @returns Promise<SpriteResult[]> 生成结果数组\n *\n * @example\n * ```typescript\n * import { generateSpriteMultiple } from '@sakitam-gis/sprite-cli';\n *\n * // 生成 1x 和 2x 两个版本\n * const results = await generateSpriteMultiple('icons/', 'output/sprite');\n *\n * // 生成 1x、2x、3x 三个版本\n * const results2 = await generateSpriteMultiple('icons/', 'output/sprite', [1, 2, 3]);\n * ```\n */\nexport async function generateSpriteMultiple(\n input: string,\n output: string,\n ratios: number[] = [1, 2],\n unique = false,\n): Promise<SpriteResult[]> {\n const results = await Promise.all(\n ratios.map((ratio) => {\n const outputPath = ratio === 1 ? output : `${output}@${ratio}x`;\n return generateSprite({\n output: outputPath,\n input,\n ratio,\n unique,\n });\n }),\n );\n\n return results;\n}\n\n/**\n * 从已加载的图片缓冲区生成雪碧图\n * 适用于需要自定义图片加载逻辑的场景\n *\n * @param buffers 图片缓冲区数组\n * @param output 输出文件路径(不包含扩展名)\n * @param options 额外选项\n * @returns Promise<SpriteResult> 生成结果\n *\n * @example\n * ```typescript\n * import { generateSpriteFromBuffers } from '@sakitam-gis/sprite-cli';\n * import fs from 'fs/promises';\n *\n * const buffers = [\n * { id: 'icon1', svg: await fs.readFile('icon1.svg') },\n * { id: 'icon2', svg: await fs.readFile('icon2.svg') },\n * ];\n *\n * const result = await generateSpriteFromBuffers(buffers, 'output/sprite');\n * ```\n */\nexport async function generateSpriteFromBuffers(\n buffers: ImageBuffer[],\n output: string,\n options: { ratio?: number; unique?: boolean; format?: boolean } = {},\n): Promise<SpriteResult> {\n const { ratio = 1, unique = false, format = true } = options;\n\n if (buffers.length === 0) {\n throw new Error('No image buffers provided');\n }\n\n // 排序\n buffers.sort(sortById);\n\n // 规范化输出路径\n let outfile = output;\n if (outfile.endsWith('/')) {\n const defaultName = ratio === 1 ? 'sprite' : `sprite@${ratio}x`;\n outfile = outfile + defaultName;\n }\n\n // 生成并保存布局和图片\n const layoutForJson = await createLayout(buffers, ratio, format, unique);\n const layoutForImage = await createLayout(buffers, ratio, false, unique);\n\n await Promise.all([\n saveLayout(outfile, layoutForJson, format),\n saveImage(outfile, layoutForImage),\n ]);\n\n return {\n jsonPath: `${outfile}.json`,\n pngPath: `${outfile}.png`,\n layout: layoutForJson,\n imageCount: buffers.length,\n };\n}\n","/**\n * Sprite layout generation\n * Migrated from @mapbox/spritezero with TypeScript enhancements\n */\n\nimport { DOMParser } from '@xmldom/xmldom';\nimport { loadImage } from '@napi-rs/canvas';\nimport ShelfPack from './shelf-pack.js';\nimport { extractMetadata, hasMetadata } from './metadata.js';\nimport type { ImageBuffer, Layout, LayoutOptions } from '../types.js';\n\n/**\n * Pack rectangles using shelf packing algorithm\n */\ninterface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\ninterface PackedImage extends Rect {\n id: string;\n}\n\n/**\n * 稳定排序比较器:按高度降序,相同高度按 ID 字母序\n * 确保输出的可预测性和稳定性\n *\n * @param a - 第一个图像\n * @param b - 第二个图像\n * @returns 排序结果\n */\nfunction heightAndIdComparator(\n a: { id: string; height: number },\n b: { id: string; height: number },\n): number {\n // 首先按高度降序排序\n const heightDiff = b.height - a.height;\n if (heightDiff !== 0) {\n return heightDiff;\n }\n\n // 高度相同时,按 ID 字母序排序(确保稳定性)\n return a.id.localeCompare(b.id);\n}\n\n/**\n * Shelf Best Height Fit 打包算法\n *\n * 使用 ShelfPack 算法进行 2D 矩形打包:\n * 1. 首先对图像进行排序(高度降序 + ID字母序)\n * 2. 使用 Shelf Best Height Fit 算法寻找最佳位置\n * 3. 自动调整 sprite 尺寸以适应所有图像\n * 4. 最后缩小到实际使用的尺寸\n *\n * 空间利用率:80-95%(比简单货架算法提升 10-20%)\n *\n * 算法特点:\n * - 重用空闲区域(当 bin 被释放后)\n * - 最小化浪费空间(选择最合适的货架)\n * - 支持自动扩展\n *\n * @param images - 带有尺寸信息的图像数组\n * @returns 打包结果,包含每个图像的位置和雪碧图总尺寸\n *\n * @example\n * ```ts\n * const result = packImages([\n * { id: 'icon-a', width: 24, height: 24 },\n * { id: 'icon-b', width: 18, height: 18 }\n * ]);\n * // result.packed = [\n * // { id: 'icon-a', x: 0, y: 0, w: 24, h: 24 },\n * // { id: 'icon-b', x: 24, y: 0, w: 18, h: 18 }\n * // ]\n * // result.width = 42, result.height = 24\n * ```\n */\nfunction packImages(images: Array<{ id: string; width: number; height: number }>): {\n packed: PackedImage[];\n width: number;\n height: number;\n} {\n // 使用稳定排序:高度降序 + ID 字母序\n // 这确保大图标优先放置,减少空间浪费\n const sorted = [...images].sort(heightAndIdComparator);\n\n // 创建 ShelfPack 实例,初始尺寸 1x1,自动调整大小\n const sprite = new ShelfPack(1, 1, { autoResize: true });\n\n // 批量打包,使用 inPlace 模式直接修改对象\n sprite.pack(sorted, { inPlace: true });\n\n // 转换为我们的 PackedImage 格式\n const packed: PackedImage[] = sorted.map((img: any) => ({\n id: img.id,\n x: img.x,\n y: img.y,\n w: img.width,\n h: img.height,\n }));\n\n return {\n packed,\n width: sprite.w,\n height: sprite.h,\n };\n}\n\n/**\n * SVG 尺寸解析缓存\n * 键: SVG Buffer 的 base64 字符串\n * 值: 原始尺寸 (未缩放)\n *\n * 优化:避免重复解析相同的 SVG\n * 适用场景:unique 模式或多个像素比率生成\n */\nconst svgDimensionsCache = new Map<string, { width: number; height: number }>();\n\n/**\n * Calculate image dimensions from SVG or PNG\n *\n * 性能优化:\n * 1. SVG 解析结果缓存(避免重复解析)\n * 2. 早期返回(PNG 已知尺寸)\n * 3. 延迟 canvas 导入(仅在需要时加载)\n *\n * @param img - Image buffer\n * @param pixelRatio - Pixel ratio for scaling\n * @param useCache - 是否使用缓存 (默认 true)\n * @returns Image dimensions\n */\nasync function getImageDimensions(\n img: ImageBuffer,\n pixelRatio: number,\n useCache: boolean = true,\n): Promise<{ width: number; height: number }> {\n // If dimensions are already provided (PNG), use them\n // 快速路径:PNG 已知尺寸,直接返回\n if (img.width && img.height) {\n return {\n width: Math.ceil(img.width * pixelRatio),\n height: Math.ceil(img.height * pixelRatio),\n };\n }\n\n // For PNG, extract dimensions from buffer\n if (img.png) {\n return new Promise((resolve, reject) => {\n loadImage(img.png!)\n .then((image) => {\n resolve({\n width: Math.ceil(image.width * pixelRatio),\n height: Math.ceil(image.height * pixelRatio),\n });\n })\n .catch(reject);\n });\n }\n\n // For SVG, parse dimensions from XML\n if (img.svg) {\n // 尝试从缓存获取\n let cachedDims: { width: number; height: number } | undefined;\n let cacheKey: string | undefined;\n\n if (useCache) {\n cacheKey = img.svg.toString('base64');\n cachedDims = svgDimensionsCache.get(cacheKey);\n\n if (cachedDims) {\n // 缓存命中,直接返回缩放后的尺寸\n return {\n width: Math.ceil(cachedDims.width * pixelRatio),\n height: Math.ceil(cachedDims.height * pixelRatio),\n };\n }\n }\n\n try {\n const svgString = img.svg.toString('utf8');\n const doc = new DOMParser().parseFromString(svgString, 'image/svg+xml');\n const svgElement = doc.documentElement;\n\n // 检查解析错误\n const parseError = doc.getElementsByTagName('parsererror');\n if (parseError && parseError.length > 0) {\n throw new Error(`Failed to parse SVG for ${img.id}: Invalid XML`);\n }\n\n let width = 0;\n let height = 0;\n\n // Try to get width/height attributes\n const widthAttr = svgElement.getAttribute('width');\n const heightAttr = svgElement.getAttribute('height');\n\n if (widthAttr && heightAttr) {\n width = parseFloat(widthAttr);\n height = parseFloat(heightAttr);\n } else {\n // Try viewBox\n const viewBox = svgElement.getAttribute('viewBox');\n if (viewBox) {\n const parts = viewBox.split(/\\s+/);\n if (parts.length === 4) {\n width = parseFloat(parts[2]);\n height = parseFloat(parts[3]);\n }\n }\n }\n\n // Default to 24x24 if no dimensions found\n if (!width || !height) {\n console.warn(`No dimensions found for ${img.id}, using default 24x24`);\n width = 24;\n height = 24;\n }\n\n // 验证尺寸有效性\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {\n throw new Error(`Invalid dimensions for ${img.id}: ${width}x${height}`);\n }\n\n // 缓存原始尺寸(未缩放)\n if (useCache && cacheKey) {\n svgDimensionsCache.set(cacheKey, { width, height });\n }\n\n return {\n width: Math.ceil(width * pixelRatio),\n height: Math.ceil(height * pixelRatio),\n };\n } catch (err) {\n throw new Error(`Failed to process SVG ${img.id}: ${err}`);\n }\n }\n\n // Fallback\n return {\n width: Math.ceil(24 * pixelRatio),\n height: Math.ceil(24 * pixelRatio),\n };\n}\n\n/**\n * 清除 SVG 尺寸缓存\n * 用于测试或内存管理\n */\nexport function clearDimensionsCache(): void {\n svgDimensionsCache.clear();\n}\n\n/**\n * Generate sprite layout\n * @param options - Layout options\n * @returns Layout object\n */\nexport async function generateLayout(options: LayoutOptions): Promise<Layout> {\n const {\n imgs,\n pixelRatio = 1,\n format = false,\n extractMetadata: shouldExtractMetadata = true,\n } = options;\n\n // Calculate dimensions for all images\n const imagesWithDims = await Promise.all(\n imgs.map(async (img) => ({\n id: img.id,\n svg: img.svg,\n png: img.png,\n ...(await getImageDimensions(img, pixelRatio)),\n })),\n );\n\n // Pack images\n const { packed } = packImages(imagesWithDims);\n\n // Build layout object\n const layout: Layout = {};\n\n for (const item of packed) {\n const originalImg = imgs.find((i) => i.id === item.id);\n\n if (format) {\n // Format for JSON output\n layout[item.id] = {\n width: item.w,\n height: item.h,\n x: item.x,\n y: item.y,\n pixelRatio,\n };\n\n // 提取元数据(仅SVG且包含mapbox标记)\n if (shouldExtractMetadata && originalImg && originalImg.svg && hasMetadata(originalImg.svg)) {\n try {\n const metadata = await extractMetadata(originalImg.svg, pixelRatio);\n // 合并元数据到布局项\n Object.assign(layout[item.id], metadata);\n } catch (err) {\n console.warn(`Failed to extract metadata for ${item.id}: ${err}`);\n }\n }\n } else {\n // Format for image generation (includes image data)\n const img = imagesWithDims.find((i) => i.id === item.id);\n if (img) {\n layout[item.id] = {\n width: item.w,\n height: item.h,\n x: item.x,\n y: item.y,\n pixelRatio,\n svg: img.svg,\n png: img.png,\n } as any;\n }\n }\n }\n\n return layout;\n}\n\n/**\n * Generate unique sprite layout (deduplicates identical images)\n * @param options - Layout options\n * @returns Layout object\n */\nexport async function generateLayoutUnique(options: LayoutOptions): Promise<Layout> {\n const { imgs, pixelRatio = 1, format = false } = options;\n\n // Group identical images (SVG or PNG)\n const imgMap = new Map<string, string[]>();\n\n for (const img of imgs) {\n const imgString = (img.svg || img.png)?.toString('base64');\n if (!imgString) continue;\n const existing = imgMap.get(imgString);\n if (existing) {\n existing.push(img.id);\n } else {\n imgMap.set(imgString, [img.id]);\n }\n }\n\n // Create unique images list\n const uniqueImgs: ImageBuffer[] = [];\n const idMapping = new Map<string, string>();\n\n for (const [imgString, ids] of imgMap.entries()) {\n const primaryId = ids[0];\n const buffer = Buffer.from(imgString, 'base64');\n const originalImg = imgs.find((i) => i.id === primaryId)!;\n\n if (originalImg.svg) {\n uniqueImgs.push({ id: primaryId, svg: buffer });\n } else {\n uniqueImgs.push({ id: primaryId, png: buffer });\n }\n\n // Map all IDs to the primary ID\n for (const id of ids) {\n idMapping.set(id, primaryId);\n }\n }\n\n // Generate layout for unique images\n const baseLayout = await generateLayout({\n imgs: uniqueImgs,\n pixelRatio,\n format: false,\n });\n\n // Expand layout to include all original IDs\n const layout: Layout = {};\n\n for (const img of imgs) {\n const primaryId = idMapping.get(img.id);\n if (primaryId && baseLayout[primaryId]) {\n const baseItem = baseLayout[primaryId];\n layout[img.id] = format\n ? {\n width: baseItem.width,\n height: baseItem.height,\n x: baseItem.x,\n y: baseItem.y,\n pixelRatio,\n }\n : baseItem;\n }\n }\n\n return layout;\n}\n","/**\n * ShelfPack - A 2D bin packing algorithm using Shelf Best Height Fit\n *\n * Based on the algorithm from:\n * http://clb.demon.fi/files/RectangleBinPack.pdf\n *\n * TypeScript port from @mapbox/shelf-pack\n */\n\n/**\n * Bin 对象 - 表示一个已分配的矩形区域\n */\nexport class Bin {\n id: number | string;\n x: number;\n y: number;\n w: number;\n h: number;\n maxw: number;\n maxh: number;\n refcount: number;\n\n /**\n * @param id Unique id of the bin\n * @param x Left coordinate of the bin\n * @param y Top coordinate of the bin\n * @param w Width of the bin\n * @param h Height of the bin\n * @param maxw Max width of the bin (defaults to `w` if not provided)\n * @param maxh Max height of the bin (defaults to `h` if not provided)\n */\n constructor(\n id: number | string,\n x: number,\n y: number,\n w: number,\n h: number,\n maxw?: number,\n maxh?: number,\n ) {\n this.id = id;\n this.x = x;\n this.y = y;\n this.w = w;\n this.h = h;\n this.maxw = maxw || w;\n this.maxh = maxh || h;\n this.refcount = 0;\n }\n}\n\n/**\n * Shelf 对象 - 表示一个水平货架\n */\nclass Shelf {\n x: number;\n y: number;\n w: number;\n h: number;\n free: number;\n\n /**\n * @param y Top coordinate of the new shelf\n * @param w Width of the new shelf\n * @param h Height of the new shelf\n */\n constructor(y: number, w: number, h: number) {\n this.x = 0;\n this.y = y;\n this.w = this.free = w;\n this.h = h;\n }\n\n /**\n * 在货架上分配一个 bin\n *\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique id of the bin to allocate\n * @returns Bin object or null if allocation failed\n */\n alloc(w: number, h: number, id: number | string): Bin | null {\n if (w > this.free || h > this.h) {\n return null;\n }\n const x = this.x;\n this.x += w;\n this.free -= w;\n return new Bin(id, x, this.y, w, h, w, this.h);\n }\n\n /**\n * 调整货架宽度\n *\n * @param w Requested new width of the shelf\n * @returns true\n */\n resize(w: number): boolean {\n this.free += w - this.w;\n this.w = w;\n return true;\n }\n}\n\n/**\n * ShelfPack 配置选项\n */\nexport interface ShelfPackOptions {\n /** If `true`, the sprite will automatically grow */\n autoResize?: boolean;\n}\n\n/**\n * 待打包的项目接口\n */\nexport interface PackItem {\n id?: number | string;\n w?: number;\n h?: number;\n width?: number;\n height?: number;\n}\n\n/**\n * 打包选项\n */\nexport interface PackOptions {\n /** If `true`, the supplied bin objects will be updated inplace with `x` and `y` properties */\n inPlace?: boolean;\n}\n\n/**\n * ShelfPack - 2D bin packing allocator\n *\n * Uses the Shelf Best Height Fit algorithm\n *\n * @example\n * ```ts\n * const sprite = new ShelfPack(64, 64, { autoResize: true });\n * const bins = sprite.pack([\n * { id: 'a', w: 12, h: 12 },\n * { id: 'b', w: 12, h: 16 },\n * ], { inPlace: true });\n * ```\n */\nexport default class ShelfPack {\n w: number;\n h: number;\n autoResize: boolean;\n shelves: Shelf[];\n freebins: Bin[];\n stats: Record<number, number>;\n bins: Record<string | number, Bin>;\n maxId: number;\n\n /**\n * @param w Initial width of the sprite\n * @param h Initial height of the sprite\n * @param options Configuration options\n */\n constructor(w: number = 64, h: number = 64, options: ShelfPackOptions = {}) {\n this.w = w;\n this.h = h;\n this.autoResize = !!options.autoResize;\n this.shelves = [];\n this.freebins = [];\n this.stats = {};\n this.bins = {};\n this.maxId = 0;\n }\n\n /**\n * 批量打包多个 bins\n *\n * @param bins Array of requested bins\n * @param options Pack options\n * @returns Array of allocated Bins\n *\n * @example\n * ```ts\n * const bins = [\n * { id: 1, w: 12, h: 12 },\n * { id: 2, w: 12, h: 16 },\n * ];\n * const results = sprite.pack(bins, { inPlace: false });\n * ```\n */\n pack(bins: PackItem[], options: PackOptions = {}): Bin[] {\n const binArray = [].concat(bins as any) as PackItem[];\n const results: Bin[] = [];\n\n for (let i = 0; i < binArray.length; i++) {\n const w = binArray[i].w || binArray[i].width;\n const h = binArray[i].h || binArray[i].height;\n const id = binArray[i].id;\n\n if (w && h) {\n const allocation = this.packOne(w, h, id);\n if (!allocation) {\n continue;\n }\n if (options.inPlace) {\n (binArray[i] as any).x = allocation.x;\n (binArray[i] as any).y = allocation.y;\n (binArray[i] as any).id = allocation.id;\n }\n results.push(allocation);\n }\n }\n\n this.shrink();\n\n return results;\n }\n\n /**\n * 打包单个 bin\n *\n * 每个 bin 都有唯一标识符。如果未提供 id,将自动创建一个。\n * Bins 自动进行引用计数。\n *\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique identifier for this bin\n * @returns Bin object or null if allocation failed\n *\n * @example\n * ```ts\n * const bin = sprite.packOne(12, 16, 'a');\n * ```\n */\n packOne(w: number, h: number, id?: number | string): Bin | null {\n const best = { freebin: -1, shelf: -1, waste: Infinity };\n let y = 0;\n let bin: Bin | undefined;\n let shelf: Shelf;\n let waste: number;\n let i: number;\n\n // if id was supplied, attempt a lookup..\n if (typeof id === 'string' || typeof id === 'number') {\n bin = this.getBin(id);\n if (bin) {\n // we packed this bin already\n this.ref(bin);\n return bin;\n }\n if (typeof id === 'number') {\n this.maxId = Math.max(id, this.maxId);\n }\n } else {\n id = ++this.maxId;\n }\n\n // First try to reuse a free bin..\n for (i = 0; i < this.freebins.length; i++) {\n bin = this.freebins[i];\n\n // exactly the right height and width, use it..\n if (h === bin.maxh && w === bin.maxw) {\n return this.allocFreebin(i, w, h, id);\n }\n // not enough height or width, skip it..\n if (h > bin.maxh || w > bin.maxw) {\n continue;\n }\n // extra height or width, minimize wasted area..\n if (h <= bin.maxh && w <= bin.maxw) {\n waste = bin.maxw * bin.maxh - w * h;\n if (waste < best.waste) {\n best.waste = waste;\n best.freebin = i;\n }\n }\n }\n\n // Next find the best shelf..\n for (i = 0; i < this.shelves.length; i++) {\n shelf = this.shelves[i];\n y += shelf.h;\n\n // not enough width on this shelf, skip it..\n if (w > shelf.free) {\n continue;\n }\n // exactly the right height, pack it..\n if (h === shelf.h) {\n return this.allocShelf(i, w, h, id);\n }\n // not enough height, skip it..\n if (h > shelf.h) {\n continue;\n }\n // extra height, minimize wasted area..\n if (h < shelf.h) {\n waste = (shelf.h - h) * w;\n if (waste < best.waste) {\n best.freebin = -1;\n best.waste = waste;\n best.shelf = i;\n }\n }\n }\n\n if (best.freebin !== -1) {\n return this.allocFreebin(best.freebin, w, h, id);\n }\n\n if (best.shelf !== -1) {\n return this.allocShelf(best.shelf, w, h, id);\n }\n\n // No free bins or shelves.. add shelf..\n if (h <= this.h - y && w <= this.w) {\n shelf = new Shelf(y, this.w, h);\n return this.allocShelf(this.shelves.push(shelf) - 1, w, h, id);\n }\n\n // No room for more shelves..\n // If `autoResize` option is set, grow the sprite\n if (this.autoResize) {\n const h1 = this.h;\n let h2 = this.h;\n const w1 = this.w;\n let w2 = this.w;\n\n if (w1 <= h1 || w > w1) {\n // grow width..\n w2 = Math.max(w, w1) * 2;\n }\n if (h1 < w1 || h > h1) {\n // grow height..\n h2 = Math.max(h, h1) * 2;\n }\n\n this.resize(w2, h2);\n return this.packOne(w, h, id); // retry\n }\n\n return null;\n }\n\n /**\n * 通过重用现有 freebin 分配一个 bin\n *\n * @param index Index into the `this.freebins` array\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique identifier for this bin\n * @returns Bin object\n */\n private allocFreebin(index: number, w: number, h: number, id: number | string): Bin {\n const bin = this.freebins.splice(index, 1)[0];\n bin.id = id;\n bin.w = w;\n bin.h = h;\n bin.refcount = 0;\n this.bins[id] = bin;\n this.ref(bin);\n return bin;\n }\n\n /**\n * 在现有货架上分配 bin\n *\n * @param index Index into the `this.shelves` array\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique identifier for this bin\n * @returns Bin object\n */\n private allocShelf(index: number, w: number, h: number, id: number | string): Bin {\n const shelf = this.shelves[index];\n const bin = shelf.alloc(w, h, id);\n if (bin) {\n this.bins[id] = bin;\n this.ref(bin);\n }\n return bin as Bin;\n }\n\n /**\n * 将 sprite 的宽度/高度缩小到最小\n *\n * 由于 shelf-pack 在空间不足时会先加倍宽度再加倍高度,\n * 如果这发生在打包结束时,可能会导致较大的未使用空间\n */\n shrink(): void {\n if (this.shelves.length > 0) {\n let w2 = 0;\n let h2 = 0;\n\n for (let j = 0; j < this.shelves.length; j++) {\n const shelf = this.shelves[j];\n h2 += shelf.h;\n w2 = Math.max(shelf.w - shelf.free, w2);\n }\n\n this.resize(w2, h2);\n }\n }\n\n /**\n * 根据 id 返回已打包的 bin\n *\n * @param id Unique identifier for this bin\n * @returns The requested bin, or undefined if not found\n *\n * @example\n * ```ts\n * const bin = sprite.getBin('a');\n * ```\n */\n getBin(id: number | string): Bin | undefined {\n return this.bins[id];\n }\n\n /**\n * 增加 bin 的引用计数并更新统计信息\n *\n * @param bin Bin instance\n * @returns New refcount of the bin\n *\n * @example\n * ```ts\n * const bin = sprite.getBin('a');\n * if (bin) sprite.ref(bin);\n * ```\n */\n ref(bin: Bin): number {\n if (++bin.refcount === 1) {\n // a new Bin.. record height in stats histogram..\n const h = bin.h;\n this.stats[h] = (this.stats[h] || 0) + 1;\n }\n\n return bin.refcount;\n }\n\n /**\n * 减少 bin 的引用计数并更新统计信息\n * 当引用计数达到 0 时,bin 将自动标记为空闲空间\n *\n * @param bin Bin instance\n * @returns New refcount of the bin\n *\n * @example\n * ```ts\n * const bin = sprite.getBin('a');\n * if (bin) sprite.unref(bin);\n * ```\n */\n unref(bin: Bin): number {\n if (bin.refcount === 0) {\n return 0;\n }\n\n if (--bin.refcount === 0) {\n this.stats[bin.h]--;\n delete this.bins[bin.id];\n this.freebins.push(bin);\n }\n\n return bin.refcount;\n }\n\n /**\n * 清除 sprite,重置所有内容和统计信息\n *\n * @example\n * ```ts\n * sprite.clear();\n * ```\n */\n clear(): void {\n this.shelves = [];\n this.freebins = [];\n this.stats = {};\n this.bins = {};\n this.maxId = 0;\n }\n\n /**\n * 调整 sprite 尺寸\n *\n * @param w Requested new sprite width\n * @param h Requested new sprite height\n * @returns `true` if resize succeeded\n *\n * @example\n * ```ts\n * sprite.resize(256, 256);\n * ```\n */\n resize(w: number, h: number): boolean {\n this.w = w;\n this.h = h;\n for (let i = 0; i < this.shelves.length; i++) {\n this.shelves[i].resize(w);\n }\n return true;\n }\n}\n","/**\n * SVG Metadata Extraction\n *\n * 支持从SVG中提取Mapbox样式的stretch元数据和placeholder元数据\n * 用于实现9-patch风格的图像拉伸和文本占位符\n *\n * 元数据通过特殊命名的SVG路径标记提取:\n * - id=\"mapbox-stretch-x\" 或 id=\"mapbox-stretch\" : 水平拉伸区域\n * - id=\"mapbox-stretch-y\" 或 id=\"mapbox-stretch\" : 垂直拉伸区域\n * - id=\"mapbox-content\" : 内容区域\n * - id=\"mapbox-text-placeholder\" : 文本占位符区域\n *\n * @see https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/#stretch-metadata\n */\n\nimport { optimize } from 'svgo';\nimport computePathBounds from 'svg-boundings';\nimport type { SpriteMetadata } from '../types.js';\n\nconst metadataPrefix = 'mapbox-';\n\n/**\n * 精确舍入浮点数\n * 从svgo移植,用于减少小数位数\n */\nfunction strongRound(value: number, precision: number = 3): number {\n const error = +Math.pow(0.1, precision).toFixed(precision);\n if (+value.toFixed(precision) !== value) {\n const rounded = +value.toFixed(precision - 1);\n value =\n +Math.abs(rounded - value).toFixed(precision + 1) >= error\n ? +value.toFixed(precision)\n : rounded;\n }\n return value;\n}\n\n/**\n * 从SVG Buffer中提取元数据\n *\n * 使用简化的方法:\n * 1. 使用 svgo 优化 SVG(转换形状为路径等)\n * 2. 使用 DOMParser 解析优化后的 SVG\n * 3. 查找带有 mapbox- 前缀的路径\n * 4. 使用 svg-boundings 计算边界\n * 5. 按像素比率缩放并返回\n *\n * @param svg - SVG文件内容\n * @param pixelRatio - 像素比率,用于缩放边界坐标\n * @returns Promise<SpriteMetadata> 提取的元数据\n */\nexport async function extractMetadata(\n svg: Buffer,\n pixelRatio: number = 1,\n): Promise<SpriteMetadata> {\n const svgString = svg.toString('utf-8');\n\n // 快速检测:如果SVG不包含mapbox-相关标记,直接返回空元数据\n if (!svgString.includes('mapbox-stretch') && !svgString.includes('mapbox-text-placeholder')) {\n return {};\n }\n\n try {\n // 使用 svgo 优化 SVG,转换形状为路径\n const optimized = optimize(svgString, {\n plugins: [\n {\n name: 'convertShapeToPath',\n params: {\n convertArcs: true,\n },\n },\n {\n name: 'convertTransform',\n },\n {\n name: 'cleanupIds',\n params: {\n preservePrefixes: ['mapbox-'],\n },\n },\n ],\n });\n\n // 使用 DOMParser 解析优化后的 SVG\n const { DOMParser } = await import('@xmldom/xmldom');\n const doc = new DOMParser().parseFromString(optimized.data, 'image/svg+xml');\n\n const metadata: SpriteMetadata = {};\n\n // 查找所有带有 mapbox- 前缀的路径\n const paths = doc.getElementsByTagName('path');\n\n for (let i = 0; i < paths.length; i++) {\n const path = paths[i];\n const id = path.getAttribute('id');\n const d = path.getAttribute('d');\n\n if (!id || !id.startsWith(metadataPrefix) || !d) {\n continue;\n }\n\n try {\n // 计算路径边界\n const bounds = computePathBounds.path({ d }, true);\n\n // 验证边界有效性\n if (\n !Number.isFinite(bounds.left) ||\n !Number.isFinite(bounds.right) ||\n !Number.isFinite(bounds.top) ||\n !Number.isFinite(bounds.bottom)\n ) {\n continue;\n }\n\n // 按像素比率缩放边界\n const left = strongRound(bounds.left * pixelRatio);\n const top = strongRound(bounds.top * pixelRatio);\n const right = strongRound(bounds.right * pixelRatio);\n const bottom = strongRound(bounds.bottom * pixelRatio);\n\n const metadataType = id.substring(metadataPrefix.length);\n\n // 根据ID类型添加元数据\n if (metadataType.startsWith('stretch-x') || metadataType === 'stretch-x') {\n metadata.stretchX = metadata.stretchX || [];\n metadata.stretchX.push([left, right]);\n } else if (metadataType.startsWith('stretch-y') || metadataType === 'stretch-y') {\n metadata.stretchY = metadata.stretchY || [];\n metadata.stretchY.push([top, bottom]);\n } else if (metadataType === 'stretch') {\n metadata.stretchX = metadata.stretchX || [];\n metadata.stretchX.push([left, right]);\n metadata.stretchY = metadata.stretchY || [];\n metadata.stretchY.push([top, bottom]);\n } else if (metadataType === 'content') {\n metadata.content = [left, top, right, bottom];\n } else if (metadataType === 'text-placeholder') {\n metadata.placeholder = [left, top, right, bottom];\n }\n } catch (err) {\n console.warn(`Failed to compute bounds for ${id}: ${err}`);\n }\n }\n\n // 排序拉伸区域(升序)\n if (metadata.stretchX) {\n metadata.stretchX.sort((a, b) => a[0] - b[0]);\n }\n if (metadata.stretchY) {\n metadata.stretchY.sort((a, b) => a[0] - b[0]);\n }\n\n return metadata;\n } catch (err) {\n console.error(`Failed to extract metadata: ${err}`);\n return {};\n }\n}\n\n/**\n * 检查SVG是否包含元数据标记\n *\n * @param svg - SVG文件内容\n * @returns boolean 是否包含元数据\n */\nexport function hasMetadata(svg: Buffer): boolean {\n const svgString = svg.toString('utf-8');\n return svgString.includes('mapbox-stretch') || svgString.includes('mapbox-text-placeholder');\n}\n\n/**\n * 示例:元数据提取的测试用例\n *\n * 输入SVG应该包含类似这样的路径:\n * ```xml\n * <svg width=\"24\" height=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\n * <!-- 主图形 -->\n * <rect width=\"24\" height=\"24\" fill=\"#333\"/>\n *\n * <!-- 拉伸元数据:水平和垂直方向都可拉伸 -->\n * <path id=\"mapbox-stretch\" d=\"M 8 0 L 16 0 L 16 24 L 8 24 Z\" fill=\"none\"/>\n *\n * <!-- 内容区域:文本或图标应该放置的区域 -->\n * <path id=\"mapbox-content\" d=\"M 4 4 L 20 4 L 20 20 L 4 20 Z\" fill=\"none\"/>\n * </svg>\n * ```\n *\n * 预期输出(pixelRatio=1):\n * ```json\n * {\n * \"stretchX\": [[8, 16]],\n * \"stretchY\": [[8, 16]],\n * \"content\": [4, 4, 20, 20]\n * }\n * ```\n */\n","/**\n * Sprite image generation\n * Migrated from @mapbox/spritezero with TypeScript enhancements\n *\n * 支持:\n * - 标准PNG输出\n * - 优化PNG输出(颜色量化、压缩)\n */\n\nimport sharp from 'sharp';\nimport { createCanvas, loadImage, type SKRSContext2D } from '@napi-rs/canvas';\nimport type { Layout } from '../types.js';\n\n/**\n * Render image (SVG or PNG) to canvas context\n * @param imageBuffer - Image buffer (SVG or PNG)\n * @param isPNG - Whether the image is PNG format\n * @param ctx - Canvas 2D context\n * @param x - X position\n * @param y - Y position\n * @param width - Image width\n * @param height - Image height\n * @param pixelRatio - Pixel ratio\n */\nasync function renderImage(\n imageBuffer: Buffer,\n isPNG: boolean,\n ctx: SKRSContext2D,\n x: number,\n y: number,\n width: number,\n height: number,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let src: Buffer | string;\n\n // Set image data as source\n if (isPNG) {\n // PNG: use buffer directly\n src = imageBuffer;\n } else {\n // SVG: convert to data URL\n const svgString = imageBuffer.toString('utf8');\n const svgDataUrl = `data:image/svg+xml;base64,${Buffer.from(svgString).toString('base64')}`;\n src = svgDataUrl;\n }\n\n loadImage(src)\n .then((image) => {\n ctx.drawImage(image, x, y, width, height);\n resolve();\n })\n .catch((error) => {\n reject(new Error(`Failed to load image: ${error}`));\n });\n });\n}\n\n/**\n * Calculate canvas dimensions from layout\n * @param layout - Sprite layout\n * @returns Canvas dimensions\n */\nfunction getCanvasDimensions(layout: Layout): { width: number; height: number } {\n let maxX = 0;\n let maxY = 0;\n\n for (const item of Object.values(layout)) {\n maxX = Math.max(maxX, item.x + item.width);\n maxY = Math.max(maxY, item.y + item.height);\n }\n\n return {\n width: maxX || 1,\n height: maxY || 1,\n };\n}\n\n/**\n * Generate sprite image from layout\n * @param layout - Sprite layout (must include SVG data)\n * @returns PNG image buffer\n * @throws Error if layout is empty or invalid\n */\nexport async function generateImage(layout: Layout): Promise<Buffer> {\n // 检查空布局\n const layoutKeys = Object.keys(layout);\n if (layoutKeys.length === 0) {\n throw new Error('Cannot generate image from empty layout');\n }\n\n // Calculate canvas size\n const { width, height } = getCanvasDimensions(layout);\n\n // 验证画布尺寸\n if (width === 0 || height === 0) {\n throw new Error('Invalid canvas dimensions: width and height must be greater than 0');\n }\n\n // Create canvas\n const canvas = createCanvas(width, height);\n const ctx = canvas.getContext('2d');\n\n // Clear canvas\n ctx.clearRect(0, 0, width, height);\n\n // Render all images\n const renderPromises: Promise<void>[] = [];\n\n for (const [id, item] of Object.entries(layout)) {\n // Check if image data is available (SVG or PNG)\n const svg = (item as any).svg as Buffer | undefined;\n const png = (item as any).png as Buffer | undefined;\n\n if (!svg && !png) {\n console.warn(`Warning: No image data for '${id}', skipping...`);\n continue;\n }\n\n const imageBuffer = svg || png!;\n const isPNG = !!png;\n\n const promise = renderImage(imageBuffer, isPNG, ctx, item.x, item.y, item.width, item.height);\n\n renderPromises.push(promise);\n }\n\n // Wait for all images to render\n await Promise.all(renderPromises);\n\n // Convert canvas to PNG buffer\n // @napi-rs/canvas uses toBuffer(mime) instead of toBuffer(format)\n return canvas.toBuffer('image/png');\n}\n\n/**\n * PNG 优化选项\n */\nexport interface OptimizeOptions {\n /** 颜色数量 (default: 256) */\n colors?: number;\n /** 压缩质量 0-100 (default: 80) */\n quality?: number;\n /** 是否使用调色板模式 (default: true) */\n palette?: boolean;\n}\n\n/**\n * 生成优化的PNG雪碧图\n *\n * 使用 sharp 进行 PNG 优化:\n * - 颜色量化(减少到指定颜色数)\n * - PNG 压缩优化\n * - 减小文件大小 30-50%\n *\n * @param layout - Sprite layout\n * @param options - 优化选项\n * @returns 优化后的PNG buffer\n *\n * @example\n * ```ts\n * const optimizedPng = await generateOptimizedImage(layout, {\n * colors: 256,\n * quality: 80,\n * palette: true\n * });\n * ```\n */\nexport async function generateOptimizedImage(\n layout: Layout,\n options: OptimizeOptions = {},\n): Promise<Buffer> {\n const { colors = 256, quality = 80, palette = true } = options;\n\n // 首先生成标准 PNG\n const standardPng = await generateImage(layout);\n\n // 使用 sharp 优化\n const optimized = await sharp(standardPng)\n .png({\n palette: palette,\n quality: quality,\n colors: colors,\n compressionLevel: 9, // 最高压缩级别\n adaptiveFiltering: true,\n force: true,\n })\n .toBuffer();\n\n return optimized;\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport type { ImageBuffer } from './types.js';\n\n/**\n * 获取目录中所有图片文件的路径(支持 SVG 和 PNG)\n * @param dir 目录路径\n * @returns 文件路径数组\n */\nexport async function getFilePaths(dir: string): Promise<string[]> {\n const entries = await fs.readdir(dir);\n return entries\n .filter((entry) => !entry.startsWith('.') && (entry.endsWith('.svg') || entry.endsWith('.png')))\n .map((entry) => path.join(dir, entry));\n}\n\n/**\n * 加载单个图片文件(支持 SVG 和 PNG)\n * @param filePath 文件路径\n * @returns 图片缓冲区对象\n */\nexport async function loadFile(filePath: string): Promise<ImageBuffer> {\n const buffer = await fs.readFile(filePath);\n const ext = path.extname(filePath).toLowerCase();\n const id = path.basename(filePath).replace(/\\.(svg|png)$/i, '');\n\n if (ext === '.svg') {\n return { svg: buffer, id };\n } else if (ext === '.png') {\n return { png: buffer, id };\n } else {\n throw new Error(`Unsupported file format: ${ext}`);\n }\n}\n\n/**\n * 根据 ID 排序\n * @param a 第一个图片缓冲区\n * @param b 第二个图片缓冲区\n * @returns 排序结果\n */\nexport function sortById(a: ImageBuffer, b: ImageBuffer): number {\n return b.id < a.id ? 1 : -1;\n}\n\n/**\n * 显示帮助信息\n */\nexport function showHelp(): void {\n console.log(`\nsprite-maker\n\nGenerate sprite sheets for maps and the web using SVG files as input\n\nUsage\n sprite-maker <output> <inputdir> [options]\n\nOptions\n --retina shorthand for --ratio=2\n --ratio=[n] pixel ratio (default: 1)\n --unique map identical images to multiple names\n --help, -h show help\n\nExample\n sprite-maker maki maki/\n sprite-maker maki maki/ --retina\n sprite-maker maki maki/ --ratio=3\n sprite-maker maki maki/ --unique\n `);\n}\n"],"mappings":";AAAA,OAAOA,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAO,eAAe;;;ACGtB,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;;;ACMnB,IAAM,MAAN,MAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBf,YACE,IACA,GACA,GACA,GACA,GACA,MACA,MACA;AACA,SAAK,KAAK;AACV,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,QAAQ;AACpB,SAAK,WAAW;AAAA,EAClB;AACF;AAKA,IAAM,QAAN,MAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYV,YAAY,GAAW,GAAW,GAAW;AAC3C,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI,KAAK,OAAO;AACrB,SAAK,IAAI;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,GAAW,GAAW,IAAiC;AAC3D,QAAI,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,UAAM,IAAI,KAAK;AACf,SAAK,KAAK;AACV,SAAK,QAAQ;AACb,WAAO,IAAI,IAAI,IAAI,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,GAAoB;AACzB,SAAK,QAAQ,IAAI,KAAK;AACtB,SAAK,IAAI;AACT,WAAO;AAAA,EACT;AACF;AA2CA,IAAqB,YAArB,MAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe7B,YAAY,IAAY,IAAI,IAAY,IAAI,UAA4B,CAAC,GAAG;AAC1E,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,aAAa,CAAC,CAAC,QAAQ;AAC5B,SAAK,UAAU,CAAC;AAChB,SAAK,WAAW,CAAC;AACjB,SAAK,QAAQ,CAAC;AACd,SAAK,OAAO,CAAC;AACb,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,KAAK,MAAkB,UAAuB,CAAC,GAAU;AACvD,UAAM,WAAW,CAAC,EAAE,OAAO,IAAW;AACtC,UAAM,UAAiB,CAAC;AAExB,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,IAAI,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE;AACvC,YAAM,IAAI,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE;AACvC,YAAM,KAAK,SAAS,CAAC,EAAE;AAEvB,UAAI,KAAK,GAAG;AACV,cAAM,aAAa,KAAK,QAAQ,GAAG,GAAG,EAAE;AACxC,YAAI,CAAC,YAAY;AACf;AAAA,QACF;AACA,YAAI,QAAQ,SAAS;AACnB,UAAC,SAAS,CAAC,EAAU,IAAI,WAAW;AACpC,UAAC,SAAS,CAAC,EAAU,IAAI,WAAW;AACpC,UAAC,SAAS,CAAC,EAAU,KAAK,WAAW;AAAA,QACvC;AACA,gBAAQ,KAAK,UAAU;AAAA,MACzB;AAAA,IACF;AAEA,SAAK,OAAO;AAEZ,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,QAAQ,GAAW,GAAW,IAAkC;AAC9D,UAAM,OAAO,EAAE,SAAS,IAAI,OAAO,IAAI,OAAO,SAAS;AACvD,QAAI,IAAI;AACR,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAGJ,QAAI,OAAO,OAAO,YAAY,OAAO,OAAO,UAAU;AACpD,YAAM,KAAK,OAAO,EAAE;AACpB,UAAI,KAAK;AAEP,aAAK,IAAI,GAAG;AACZ,eAAO;AAAA,MACT;AACA,UAAI,OAAO,OAAO,UAAU;AAC1B,aAAK,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK;AAAA,MACtC;AAAA,IACF,OAAO;AACL,WAAK,EAAE,KAAK;AAAA,IACd;AAGA,SAAK,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;AACzC,YAAM,KAAK,SAAS,CAAC;AAGrB,UAAI,MAAM,IAAI,QAAQ,MAAM,IAAI,MAAM;AACpC,eAAO,KAAK,aAAa,GAAG,GAAG,GAAG,EAAE;AAAA,MACtC;AAEA,UAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,MAAM;AAChC;AAAA,MACF;AAEA,UAAI,KAAK,IAAI,QAAQ,KAAK,IAAI,MAAM;AAClC,gBAAQ,IAAI,OAAO,IAAI,OAAO,IAAI;AAClC,YAAI,QAAQ,KAAK,OAAO;AACtB,eAAK,QAAQ;AACb,eAAK,UAAU;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAGA,SAAK,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AACxC,cAAQ,KAAK,QAAQ,CAAC;AACtB,WAAK,MAAM;AAGX,UAAI,IAAI,MAAM,MAAM;AAClB;AAAA,MACF;AAEA,UAAI,MAAM,MAAM,GAAG;AACjB,eAAO,KAAK,WAAW,GAAG,GAAG,GAAG,EAAE;AAAA,MACpC;AAEA,UAAI,IAAI,MAAM,GAAG;AACf;AAAA,MACF;AAEA,UAAI,IAAI,MAAM,GAAG;AACf,iBAAS,MAAM,IAAI,KAAK;AACxB,YAAI,QAAQ,KAAK,OAAO;AACtB,eAAK,UAAU;AACf,eAAK,QAAQ;AACb,eAAK,QAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,QAAI,KAAK,YAAY,IAAI;AACvB,aAAO,KAAK,aAAa,KAAK,SAAS,GAAG,GAAG,EAAE;AAAA,IACjD;AAEA,QAAI,KAAK,UAAU,IAAI;AACrB,aAAO,KAAK,WAAW,KAAK,OAAO,GAAG,GAAG,EAAE;AAAA,IAC7C;AAGA,QAAI,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,GAAG;AAClC,cAAQ,IAAI,MAAM,GAAG,KAAK,GAAG,CAAC;AAC9B,aAAO,KAAK,WAAW,KAAK,QAAQ,KAAK,KAAK,IAAI,GAAG,GAAG,GAAG,EAAE;AAAA,IAC/D;AAIA,QAAI,KAAK,YAAY;AACnB,YAAM,KAAK,KAAK;AAChB,UAAI,KAAK,KAAK;AACd,YAAM,KAAK,KAAK;AAChB,UAAI,KAAK,KAAK;AAEd,UAAI,MAAM,MAAM,IAAI,IAAI;AAEtB,aAAK,KAAK,IAAI,GAAG,EAAE,IAAI;AAAA,MACzB;AACA,UAAI,KAAK,MAAM,IAAI,IAAI;AAErB,aAAK,KAAK,IAAI,GAAG,EAAE,IAAI;AAAA,MACzB;AAEA,WAAK,OAAO,IAAI,EAAE;AAClB,aAAO,KAAK,QAAQ,GAAG,GAAG,EAAE;AAAA,IAC9B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,aAAa,OAAe,GAAW,GAAW,IAA0B;AAClF,UAAM,MAAM,KAAK,SAAS,OAAO,OAAO,CAAC,EAAE,CAAC;AAC5C,QAAI,KAAK;AACT,QAAI,IAAI;AACR,QAAI,IAAI;AACR,QAAI,WAAW;AACf,SAAK,KAAK,EAAE,IAAI;AAChB,SAAK,IAAI,GAAG;AACZ,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,WAAW,OAAe,GAAW,GAAW,IAA0B;AAChF,UAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,UAAM,MAAM,MAAM,MAAM,GAAG,GAAG,EAAE;AAChC,QAAI,KAAK;AACP,WAAK,KAAK,EAAE,IAAI;AAChB,WAAK,IAAI,GAAG;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAe;AACb,QAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,UAAI,KAAK;AACT,UAAI,KAAK;AAET,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC5C,cAAM,QAAQ,KAAK,QAAQ,CAAC;AAC5B,cAAM,MAAM;AACZ,aAAK,KAAK,IAAI,MAAM,IAAI,MAAM,MAAM,EAAE;AAAA,MACxC;AAEA,WAAK,OAAO,IAAI,EAAE;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,OAAO,IAAsC;AAC3C,WAAO,KAAK,KAAK,EAAE;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,IAAI,KAAkB;AACpB,QAAI,EAAE,IAAI,aAAa,GAAG;AAExB,YAAM,IAAI,IAAI;AACd,WAAK,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,KAAK;AAAA,IACzC;AAEA,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,KAAkB;AACtB,QAAI,IAAI,aAAa,GAAG;AACtB,aAAO;AAAA,IACT;AAEA,QAAI,EAAE,IAAI,aAAa,GAAG;AACxB,WAAK,MAAM,IAAI,CAAC;AAChB,aAAO,KAAK,KAAK,IAAI,EAAE;AACvB,WAAK,SAAS,KAAK,GAAG;AAAA,IACxB;AAEA,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QAAc;AACZ,SAAK,UAAU,CAAC;AAChB,SAAK,WAAW,CAAC;AACjB,SAAK,QAAQ,CAAC;AACd,SAAK,OAAO,CAAC;AACb,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAO,GAAW,GAAoB;AACpC,SAAK,IAAI;AACT,SAAK,IAAI;AACT,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC5C,WAAK,QAAQ,CAAC,EAAE,OAAO,CAAC;AAAA,IAC1B;AACA,WAAO;AAAA,EACT;AACF;;;ACveA,SAAS,gBAAgB;AACzB,OAAO,uBAAuB;AAG9B,IAAM,iBAAiB;AAMvB,SAAS,YAAY,OAAe,YAAoB,GAAW;AACjE,QAAM,QAAQ,CAAC,KAAK,IAAI,KAAK,SAAS,EAAE,QAAQ,SAAS;AACzD,MAAI,CAAC,MAAM,QAAQ,SAAS,MAAM,OAAO;AACvC,UAAM,UAAU,CAAC,MAAM,QAAQ,YAAY,CAAC;AAC5C,YACE,CAAC,KAAK,IAAI,UAAU,KAAK,EAAE,QAAQ,YAAY,CAAC,KAAK,QACjD,CAAC,MAAM,QAAQ,SAAS,IACxB;AAAA,EACR;AACA,SAAO;AACT;AAgBA,eAAsB,gBACpB,KACA,aAAqB,GACI;AACzB,QAAM,YAAY,IAAI,SAAS,OAAO;AAGtC,MAAI,CAAC,UAAU,SAAS,gBAAgB,KAAK,CAAC,UAAU,SAAS,yBAAyB,GAAG;AAC3F,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AAEF,UAAM,YAAY,SAAS,WAAW;AAAA,MACpC,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,QAAQ;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA;AAAA,UACE,MAAM;AAAA,QACR;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,QAAQ;AAAA,YACN,kBAAkB,CAAC,SAAS;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAGD,UAAM,EAAE,WAAAC,WAAU,IAAI,MAAM,OAAO,gBAAgB;AACnD,UAAM,MAAM,IAAIA,WAAU,EAAE,gBAAgB,UAAU,MAAM,eAAe;AAE3E,UAAM,WAA2B,CAAC;AAGlC,UAAM,QAAQ,IAAI,qBAAqB,MAAM;AAE7C,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAMC,QAAO,MAAM,CAAC;AACpB,YAAM,KAAKA,MAAK,aAAa,IAAI;AACjC,YAAM,IAAIA,MAAK,aAAa,GAAG;AAE/B,UAAI,CAAC,MAAM,CAAC,GAAG,WAAW,cAAc,KAAK,CAAC,GAAG;AAC/C;AAAA,MACF;AAEA,UAAI;AAEF,cAAM,SAAS,kBAAkB,KAAK,EAAE,EAAE,GAAG,IAAI;AAGjD,YACE,CAAC,OAAO,SAAS,OAAO,IAAI,KAC5B,CAAC,OAAO,SAAS,OAAO,KAAK,KAC7B,CAAC,OAAO,SAAS,OAAO,GAAG,KAC3B,CAAC,OAAO,SAAS,OAAO,MAAM,GAC9B;AACA;AAAA,QACF;AAGA,cAAM,OAAO,YAAY,OAAO,OAAO,UAAU;AACjD,cAAM,MAAM,YAAY,OAAO,MAAM,UAAU;AAC/C,cAAM,QAAQ,YAAY,OAAO,QAAQ,UAAU;AACnD,cAAM,SAAS,YAAY,OAAO,SAAS,UAAU;AAErD,cAAM,eAAe,GAAG,UAAU,eAAe,MAAM;AAGvD,YAAI,aAAa,WAAW,WAAW,KAAK,iBAAiB,aAAa;AACxE,mBAAS,WAAW,SAAS,YAAY,CAAC;AAC1C,mBAAS,SAAS,KAAK,CAAC,MAAM,KAAK,CAAC;AAAA,QACtC,WAAW,aAAa,WAAW,WAAW,KAAK,iBAAiB,aAAa;AAC/E,mBAAS,WAAW,SAAS,YAAY,CAAC;AAC1C,mBAAS,SAAS,KAAK,CAAC,KAAK,MAAM,CAAC;AAAA,QACtC,WAAW,iBAAiB,WAAW;AACrC,mBAAS,WAAW,SAAS,YAAY,CAAC;AAC1C,mBAAS,SAAS,KAAK,CAAC,MAAM,KAAK,CAAC;AACpC,mBAAS,WAAW,SAAS,YAAY,CAAC;AAC1C,mBAAS,SAAS,KAAK,CAAC,KAAK,MAAM,CAAC;AAAA,QACtC,WAAW,iBAAiB,WAAW;AACrC,mBAAS,UAAU,CAAC,MAAM,KAAK,OAAO,MAAM;AAAA,QAC9C,WAAW,iBAAiB,oBAAoB;AAC9C,mBAAS,cAAc,CAAC,MAAM,KAAK,OAAO,MAAM;AAAA,QAClD;AAAA,MACF,SAAS,KAAK;AACZ,gBAAQ,KAAK,gCAAgC,EAAE,KAAK,GAAG,EAAE;AAAA,MAC3D;AAAA,IACF;AAGA,QAAI,SAAS,UAAU;AACrB,eAAS,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAAA,IAC9C;AACA,QAAI,SAAS,UAAU;AACrB,eAAS,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAAA,IAC9C;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,MAAM,+BAA+B,GAAG,EAAE;AAClD,WAAO,CAAC;AAAA,EACV;AACF;AAQO,SAAS,YAAY,KAAsB;AAChD,QAAM,YAAY,IAAI,SAAS,OAAO;AACtC,SAAO,UAAU,SAAS,gBAAgB,KAAK,UAAU,SAAS,yBAAyB;AAC7F;;;AFzIA,SAAS,sBACP,GACA,GACQ;AAER,QAAM,aAAa,EAAE,SAAS,EAAE;AAChC,MAAI,eAAe,GAAG;AACpB,WAAO;AAAA,EACT;AAGA,SAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAChC;AAkCA,SAAS,WAAW,QAIlB;AAGA,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,qBAAqB;AAGrD,QAAM,SAAS,IAAI,UAAU,GAAG,GAAG,EAAE,YAAY,KAAK,CAAC;AAGvD,SAAO,KAAK,QAAQ,EAAE,SAAS,KAAK,CAAC;AAGrC,QAAM,SAAwB,OAAO,IAAI,CAAC,SAAc;AAAA,IACtD,IAAI,IAAI;AAAA,IACR,GAAG,IAAI;AAAA,IACP,GAAG,IAAI;AAAA,IACP,GAAG,IAAI;AAAA,IACP,GAAG,IAAI;AAAA,EACT,EAAE;AAEF,SAAO;AAAA,IACL;AAAA,IACA,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,EACjB;AACF;AAUA,IAAM,qBAAqB,oBAAI,IAA+C;AAe9E,eAAe,mBACb,KACA,YACA,WAAoB,MACwB;AAG5C,MAAI,IAAI,SAAS,IAAI,QAAQ;AAC3B,WAAO;AAAA,MACL,OAAO,KAAK,KAAK,IAAI,QAAQ,UAAU;AAAA,MACvC,QAAQ,KAAK,KAAK,IAAI,SAAS,UAAU;AAAA,IAC3C;AAAA,EACF;AAGA,MAAI,IAAI,KAAK;AACX,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAU,IAAI,GAAI,EACf,KAAK,CAAC,UAAU;AACf,gBAAQ;AAAA,UACN,OAAO,KAAK,KAAK,MAAM,QAAQ,UAAU;AAAA,UACzC,QAAQ,KAAK,KAAK,MAAM,SAAS,UAAU;AAAA,QAC7C,CAAC;AAAA,MACH,CAAC,EACA,MAAM,MAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAGA,MAAI,IAAI,KAAK;AAEX,QAAI;AACJ,QAAI;AAEJ,QAAI,UAAU;AACZ,iBAAW,IAAI,IAAI,SAAS,QAAQ;AACpC,mBAAa,mBAAmB,IAAI,QAAQ;AAE5C,UAAI,YAAY;AAEd,eAAO;AAAA,UACL,OAAO,KAAK,KAAK,WAAW,QAAQ,UAAU;AAAA,UAC9C,QAAQ,KAAK,KAAK,WAAW,SAAS,UAAU;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,IAAI,IAAI,SAAS,MAAM;AACzC,YAAM,MAAM,IAAI,UAAU,EAAE,gBAAgB,WAAW,eAAe;AACtE,YAAM,aAAa,IAAI;AAGvB,YAAM,aAAa,IAAI,qBAAqB,aAAa;AACzD,UAAI,cAAc,WAAW,SAAS,GAAG;AACvC,cAAM,IAAI,MAAM,2BAA2B,IAAI,EAAE,eAAe;AAAA,MAClE;AAEA,UAAI,QAAQ;AACZ,UAAI,SAAS;AAGb,YAAM,YAAY,WAAW,aAAa,OAAO;AACjD,YAAM,aAAa,WAAW,aAAa,QAAQ;AAEnD,UAAI,aAAa,YAAY;AAC3B,gBAAQ,WAAW,SAAS;AAC5B,iBAAS,WAAW,UAAU;AAAA,MAChC,OAAO;AAEL,cAAM,UAAU,WAAW,aAAa,SAAS;AACjD,YAAI,SAAS;AACX,gBAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,cAAI,MAAM,WAAW,GAAG;AACtB,oBAAQ,WAAW,MAAM,CAAC,CAAC;AAC3B,qBAAS,WAAW,MAAM,CAAC,CAAC;AAAA,UAC9B;AAAA,QACF;AAAA,MACF;AAGA,UAAI,CAAC,SAAS,CAAC,QAAQ;AACrB,gBAAQ,KAAK,2BAA2B,IAAI,EAAE,uBAAuB;AACrE,gBAAQ;AACR,iBAAS;AAAA,MACX;AAGA,UAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,KAAK,UAAU,GAAG;AACpF,cAAM,IAAI,MAAM,0BAA0B,IAAI,EAAE,KAAK,KAAK,IAAI,MAAM,EAAE;AAAA,MACxE;AAGA,UAAI,YAAY,UAAU;AACxB,2BAAmB,IAAI,UAAU,EAAE,OAAO,OAAO,CAAC;AAAA,MACpD;AAEA,aAAO;AAAA,QACL,OAAO,KAAK,KAAK,QAAQ,UAAU;AAAA,QACnC,QAAQ,KAAK,KAAK,SAAS,UAAU;AAAA,MACvC;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,IAAI,MAAM,yBAAyB,IAAI,EAAE,KAAK,GAAG,EAAE;AAAA,IAC3D;AAAA,EACF;AAGA,SAAO;AAAA,IACL,OAAO,KAAK,KAAK,KAAK,UAAU;AAAA,IAChC,QAAQ,KAAK,KAAK,KAAK,UAAU;AAAA,EACnC;AACF;AAMO,SAAS,uBAA6B;AAC3C,qBAAmB,MAAM;AAC3B;AAOA,eAAsB,eAAe,SAAyC;AAC5E,QAAM;AAAA,IACJ;AAAA,IACA,aAAa;AAAA,IACb,SAAS;AAAA,IACT,iBAAiB,wBAAwB;AAAA,EAC3C,IAAI;AAGJ,QAAM,iBAAiB,MAAM,QAAQ;AAAA,IACnC,KAAK,IAAI,OAAO,SAAS;AAAA,MACvB,IAAI,IAAI;AAAA,MACR,KAAK,IAAI;AAAA,MACT,KAAK,IAAI;AAAA,MACT,GAAI,MAAM,mBAAmB,KAAK,UAAU;AAAA,IAC9C,EAAE;AAAA,EACJ;AAGA,QAAM,EAAE,OAAO,IAAI,WAAW,cAAc;AAG5C,QAAM,SAAiB,CAAC;AAExB,aAAW,QAAQ,QAAQ;AACzB,UAAM,cAAc,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE;AAErD,QAAI,QAAQ;AAEV,aAAO,KAAK,EAAE,IAAI;AAAA,QAChB,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR;AAAA,MACF;AAGA,UAAI,yBAAyB,eAAe,YAAY,OAAO,YAAY,YAAY,GAAG,GAAG;AAC3F,YAAI;AACF,gBAAM,WAAW,MAAM,gBAAgB,YAAY,KAAK,UAAU;AAElE,iBAAO,OAAO,OAAO,KAAK,EAAE,GAAG,QAAQ;AAAA,QACzC,SAAS,KAAK;AACZ,kBAAQ,KAAK,kCAAkC,KAAK,EAAE,KAAK,GAAG,EAAE;AAAA,QAClE;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAM,MAAM,eAAe,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE;AACvD,UAAI,KAAK;AACP,eAAO,KAAK,EAAE,IAAI;AAAA,UAChB,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,UACb,GAAG,KAAK;AAAA,UACR,GAAG,KAAK;AAAA,UACR;AAAA,UACA,KAAK,IAAI;AAAA,UACT,KAAK,IAAI;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOA,eAAsB,qBAAqB,SAAyC;AAClF,QAAM,EAAE,MAAM,aAAa,GAAG,SAAS,MAAM,IAAI;AAGjD,QAAM,SAAS,oBAAI,IAAsB;AAEzC,aAAW,OAAO,MAAM;AACtB,UAAM,aAAa,IAAI,OAAO,IAAI,MAAM,SAAS,QAAQ;AACzD,QAAI,CAAC,UAAW;AAChB,UAAM,WAAW,OAAO,IAAI,SAAS;AACrC,QAAI,UAAU;AACZ,eAAS,KAAK,IAAI,EAAE;AAAA,IACtB,OAAO;AACL,aAAO,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC;AAAA,IAChC;AAAA,EACF;AAGA,QAAM,aAA4B,CAAC;AACnC,QAAM,YAAY,oBAAI,IAAoB;AAE1C,aAAW,CAAC,WAAW,GAAG,KAAK,OAAO,QAAQ,GAAG;AAC/C,UAAM,YAAY,IAAI,CAAC;AACvB,UAAM,SAAS,OAAO,KAAK,WAAW,QAAQ;AAC9C,UAAM,cAAc,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS;AAEvD,QAAI,YAAY,KAAK;AACnB,iBAAW,KAAK,EAAE,IAAI,WAAW,KAAK,OAAO,CAAC;AAAA,IAChD,OAAO;AACL,iBAAW,KAAK,EAAE,IAAI,WAAW,KAAK,OAAO,CAAC;AAAA,IAChD;AAGA,eAAW,MAAM,KAAK;AACpB,gBAAU,IAAI,IAAI,SAAS;AAAA,IAC7B;AAAA,EACF;AAGA,QAAM,aAAa,MAAM,eAAe;AAAA,IACtC,MAAM;AAAA,IACN;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAGD,QAAM,SAAiB,CAAC;AAExB,aAAW,OAAO,MAAM;AACtB,UAAM,YAAY,UAAU,IAAI,IAAI,EAAE;AACtC,QAAI,aAAa,WAAW,SAAS,GAAG;AACtC,YAAM,WAAW,WAAW,SAAS;AACrC,aAAO,IAAI,EAAE,IAAI,SACb;AAAA,QACE,OAAO,SAAS;AAAA,QAChB,QAAQ,SAAS;AAAA,QACjB,GAAG,SAAS;AAAA,QACZ,GAAG,SAAS;AAAA,QACZ;AAAA,MACF,IACA;AAAA,IACN;AAAA,EACF;AAEA,SAAO;AACT;;;AGnYA,OAAO,WAAW;AAClB,SAAS,cAAc,aAAAC,kBAAqC;AAc5D,eAAe,YACb,aACA,OACA,KACA,GACA,GACA,OACA,QACe;AACf,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI;AAGJ,QAAI,OAAO;AAET,YAAM;AAAA,IACR,OAAO;AAEL,YAAM,YAAY,YAAY,SAAS,MAAM;AAC7C,YAAM,aAAa,6BAA6B,OAAO,KAAK,SAAS,EAAE,SAAS,QAAQ,CAAC;AACzF,YAAM;AAAA,IACR;AAEA,IAAAA,WAAU,GAAG,EACV,KAAK,CAAC,UAAU;AACf,UAAI,UAAU,OAAO,GAAG,GAAG,OAAO,MAAM;AACxC,cAAQ;AAAA,IACV,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,aAAO,IAAI,MAAM,yBAAyB,KAAK,EAAE,CAAC;AAAA,IACpD,CAAC;AAAA,EACL,CAAC;AACH;AAOA,SAAS,oBAAoB,QAAmD;AAC9E,MAAI,OAAO;AACX,MAAI,OAAO;AAEX,aAAW,QAAQ,OAAO,OAAO,MAAM,GAAG;AACxC,WAAO,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,KAAK;AACzC,WAAO,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB;AACF;AAQA,eAAsB,cAAc,QAAiC;AAEnE,QAAM,aAAa,OAAO,KAAK,MAAM;AACrC,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAGA,QAAM,EAAE,OAAO,OAAO,IAAI,oBAAoB,MAAM;AAGpD,MAAI,UAAU,KAAK,WAAW,GAAG;AAC/B,UAAM,IAAI,MAAM,oEAAoE;AAAA,EACtF;AAGA,QAAM,SAAS,aAAa,OAAO,MAAM;AACzC,QAAM,MAAM,OAAO,WAAW,IAAI;AAGlC,MAAI,UAAU,GAAG,GAAG,OAAO,MAAM;AAGjC,QAAM,iBAAkC,CAAC;AAEzC,aAAW,CAAC,IAAI,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AAE/C,UAAM,MAAO,KAAa;AAC1B,UAAM,MAAO,KAAa;AAE1B,QAAI,CAAC,OAAO,CAAC,KAAK;AAChB,cAAQ,KAAK,+BAA+B,EAAE,gBAAgB;AAC9D;AAAA,IACF;AAEA,UAAM,cAAc,OAAO;AAC3B,UAAM,QAAQ,CAAC,CAAC;AAEhB,UAAM,UAAU,YAAY,aAAa,OAAO,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,MAAM;AAE5F,mBAAe,KAAK,OAAO;AAAA,EAC7B;AAGA,QAAM,QAAQ,IAAI,cAAc;AAIhC,SAAO,OAAO,SAAS,WAAW;AACpC;AAmCA,eAAsB,uBACpB,QACA,UAA2B,CAAC,GACX;AACjB,QAAM,EAAE,SAAS,KAAK,UAAU,IAAI,UAAU,KAAK,IAAI;AAGvD,QAAM,cAAc,MAAM,cAAc,MAAM;AAG9C,QAAM,YAAY,MAAM,MAAM,WAAW,EACtC,IAAI;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA;AAAA,IAClB,mBAAmB;AAAA,IACnB,OAAO;AAAA,EACT,CAAC,EACA,SAAS;AAEZ,SAAO;AACT;;;AC9LA,OAAO,QAAQ;AACf,OAAO,UAAU;AAQjB,eAAsB,aAAa,KAAgC;AACjE,QAAM,UAAU,MAAM,GAAG,QAAQ,GAAG;AACpC,SAAO,QACJ,OAAO,CAAC,UAAU,CAAC,MAAM,WAAW,GAAG,MAAM,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,MAAM,EAAE,EAC9F,IAAI,CAAC,UAAU,KAAK,KAAK,KAAK,KAAK,CAAC;AACzC;AAOA,eAAsB,SAAS,UAAwC;AACrE,QAAM,SAAS,MAAM,GAAG,SAAS,QAAQ;AACzC,QAAM,MAAM,KAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,QAAM,KAAK,KAAK,SAAS,QAAQ,EAAE,QAAQ,iBAAiB,EAAE;AAE9D,MAAI,QAAQ,QAAQ;AAClB,WAAO,EAAE,KAAK,QAAQ,GAAG;AAAA,EAC3B,WAAW,QAAQ,QAAQ;AACzB,WAAO,EAAE,KAAK,QAAQ,GAAG;AAAA,EAC3B,OAAO;AACL,UAAM,IAAI,MAAM,4BAA4B,GAAG,EAAE;AAAA,EACnD;AACF;AAQO,SAAS,SAAS,GAAgB,GAAwB;AAC/D,SAAO,EAAE,KAAK,EAAE,KAAK,IAAI;AAC3B;;;ALFA,eAAe,UAAU,UAAiC;AACxD,QAAM,MAAMC,MAAK,QAAQ,QAAQ;AACjC,MAAI;AACF,UAAMC,IAAG,OAAO,GAAG;AAAA,EACrB,QAAQ;AACN,UAAMA,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACzC;AACF;AAQA,eAAe,WAAW,SAAiB,QAAgB,SAAS,MAAqB;AACvF,QAAM,WAAW,GAAG,OAAO;AAC3B,QAAM,UAAU,QAAQ;AACxB,QAAM,kBAAkB,SACpB,UAAU,QAAQ,EAAE,OAAO,KAAK,CAAC,KAAK,OACtC,KAAK,UAAU,MAAM;AACzB,QAAMA,IAAG,UAAU,UAAU,iBAAiB,MAAM;AACtD;AAOA,eAAe,UAAU,SAAiB,QAA+B;AACvE,QAAM,UAAU,GAAG,OAAO;AAC1B,QAAM,UAAU,OAAO;AACvB,QAAM,QAAQ,MAAM,cAAc,MAAM;AACxC,QAAMA,IAAG,UAAU,SAAS,KAAK;AACnC;AAUA,eAAe,aACb,SACA,OACA,QACA,QACiB;AACjB,QAAM,YAAY,SAAS,uBAAuB;AAClD,SAAO,MAAM,UAAU,EAAE,MAAM,SAAS,YAAY,OAAO,OAAO,CAAC;AACrE;AAiCA,eAAsB,eAAe,SAA+C;AAClF,QAAM,EAAE,QAAQ,OAAO,QAAQ,GAAG,SAAS,OAAO,SAAS,KAAK,IAAI;AAGpE,MAAI,UAAU;AACd,MAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,UAAM,cAAc,UAAU,IAAI,WAAW,UAAU,KAAK;AAC5D,cAAU,UAAU;AAAA,EACtB;AAGA,QAAM,YAAY,MAAM,aAAa,KAAK;AAC1C,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,IAAI,MAAM,2CAA2C,KAAK,EAAE;AAAA,EACpE;AAEA,QAAM,UAAU,MAAM,QAAQ,IAAI,UAAU,IAAI,CAAC,SAAS,SAAS,IAAI,CAAC,CAAC;AAGzE,UAAQ,KAAK,QAAQ;AAGrB,QAAM,gBAAgB,MAAM,aAAa,SAAS,OAAO,QAAQ,MAAM;AACvE,QAAM,iBAAiB,MAAM,aAAa,SAAS,OAAO,OAAO,MAAM;AAEvE,QAAM,QAAQ,IAAI;AAAA,IAChB,WAAW,SAAS,eAAe,MAAM;AAAA,IACzC,UAAU,SAAS,cAAc;AAAA,EACnC,CAAC;AAED,SAAO;AAAA,IACL,UAAU,GAAG,OAAO;AAAA,IACpB,SAAS,GAAG,OAAO;AAAA,IACnB,QAAQ;AAAA,IACR,YAAY,QAAQ;AAAA,EACtB;AACF;AAsBA,eAAsB,uBACpB,OACA,QACA,SAAmB,CAAC,GAAG,CAAC,GACxB,SAAS,OACgB;AACzB,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,OAAO,IAAI,CAAC,UAAU;AACpB,YAAM,aAAa,UAAU,IAAI,SAAS,GAAG,MAAM,IAAI,KAAK;AAC5D,aAAO,eAAe;AAAA,QACpB,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAwBA,eAAsB,0BACpB,SACA,QACA,UAAkE,CAAC,GAC5C;AACvB,QAAM,EAAE,QAAQ,GAAG,SAAS,OAAO,SAAS,KAAK,IAAI;AAErD,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AAGA,UAAQ,KAAK,QAAQ;AAGrB,MAAI,UAAU;AACd,MAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,UAAM,cAAc,UAAU,IAAI,WAAW,UAAU,KAAK;AAC5D,cAAU,UAAU;AAAA,EACtB;AAGA,QAAM,gBAAgB,MAAM,aAAa,SAAS,OAAO,QAAQ,MAAM;AACvE,QAAM,iBAAiB,MAAM,aAAa,SAAS,OAAO,OAAO,MAAM;AAEvE,QAAM,QAAQ,IAAI;AAAA,IAChB,WAAW,SAAS,eAAe,MAAM;AAAA,IACzC,UAAU,SAAS,cAAc;AAAA,EACnC,CAAC;AAED,SAAO;AAAA,IACL,UAAU,GAAG,OAAO;AAAA,IACpB,SAAS,GAAG,OAAO;AAAA,IACnB,QAAQ;AAAA,IACR,YAAY,QAAQ;AAAA,EACtB;AACF;","names":["fs","path","DOMParser","path","loadImage","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/sprite.ts","../src/core/layout.ts","../src/core/shelf-pack.ts","../src/core/metadata.ts","../src/core/image.ts","../src/core/svg-styles.ts","../src/core/colors.ts","../src/utils.ts"],"sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\nimport stringify from 'json-stable-stringify';\nimport { generateLayout, generateLayoutUnique, generateImage } from './core/index.js';\nimport { getFilePaths, loadFile, sortById } from './utils.js';\nimport type { ImageBuffer, Layout } from './types.js';\n\n/**\n * 雪碧图生成选项\n */\nexport interface SpriteOptions {\n /** 输出文件路径(不包含扩展名) */\n output: string;\n /** 输入目录路径 */\n input: string;\n /** 像素比率 (默认: 1) */\n ratio?: number;\n /** 是否使用 unique 模式 (默认: false) */\n unique?: boolean;\n /** 是否格式化 JSON 输出 (默认: true) */\n format?: boolean;\n}\n\n/**\n * 雪碧图生成结果\n */\nexport interface SpriteResult {\n /** JSON 文件路径 */\n jsonPath: string;\n /** PNG 文件路径 */\n pngPath: string;\n /** 布局对象 */\n layout: Layout;\n /** 生成的图片数量 */\n imageCount: number;\n}\n\n/**\n * 确保目录存在\n * @param filePath 文件路径\n */\nasync function ensureDir(filePath: string): Promise<void> {\n const dir = path.dirname(filePath);\n try {\n await fs.access(dir);\n } catch {\n await fs.mkdir(dir, { recursive: true });\n }\n}\n\n/**\n * 保存布局 JSON 文件\n * @param outfile 输出文件路径\n * @param layout 布局对象\n * @param format 是否格式化\n */\nasync function saveLayout(outfile: string, layout: Layout, format = true): Promise<void> {\n const jsonPath = `${outfile}.json`;\n await ensureDir(jsonPath);\n const formattedLayout = format\n ? stringify(layout, { space: ' ' }) || '{}'\n : JSON.stringify(layout);\n await fs.writeFile(jsonPath, formattedLayout, 'utf8');\n}\n\n/**\n * 保存雪碧图图片\n * @param outfile 输出文件路径\n * @param layout 布局对象\n */\nasync function saveImage(outfile: string, layout: Layout): Promise<void> {\n const pngPath = `${outfile}.png`;\n await ensureDir(pngPath);\n const image = await generateImage(layout);\n await fs.writeFile(pngPath, image);\n}\n\n/**\n * 生成布局\n * @param buffers 图片缓冲区数组\n * @param ratio 像素比率\n * @param format 是否格式化\n * @param unique 是否使用 unique 模式\n * @returns 布局对象\n */\nasync function createLayout(\n buffers: ImageBuffer[],\n ratio: number,\n format: boolean,\n unique: boolean,\n): Promise<Layout> {\n const genLayout = unique ? generateLayoutUnique : generateLayout;\n return await genLayout({ imgs: buffers, pixelRatio: ratio, format });\n}\n\n/**\n * 生成雪碧图\n *\n * @param options 生成选项\n * @returns Promise<SpriteResult> 生成结果\n *\n * @example\n * ```typescript\n * import { generateSprite } from '@sakitam-gis/sprite-cli';\n *\n * // 生成 1x 雪碧图\n * const result = await generateSprite({\n * output: 'output/sprite',\n * input: 'icons/',\n * });\n *\n * // 生成 2x 雪碧图\n * const result2x = await generateSprite({\n * output: 'output/sprite@2x',\n * input: 'icons/',\n * ratio: 2,\n * });\n *\n * // 使用 unique 模式\n * const resultUnique = await generateSprite({\n * output: 'output/sprite-unique',\n * input: 'icons/',\n * unique: true,\n * });\n * ```\n */\nexport async function generateSprite(options: SpriteOptions): Promise<SpriteResult> {\n const { output, input, ratio = 1, unique = false, format = true } = options;\n\n // 规范化输出路径\n let outfile = output;\n if (outfile.endsWith('/')) {\n const defaultName = ratio === 1 ? 'sprite' : `sprite@${ratio}x`;\n outfile = outfile + defaultName;\n }\n\n // 加载所有 SVG/PNG 文件\n const filePaths = await getFilePaths(input);\n if (filePaths.length === 0) {\n throw new Error(`No SVG or PNG files found in directory: ${input}`);\n }\n\n const buffers = await Promise.all(filePaths.map((file) => loadFile(file)));\n\n // 排序\n buffers.sort(sortById);\n\n // 生成并保存布局和图片\n const layoutForJson = await createLayout(buffers, ratio, format, unique);\n const layoutForImage = await createLayout(buffers, ratio, false, unique);\n\n await Promise.all([\n saveLayout(outfile, layoutForJson, format),\n saveImage(outfile, layoutForImage),\n ]);\n\n return {\n jsonPath: `${outfile}.json`,\n pngPath: `${outfile}.png`,\n layout: layoutForJson,\n imageCount: buffers.length,\n };\n}\n\n/**\n * 批量生成多个比例的雪碧图\n *\n * @param input 输入目录\n * @param output 输出文件路径(不包含扩展名)\n * @param ratios 像素比率数组 (默认: [1, 2])\n * @param unique 是否使用 unique 模式 (默认: false)\n * @returns Promise<SpriteResult[]> 生成结果数组\n *\n * @example\n * ```typescript\n * import { generateSpriteMultiple } from '@sakitam-gis/sprite-cli';\n *\n * // 生成 1x 和 2x 两个版本\n * const results = await generateSpriteMultiple('icons/', 'output/sprite');\n *\n * // 生成 1x、2x、3x 三个版本\n * const results2 = await generateSpriteMultiple('icons/', 'output/sprite', [1, 2, 3]);\n * ```\n */\nexport async function generateSpriteMultiple(\n input: string,\n output: string,\n ratios: number[] = [1, 2],\n unique = false,\n): Promise<SpriteResult[]> {\n const results = await Promise.all(\n ratios.map((ratio) => {\n const outputPath = ratio === 1 ? output : `${output}@${ratio}x`;\n return generateSprite({\n output: outputPath,\n input,\n ratio,\n unique,\n });\n }),\n );\n\n return results;\n}\n\n/**\n * 从已加载的图片缓冲区生成雪碧图\n * 适用于需要自定义图片加载逻辑的场景\n *\n * @param buffers 图片缓冲区数组\n * @param output 输出文件路径(不包含扩展名)\n * @param options 额外选项\n * @returns Promise<SpriteResult> 生成结果\n *\n * @example\n * ```typescript\n * import { generateSpriteFromBuffers } from '@sakitam-gis/sprite-cli';\n * import fs from 'fs/promises';\n *\n * const buffers = [\n * { id: 'icon1', svg: await fs.readFile('icon1.svg') },\n * { id: 'icon2', svg: await fs.readFile('icon2.svg') },\n * ];\n *\n * const result = await generateSpriteFromBuffers(buffers, 'output/sprite');\n * ```\n */\nexport async function generateSpriteFromBuffers(\n buffers: ImageBuffer[],\n output: string,\n options: { ratio?: number; unique?: boolean; format?: boolean } = {},\n): Promise<SpriteResult> {\n const { ratio = 1, unique = false, format = true } = options;\n\n if (buffers.length === 0) {\n throw new Error('No image buffers provided');\n }\n\n // 排序\n buffers.sort(sortById);\n\n // 规范化输出路径\n let outfile = output;\n if (outfile.endsWith('/')) {\n const defaultName = ratio === 1 ? 'sprite' : `sprite@${ratio}x`;\n outfile = outfile + defaultName;\n }\n\n // 生成并保存布局和图片\n const layoutForJson = await createLayout(buffers, ratio, format, unique);\n const layoutForImage = await createLayout(buffers, ratio, false, unique);\n\n await Promise.all([\n saveLayout(outfile, layoutForJson, format),\n saveImage(outfile, layoutForImage),\n ]);\n\n return {\n jsonPath: `${outfile}.json`,\n pngPath: `${outfile}.png`,\n layout: layoutForJson,\n imageCount: buffers.length,\n };\n}\n","/**\n * Sprite layout generation\n * Migrated from @mapbox/spritezero with TypeScript enhancements\n */\n\nimport { DOMParser } from '@xmldom/xmldom';\nimport { loadImage } from '@napi-rs/canvas';\nimport ShelfPack from './shelf-pack.js';\nimport { extractMetadata, hasMetadata } from './metadata.js';\nimport type { ImageBuffer, Layout, LayoutOptions } from '../types.js';\n\n/**\n * Pack rectangles using shelf packing algorithm\n */\ninterface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\ninterface PackedImage extends Rect {\n id: string;\n}\n\n/**\n * 稳定排序比较器:按高度降序,相同高度按 ID 字母序\n * 确保输出的可预测性和稳定性\n *\n * @param a - 第一个图像\n * @param b - 第二个图像\n * @returns 排序结果\n */\nfunction heightAndIdComparator(\n a: { id: string; height: number },\n b: { id: string; height: number },\n): number {\n // 首先按高度降序排序\n const heightDiff = b.height - a.height;\n if (heightDiff !== 0) {\n return heightDiff;\n }\n\n // 高度相同时,按 ID 字母序排序(确保稳定性)\n return a.id.localeCompare(b.id);\n}\n\n/**\n * Shelf Best Height Fit 打包算法\n *\n * 使用 ShelfPack 算法进行 2D 矩形打包:\n * 1. 首先对图像进行排序(高度降序 + ID字母序)\n * 2. 使用 Shelf Best Height Fit 算法寻找最佳位置\n * 3. 自动调整 sprite 尺寸以适应所有图像\n * 4. 最后缩小到实际使用的尺寸\n *\n * 空间利用率:80-95%(比简单货架算法提升 10-20%)\n *\n * 算法特点:\n * - 重用空闲区域(当 bin 被释放后)\n * - 最小化浪费空间(选择最合适的货架)\n * - 支持自动扩展\n *\n * @param images - 带有尺寸信息的图像数组\n * @returns 打包结果,包含每个图像的位置和雪碧图总尺寸\n *\n * @example\n * ```ts\n * const result = packImages([\n * { id: 'icon-a', width: 24, height: 24 },\n * { id: 'icon-b', width: 18, height: 18 }\n * ]);\n * // result.packed = [\n * // { id: 'icon-a', x: 0, y: 0, w: 24, h: 24 },\n * // { id: 'icon-b', x: 24, y: 0, w: 18, h: 18 }\n * // ]\n * // result.width = 42, result.height = 24\n * ```\n */\nfunction packImages(images: Array<{ id: string; width: number; height: number }>): {\n packed: PackedImage[];\n width: number;\n height: number;\n} {\n // 使用稳定排序:高度降序 + ID 字母序\n // 这确保大图标优先放置,减少空间浪费\n const sorted: ((typeof images)[number] & { x?: number; y?: number })[] = [...images].sort(\n heightAndIdComparator,\n );\n\n // 创建 ShelfPack 实例,初始尺寸 1x1,自动调整大小\n const sprite = new ShelfPack(1, 1, { autoResize: true });\n\n // 批量打包,使用 inPlace 模式直接修改对象\n sprite.pack(sorted, { inPlace: true });\n\n // 转换为我们的 PackedImage 格式\n const packed: PackedImage[] = sorted.map((img) => ({\n id: img.id,\n x: img.x!,\n y: img.y!,\n w: img.width,\n h: img.height,\n }));\n\n return {\n packed,\n width: sprite.w,\n height: sprite.h,\n };\n}\n\n/**\n * SVG 尺寸解析缓存\n * 键: SVG Buffer 的 base64 字符串\n * 值: 原始尺寸 (未缩放)\n *\n * 优化:避免重复解析相同的 SVG\n * 适用场景:unique 模式或多个像素比率生成\n */\nconst svgDimensionsCache = new Map<string, { width: number; height: number }>();\n\n/**\n * Calculate image dimensions from SVG or PNG\n *\n * 性能优化:\n * 1. SVG 解析结果缓存(避免重复解析)\n * 2. 早期返回(PNG 已知尺寸)\n * 3. 延迟 canvas 导入(仅在需要时加载)\n *\n * @param img - Image buffer\n * @param pixelRatio - Pixel ratio for scaling\n * @param useCache - 是否使用缓存 (默认 true)\n * @returns Image dimensions\n */\nasync function getImageDimensions(\n img: ImageBuffer,\n pixelRatio: number,\n useCache: boolean = true,\n): Promise<{ width: number; height: number }> {\n // If dimensions are already provided (PNG), use them\n // 快速路径:PNG 已知尺寸,直接返回\n if (img.width && img.height) {\n return {\n width: Math.ceil(img.width * pixelRatio),\n height: Math.ceil(img.height * pixelRatio),\n };\n }\n\n // For PNG, extract dimensions from buffer\n if (img.png) {\n return new Promise((resolve, reject) => {\n loadImage(img.png!)\n .then((image) => {\n resolve({\n width: Math.ceil(image.width * pixelRatio),\n height: Math.ceil(image.height * pixelRatio),\n });\n })\n .catch(reject);\n });\n }\n\n // For SVG, parse dimensions from XML\n if (img.svg) {\n // 尝试从缓存获取\n let cachedDims: { width: number; height: number } | undefined;\n let cacheKey: string | undefined;\n\n if (useCache) {\n cacheKey = img.svg.toString('base64');\n cachedDims = svgDimensionsCache.get(cacheKey);\n\n if (cachedDims) {\n // 缓存命中,直接返回缩放后的尺寸\n return {\n width: Math.ceil(cachedDims.width * pixelRatio),\n height: Math.ceil(cachedDims.height * pixelRatio),\n };\n }\n }\n\n try {\n const svgString = img.svg.toString('utf8');\n const doc = new DOMParser().parseFromString(svgString, 'image/svg+xml');\n const svgElement = doc.documentElement;\n\n // 检查解析错误\n const parseError = doc.getElementsByTagName('parsererror');\n if (parseError && parseError.length > 0) {\n throw new Error(`Failed to parse SVG for ${img.id}: Invalid XML`);\n }\n\n let width = 0;\n let height = 0;\n\n // Try to get width/height attributes\n const widthAttr = svgElement.getAttribute('width');\n const heightAttr = svgElement.getAttribute('height');\n\n if (widthAttr && heightAttr) {\n width = parseFloat(widthAttr);\n height = parseFloat(heightAttr);\n } else {\n // Try viewBox\n const viewBox = svgElement.getAttribute('viewBox');\n if (viewBox) {\n const parts = viewBox.split(/\\s+/);\n if (parts.length === 4) {\n width = parseFloat(parts[2]);\n height = parseFloat(parts[3]);\n }\n }\n }\n\n // Default to 24x24 if no dimensions found\n if (!width || !height) {\n console.warn(`No dimensions found for ${img.id}, using default 24x24`);\n width = 24;\n height = 24;\n }\n\n // 验证尺寸有效性\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {\n throw new Error(`Invalid dimensions for ${img.id}: ${width}x${height}`);\n }\n\n // 缓存原始尺寸(未缩放)\n if (useCache && cacheKey) {\n svgDimensionsCache.set(cacheKey, { width, height });\n }\n\n return {\n width: Math.ceil(width * pixelRatio),\n height: Math.ceil(height * pixelRatio),\n };\n } catch (err) {\n throw new Error(`Failed to process SVG ${img.id}: ${String(err)}`);\n }\n }\n\n // Fallback\n return {\n width: Math.ceil(24 * pixelRatio),\n height: Math.ceil(24 * pixelRatio),\n };\n}\n\n/**\n * 清除 SVG 尺寸缓存\n * 用于测试或内存管理\n */\nexport function clearDimensionsCache(): void {\n svgDimensionsCache.clear();\n}\n\n/**\n * Generate sprite layout\n * @param options - Layout options\n * @returns Layout object\n */\nexport async function generateLayout(options: LayoutOptions): Promise<Layout> {\n const {\n imgs,\n pixelRatio = 1,\n format = false,\n extractMetadata: shouldExtractMetadata = true,\n } = options;\n\n // Calculate dimensions for all images\n const imagesWithDims = await Promise.all(\n imgs.map(async (img) => ({\n id: img.id,\n svg: img.svg,\n png: img.png,\n ...(await getImageDimensions(img, pixelRatio)),\n })),\n );\n\n // Pack images\n const { packed } = packImages(imagesWithDims);\n\n // Build layout object\n const layout: Layout = {};\n\n for (const item of packed) {\n const originalImg = imgs.find((i) => i.id === item.id);\n\n if (format) {\n // Format for JSON output\n layout[item.id] = {\n width: item.w,\n height: item.h,\n x: item.x,\n y: item.y,\n pixelRatio,\n };\n\n // 提取元数据(仅SVG且包含mapbox标记)\n if (shouldExtractMetadata && originalImg && originalImg.svg && hasMetadata(originalImg.svg)) {\n try {\n const metadata = await extractMetadata(originalImg.svg, pixelRatio);\n // 合并元数据到布局项\n Object.assign(layout[item.id], metadata);\n } catch (err) {\n console.warn(`Failed to extract metadata for ${item.id}: ${String(err)}`);\n }\n }\n } else {\n // Format for image generation (includes image data)\n const img = imagesWithDims.find((i) => i.id === item.id);\n if (img) {\n layout[item.id] = {\n width: item.w,\n height: item.h,\n x: item.x,\n y: item.y,\n pixelRatio,\n svg: img.svg,\n png: img.png,\n };\n }\n }\n }\n\n return layout;\n}\n\n/**\n * Generate unique sprite layout (deduplicates identical images)\n * @param options - Layout options\n * @returns Layout object\n */\nexport async function generateLayoutUnique(options: LayoutOptions): Promise<Layout> {\n const { imgs, pixelRatio = 1, format = false } = options;\n\n // Group identical images (SVG or PNG)\n const imgMap = new Map<string, string[]>();\n\n for (const img of imgs) {\n const imgString = (img.svg || img.png)?.toString('base64');\n if (!imgString) continue;\n const existing = imgMap.get(imgString);\n if (existing) {\n existing.push(img.id);\n } else {\n imgMap.set(imgString, [img.id]);\n }\n }\n\n // Create unique images list\n const uniqueImgs: ImageBuffer[] = [];\n const idMapping = new Map<string, string>();\n\n for (const [imgString, ids] of imgMap.entries()) {\n const primaryId = ids[0];\n const buffer = Buffer.from(imgString, 'base64');\n const originalImg = imgs.find((i) => i.id === primaryId)!;\n\n if (originalImg.svg) {\n uniqueImgs.push({ id: primaryId, svg: buffer });\n } else {\n uniqueImgs.push({ id: primaryId, png: buffer });\n }\n\n // Map all IDs to the primary ID\n for (const id of ids) {\n idMapping.set(id, primaryId);\n }\n }\n\n // Generate layout for unique images\n const baseLayout = await generateLayout({\n imgs: uniqueImgs,\n pixelRatio,\n format: false,\n });\n\n // Expand layout to include all original IDs\n const layout: Layout = {};\n\n for (const img of imgs) {\n const primaryId = idMapping.get(img.id);\n if (primaryId && baseLayout[primaryId]) {\n const baseItem = baseLayout[primaryId];\n layout[img.id] = format\n ? {\n width: baseItem.width,\n height: baseItem.height,\n x: baseItem.x,\n y: baseItem.y,\n pixelRatio,\n }\n : baseItem;\n }\n }\n\n return layout;\n}\n","/**\n * ShelfPack - A 2D bin packing algorithm using Shelf Best Height Fit\n *\n * Based on the algorithm from:\n * http://clb.demon.fi/files/RectangleBinPack.pdf\n *\n * TypeScript port from @mapbox/shelf-pack\n */\n\n/**\n * Bin 对象 - 表示一个已分配的矩形区域\n */\nexport class Bin {\n id: number | string;\n x: number;\n y: number;\n w: number;\n h: number;\n maxw: number;\n maxh: number;\n refcount: number;\n\n /**\n * @param id Unique id of the bin\n * @param x Left coordinate of the bin\n * @param y Top coordinate of the bin\n * @param w Width of the bin\n * @param h Height of the bin\n * @param maxw Max width of the bin (defaults to `w` if not provided)\n * @param maxh Max height of the bin (defaults to `h` if not provided)\n */\n constructor(\n id: number | string,\n x: number,\n y: number,\n w: number,\n h: number,\n maxw?: number,\n maxh?: number,\n ) {\n this.id = id;\n this.x = x;\n this.y = y;\n this.w = w;\n this.h = h;\n this.maxw = maxw || w;\n this.maxh = maxh || h;\n this.refcount = 0;\n }\n}\n\n/**\n * Shelf 对象 - 表示一个水平货架\n */\nclass Shelf {\n x: number;\n y: number;\n w: number;\n h: number;\n free: number;\n\n /**\n * @param y Top coordinate of the new shelf\n * @param w Width of the new shelf\n * @param h Height of the new shelf\n */\n constructor(y: number, w: number, h: number) {\n this.x = 0;\n this.y = y;\n this.w = this.free = w;\n this.h = h;\n }\n\n /**\n * 在货架上分配一个 bin\n *\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique id of the bin to allocate\n * @returns Bin object or null if allocation failed\n */\n alloc(w: number, h: number, id: number | string): Bin | null {\n if (w > this.free || h > this.h) {\n return null;\n }\n const x = this.x;\n this.x += w;\n this.free -= w;\n return new Bin(id, x, this.y, w, h, w, this.h);\n }\n\n /**\n * 调整货架宽度\n *\n * @param w Requested new width of the shelf\n * @returns true\n */\n resize(w: number): boolean {\n this.free += w - this.w;\n this.w = w;\n return true;\n }\n}\n\n/**\n * ShelfPack 配置选项\n */\nexport interface ShelfPackOptions {\n /** If `true`, the sprite will automatically grow */\n autoResize?: boolean;\n}\n\n/**\n * 待打包的项目接口\n */\nexport interface PackItem {\n x?: number;\n y?: number;\n id?: number | string;\n w?: number;\n h?: number;\n width?: number;\n height?: number;\n}\n\n/**\n * 打包选项\n */\nexport interface PackOptions {\n /** If `true`, the supplied bin objects will be updated inplace with `x` and `y` properties */\n inPlace?: boolean;\n}\n\n/**\n * ShelfPack - 2D bin packing allocator\n *\n * Uses the Shelf Best Height Fit algorithm\n *\n * @example\n * ```ts\n * const sprite = new ShelfPack(64, 64, { autoResize: true });\n * const bins = sprite.pack([\n * { id: 'a', w: 12, h: 12 },\n * { id: 'b', w: 12, h: 16 },\n * ], { inPlace: true });\n * ```\n */\nexport default class ShelfPack {\n w: number;\n h: number;\n autoResize: boolean;\n shelves: Shelf[];\n freebins: Bin[];\n stats: Record<number, number>;\n bins: Record<string | number, Bin>;\n maxId: number;\n\n /**\n * @param w Initial width of the sprite\n * @param h Initial height of the sprite\n * @param options Configuration options\n */\n constructor(w: number = 64, h: number = 64, options: ShelfPackOptions = {}) {\n this.w = w;\n this.h = h;\n this.autoResize = !!options.autoResize;\n this.shelves = [];\n this.freebins = [];\n this.stats = {};\n this.bins = {};\n this.maxId = 0;\n }\n\n /**\n * 批量打包多个 bins\n *\n * @param bins Array of requested bins\n * @param options Pack options\n * @returns Array of allocated Bins\n *\n * @example\n * ```ts\n * const bins = [\n * { id: 1, w: 12, h: 12 },\n * { id: 2, w: 12, h: 16 },\n * ];\n * const results = sprite.pack(bins, { inPlace: false });\n * ```\n */\n pack(bins: PackItem | PackItem[], options: PackOptions = {}): Bin[] {\n const binArray = Array.isArray(bins) ? bins : [bins];\n const results: Bin[] = [];\n\n for (let i = 0; i < binArray.length; i++) {\n const w = binArray[i].w || binArray[i].width;\n const h = binArray[i].h || binArray[i].height;\n const id = binArray[i].id;\n\n if (w && h) {\n const allocation = this.packOne(w, h, id);\n if (!allocation) {\n continue;\n }\n if (options.inPlace) {\n binArray[i].x = allocation.x;\n binArray[i].y = allocation.y;\n binArray[i].id = allocation.id;\n }\n results.push(allocation);\n }\n }\n\n this.shrink();\n\n return results;\n }\n\n /**\n * 打包单个 bin\n *\n * 每个 bin 都有唯一标识符。如果未提供 id,将自动创建一个。\n * Bins 自动进行引用计数。\n *\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique identifier for this bin\n * @returns Bin object or null if allocation failed\n *\n * @example\n * ```ts\n * const bin = sprite.packOne(12, 16, 'a');\n * ```\n */\n packOne(w: number, h: number, id?: number | string): Bin | null {\n const best = { freebin: -1, shelf: -1, waste: Infinity };\n let y = 0;\n let bin: Bin | undefined;\n let shelf: Shelf;\n let waste: number;\n let i: number;\n\n // if id was supplied, attempt a lookup..\n if (typeof id === 'string' || typeof id === 'number') {\n bin = this.getBin(id);\n if (bin) {\n // we packed this bin already\n this.ref(bin);\n return bin;\n }\n if (typeof id === 'number') {\n this.maxId = Math.max(id, this.maxId);\n }\n } else {\n id = ++this.maxId;\n }\n\n // First try to reuse a free bin..\n for (i = 0; i < this.freebins.length; i++) {\n bin = this.freebins[i];\n\n // exactly the right height and width, use it..\n if (h === bin.maxh && w === bin.maxw) {\n return this.allocFreebin(i, w, h, id);\n }\n // not enough height or width, skip it..\n if (h > bin.maxh || w > bin.maxw) {\n continue;\n }\n // extra height or width, minimize wasted area..\n if (h <= bin.maxh && w <= bin.maxw) {\n waste = bin.maxw * bin.maxh - w * h;\n if (waste < best.waste) {\n best.waste = waste;\n best.freebin = i;\n }\n }\n }\n\n // Next find the best shelf..\n for (i = 0; i < this.shelves.length; i++) {\n shelf = this.shelves[i];\n y += shelf.h;\n\n // not enough width on this shelf, skip it..\n if (w > shelf.free) {\n continue;\n }\n // exactly the right height, pack it..\n if (h === shelf.h) {\n return this.allocShelf(i, w, h, id);\n }\n // not enough height, skip it..\n if (h > shelf.h) {\n continue;\n }\n // extra height, minimize wasted area..\n if (h < shelf.h) {\n waste = (shelf.h - h) * w;\n if (waste < best.waste) {\n best.freebin = -1;\n best.waste = waste;\n best.shelf = i;\n }\n }\n }\n\n if (best.freebin !== -1) {\n return this.allocFreebin(best.freebin, w, h, id);\n }\n\n if (best.shelf !== -1) {\n return this.allocShelf(best.shelf, w, h, id);\n }\n\n // No free bins or shelves.. add shelf..\n if (h <= this.h - y && w <= this.w) {\n shelf = new Shelf(y, this.w, h);\n return this.allocShelf(this.shelves.push(shelf) - 1, w, h, id);\n }\n\n // No room for more shelves..\n // If `autoResize` option is set, grow the sprite\n if (this.autoResize) {\n const h1 = this.h;\n let h2 = this.h;\n const w1 = this.w;\n let w2 = this.w;\n\n if (w1 <= h1 || w > w1) {\n // grow width..\n w2 = Math.max(w, w1) * 2;\n }\n if (h1 < w1 || h > h1) {\n // grow height..\n h2 = Math.max(h, h1) * 2;\n }\n\n this.resize(w2, h2);\n return this.packOne(w, h, id); // retry\n }\n\n return null;\n }\n\n /**\n * 通过重用现有 freebin 分配一个 bin\n *\n * @param index Index into the `this.freebins` array\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique identifier for this bin\n * @returns Bin object\n */\n private allocFreebin(index: number, w: number, h: number, id: number | string): Bin {\n const bin = this.freebins.splice(index, 1)[0];\n bin.id = id;\n bin.w = w;\n bin.h = h;\n bin.refcount = 0;\n this.bins[id] = bin;\n this.ref(bin);\n return bin;\n }\n\n /**\n * 在现有货架上分配 bin\n *\n * @param index Index into the `this.shelves` array\n * @param w Width of the bin to allocate\n * @param h Height of the bin to allocate\n * @param id Unique identifier for this bin\n * @returns Bin object\n */\n private allocShelf(index: number, w: number, h: number, id: number | string): Bin {\n const shelf = this.shelves[index];\n const bin = shelf.alloc(w, h, id);\n if (bin) {\n this.bins[id] = bin;\n this.ref(bin);\n }\n return bin as Bin;\n }\n\n /**\n * 将 sprite 的宽度/高度缩小到最小\n *\n * 由于 shelf-pack 在空间不足时会先加倍宽度再加倍高度,\n * 如果这发生在打包结束时,可能会导致较大的未使用空间\n */\n shrink(): void {\n if (this.shelves.length > 0) {\n let w2 = 0;\n let h2 = 0;\n\n for (let j = 0; j < this.shelves.length; j++) {\n const shelf = this.shelves[j];\n h2 += shelf.h;\n w2 = Math.max(shelf.w - shelf.free, w2);\n }\n\n this.resize(w2, h2);\n }\n }\n\n /**\n * 根据 id 返回已打包的 bin\n *\n * @param id Unique identifier for this bin\n * @returns The requested bin, or undefined if not found\n *\n * @example\n * ```ts\n * const bin = sprite.getBin('a');\n * ```\n */\n getBin(id: number | string): Bin | undefined {\n return this.bins[id];\n }\n\n /**\n * 增加 bin 的引用计数并更新统计信息\n *\n * @param bin Bin instance\n * @returns New refcount of the bin\n *\n * @example\n * ```ts\n * const bin = sprite.getBin('a');\n * if (bin) sprite.ref(bin);\n * ```\n */\n ref(bin: Bin): number {\n if (++bin.refcount === 1) {\n // a new Bin.. record height in stats histogram..\n const h = bin.h;\n this.stats[h] = (this.stats[h] || 0) + 1;\n }\n\n return bin.refcount;\n }\n\n /**\n * 减少 bin 的引用计数并更新统计信息\n * 当引用计数达到 0 时,bin 将自动标记为空闲空间\n *\n * @param bin Bin instance\n * @returns New refcount of the bin\n *\n * @example\n * ```ts\n * const bin = sprite.getBin('a');\n * if (bin) sprite.unref(bin);\n * ```\n */\n unref(bin: Bin): number {\n if (bin.refcount === 0) {\n return 0;\n }\n\n if (--bin.refcount === 0) {\n this.stats[bin.h]--;\n delete this.bins[bin.id];\n this.freebins.push(bin);\n }\n\n return bin.refcount;\n }\n\n /**\n * 清除 sprite,重置所有内容和统计信息\n *\n * @example\n * ```ts\n * sprite.clear();\n * ```\n */\n clear(): void {\n this.shelves = [];\n this.freebins = [];\n this.stats = {};\n this.bins = {};\n this.maxId = 0;\n }\n\n /**\n * 调整 sprite 尺寸\n *\n * @param w Requested new sprite width\n * @param h Requested new sprite height\n * @returns `true` if resize succeeded\n *\n * @example\n * ```ts\n * sprite.resize(256, 256);\n * ```\n */\n resize(w: number, h: number): boolean {\n this.w = w;\n this.h = h;\n for (let i = 0; i < this.shelves.length; i++) {\n this.shelves[i].resize(w);\n }\n return true;\n }\n}\n","/**\n * SVG Metadata Extraction\n *\n * 支持从SVG中提取Mapbox样式的stretch元数据和placeholder元数据\n * 用于实现9-patch风格的图像拉伸和文本占位符\n *\n * 元数据通过特殊命名的SVG路径标记提取:\n * - id=\"mapbox-stretch-x\" 或 id=\"mapbox-stretch\" : 水平拉伸区域\n * - id=\"mapbox-stretch-y\" 或 id=\"mapbox-stretch\" : 垂直拉伸区域\n * - id=\"mapbox-content\" : 内容区域\n * - id=\"mapbox-text-placeholder\" : 文本占位符区域\n *\n * @see https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/#stretch-metadata\n */\n\nimport { optimize } from 'svgo';\nimport computePathBounds from 'svg-boundings';\nimport type { SpriteMetadata } from '../types.js';\n\nconst metadataPrefix = 'mapbox-';\n\n/**\n * 精确舍入浮点数\n * 从svgo移植,用于减少小数位数\n */\nfunction strongRound(value: number, precision: number = 3): number {\n const error = +Math.pow(0.1, precision).toFixed(precision);\n if (+value.toFixed(precision) !== value) {\n const rounded = +value.toFixed(precision - 1);\n value =\n +Math.abs(rounded - value).toFixed(precision + 1) >= error\n ? +value.toFixed(precision)\n : rounded;\n }\n return value;\n}\n\n/**\n * 从SVG Buffer中提取元数据\n *\n * 使用简化的方法:\n * 1. 使用 svgo 优化 SVG(转换形状为路径等)\n * 2. 使用 DOMParser 解析优化后的 SVG\n * 3. 查找带有 mapbox- 前缀的路径\n * 4. 使用 svg-boundings 计算边界\n * 5. 按像素比率缩放并返回\n *\n * @param svg - SVG文件内容\n * @param pixelRatio - 像素比率,用于缩放边界坐标\n * @returns Promise<SpriteMetadata> 提取的元数据\n */\nexport async function extractMetadata(\n svg: Buffer,\n pixelRatio: number = 1,\n): Promise<SpriteMetadata> {\n const svgString = svg.toString('utf-8');\n\n // 快速检测:如果SVG不包含mapbox-相关标记,直接返回空元数据\n if (!svgString.includes('mapbox-stretch') && !svgString.includes('mapbox-text-placeholder')) {\n return {};\n }\n\n try {\n // 使用 svgo 优化 SVG,转换形状为路径\n const optimized = optimize(svgString, {\n plugins: [\n {\n name: 'convertShapeToPath',\n params: {\n convertArcs: true,\n },\n },\n {\n name: 'convertTransform',\n },\n {\n name: 'cleanupIds',\n params: {\n preservePrefixes: ['mapbox-'],\n },\n },\n ],\n });\n\n // 使用 DOMParser 解析优化后的 SVG\n const { DOMParser } = await import('@xmldom/xmldom');\n const doc = new DOMParser().parseFromString(optimized.data, 'image/svg+xml');\n\n const metadata: SpriteMetadata = {};\n\n // 查找所有带有 mapbox- 前缀的路径\n const paths = doc.getElementsByTagName('path');\n\n for (let i = 0; i < paths.length; i++) {\n const path = paths[i];\n const id = path.getAttribute('id');\n const d = path.getAttribute('d');\n\n if (!id || !id.startsWith(metadataPrefix) || !d) {\n continue;\n }\n\n try {\n // 计算路径边界\n const bounds = computePathBounds.path({ d }, true);\n\n // 验证边界有效性\n if (\n !Number.isFinite(bounds.left) ||\n !Number.isFinite(bounds.right) ||\n !Number.isFinite(bounds.top) ||\n !Number.isFinite(bounds.bottom)\n ) {\n continue;\n }\n\n // 按像素比率缩放边界\n const left = strongRound(bounds.left * pixelRatio);\n const top = strongRound(bounds.top * pixelRatio);\n const right = strongRound(bounds.right * pixelRatio);\n const bottom = strongRound(bounds.bottom * pixelRatio);\n\n const metadataType = id.substring(metadataPrefix.length);\n\n // 根据ID类型添加元数据\n if (metadataType.startsWith('stretch-x') || metadataType === 'stretch-x') {\n metadata.stretchX = metadata.stretchX || [];\n metadata.stretchX.push([left, right]);\n } else if (metadataType.startsWith('stretch-y') || metadataType === 'stretch-y') {\n metadata.stretchY = metadata.stretchY || [];\n metadata.stretchY.push([top, bottom]);\n } else if (metadataType === 'stretch') {\n metadata.stretchX = metadata.stretchX || [];\n metadata.stretchX.push([left, right]);\n metadata.stretchY = metadata.stretchY || [];\n metadata.stretchY.push([top, bottom]);\n } else if (metadataType === 'content') {\n metadata.content = [left, top, right, bottom];\n } else if (metadataType === 'text-placeholder') {\n metadata.placeholder = [left, top, right, bottom];\n }\n } catch (err) {\n console.warn(`Failed to compute bounds for ${id}: ${String(err)}`);\n }\n }\n\n // 排序拉伸区域(升序)\n if (metadata.stretchX) {\n metadata.stretchX.sort((a, b) => a[0] - b[0]);\n }\n if (metadata.stretchY) {\n metadata.stretchY.sort((a, b) => a[0] - b[0]);\n }\n\n return metadata;\n } catch (err) {\n console.error(`Failed to extract metadata: ${String(err)}`);\n return {};\n }\n}\n\n/**\n * 检查SVG是否包含元数据标记\n *\n * @param svg - SVG文件内容\n * @returns boolean 是否包含元数据\n */\nexport function hasMetadata(svg: Buffer): boolean {\n const svgString = svg.toString('utf-8');\n return svgString.includes('mapbox-stretch') || svgString.includes('mapbox-text-placeholder');\n}\n\n/**\n * 示例:元数据提取的测试用例\n *\n * 输入SVG应该包含类似这样的路径:\n * ```xml\n * <svg width=\"24\" height=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\n * <!-- 主图形 -->\n * <rect width=\"24\" height=\"24\" fill=\"#333\"/>\n *\n * <!-- 拉伸元数据:水平和垂直方向都可拉伸 -->\n * <path id=\"mapbox-stretch\" d=\"M 8 0 L 16 0 L 16 24 L 8 24 Z\" fill=\"none\"/>\n *\n * <!-- 内容区域:文本或图标应该放置的区域 -->\n * <path id=\"mapbox-content\" d=\"M 4 4 L 20 4 L 20 20 L 4 20 Z\" fill=\"none\"/>\n * </svg>\n * ```\n *\n * 预期输出(pixelRatio=1):\n * ```json\n * {\n * \"stretchX\": [[8, 16]],\n * \"stretchY\": [[8, 16]],\n * \"content\": [4, 4, 20, 20]\n * }\n * ```\n */\n","/**\n * Sprite image generation\n * Migrated from @mapbox/spritezero with TypeScript enhancements\n *\n * 支持:\n * - 标准PNG输出\n * - 优化PNG输出(颜色量化、压缩)\n */\n\nimport sharp from 'sharp';\nimport { createCanvas, loadImage, type SKRSContext2D } from '@napi-rs/canvas';\nimport { inlineSvgClassStyles } from './svg-styles.js';\nimport { normalizeSvgColors } from './colors.js';\nimport type { Layout } from '../types.js';\n\n/**\n * Render image (SVG or PNG) to canvas context\n * @param imageBuffer - Image buffer (SVG or PNG)\n * @param isPNG - Whether the image is PNG format\n * @param ctx - Canvas 2D context\n * @param x - X position\n * @param y - Y position\n * @param width - Image width\n * @param height - Image height\n * @param pixelRatio - Pixel ratio\n */\nasync function renderImage(\n imageBuffer: Buffer,\n isPNG: boolean,\n ctx: SKRSContext2D,\n x: number,\n y: number,\n width: number,\n height: number,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let src: Buffer | string;\n\n // Set image data as source\n if (isPNG) {\n // PNG: use buffer directly\n src = imageBuffer;\n } else {\n // SVG: normalize colors and convert to data URL\n const svgString = normalizeSvgColors(inlineSvgClassStyles(imageBuffer.toString('utf8')));\n const svgDataUrl = `data:image/svg+xml;base64,${Buffer.from(svgString).toString('base64')}`;\n src = svgDataUrl;\n }\n\n loadImage(src)\n .then((image) => {\n ctx.drawImage(image, x, y, width, height);\n resolve();\n })\n .catch((error) => {\n reject(new Error(`Failed to load image: ${error}`));\n });\n });\n}\n\n/**\n * Calculate canvas dimensions from layout\n * @param layout - Sprite layout\n * @returns Canvas dimensions\n */\nfunction getCanvasDimensions(layout: Layout): { width: number; height: number } {\n let maxX = 0;\n let maxY = 0;\n\n for (const item of Object.values(layout)) {\n maxX = Math.max(maxX, item.x + item.width);\n maxY = Math.max(maxY, item.y + item.height);\n }\n\n return {\n width: maxX || 1,\n height: maxY || 1,\n };\n}\n\n/**\n * Generate sprite image from layout\n * @param layout - Sprite layout (must include SVG data)\n * @returns PNG image buffer\n * @throws Error if layout is empty or invalid\n */\nexport async function generateImage(layout: Layout): Promise<Buffer> {\n // 检查空布局\n const layoutKeys = Object.keys(layout);\n if (layoutKeys.length === 0) {\n throw new Error('Cannot generate image from empty layout');\n }\n\n // Calculate canvas size\n const { width, height } = getCanvasDimensions(layout);\n\n // 验证画布尺寸\n if (width === 0 || height === 0) {\n throw new Error('Invalid canvas dimensions: width and height must be greater than 0');\n }\n\n // Create canvas\n const canvas = createCanvas(width, height);\n const ctx = canvas.getContext('2d');\n\n // Clear canvas\n ctx.clearRect(0, 0, width, height);\n\n // Render all images\n const renderPromises: Promise<void>[] = [];\n\n // Unique layouts share the same item object across aliases.\n const rendered = new Set<Layout[string]>();\n for (const [id, item] of Object.entries(layout)) {\n if (rendered.has(item)) continue;\n rendered.add(item);\n\n // Check if image data is available (SVG or PNG)\n const svg = item.svg;\n const png = item.png;\n\n if (!svg && !png) {\n console.warn(`Warning: No image data for '${id}', skipping...`);\n continue;\n }\n\n const imageBuffer = svg || png!;\n const isPNG = !!png;\n\n const promise = renderImage(imageBuffer, isPNG, ctx, item.x, item.y, item.width, item.height);\n\n renderPromises.push(promise);\n }\n\n // Wait for all images to render\n await Promise.all(renderPromises);\n\n // Convert canvas to PNG buffer\n // @napi-rs/canvas uses toBuffer(mime) instead of toBuffer(format)\n return canvas.toBuffer('image/png');\n}\n\n/**\n * PNG 优化选项\n */\nexport interface OptimizeOptions {\n /** 颜色数量 (default: 256) */\n colors?: number;\n /** 压缩质量 0-100 (default: 80) */\n quality?: number;\n /** 是否使用调色板模式 (default: true) */\n palette?: boolean;\n}\n\n/**\n * 生成优化的PNG雪碧图\n *\n * 使用 sharp 进行 PNG 优化:\n * - 颜色量化(减少到指定颜色数)\n * - PNG 压缩优化\n * - 减小文件大小 30-50%\n *\n * @param layout - Sprite layout\n * @param options - 优化选项\n * @returns 优化后的PNG buffer\n *\n * @example\n * ```ts\n * const optimizedPng = await generateOptimizedImage(layout, {\n * colors: 256,\n * quality: 80,\n * palette: true\n * });\n * ```\n */\nexport async function generateOptimizedImage(\n layout: Layout,\n options: OptimizeOptions = {},\n): Promise<Buffer> {\n const { colors = 256, quality = 80, palette = true } = options;\n\n // 首先生成标准 PNG\n const standardPng = await generateImage(layout);\n\n // 使用 sharp 优化\n const optimized = await sharp(standardPng)\n .png({\n palette: palette,\n quality: quality,\n colors: colors,\n compressionLevel: 9, // 最高压缩级别\n adaptiveFiltering: true,\n force: true,\n })\n .toBuffer();\n\n return optimized;\n}\n","import { DOMParser, XMLSerializer } from '@xmldom/xmldom';\nimport postcss, { type Declaration, type Root } from 'postcss';\n\nconst PRESENTATION = new Set([\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'stroke',\n 'stroke-width',\n 'stroke-opacity',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'opacity',\n 'color',\n 'stop-color',\n 'stop-opacity',\n 'clip-rule',\n]);\n\n/** Parse one style block independently so malformed CSS cannot discard other blocks. */\nfunction parseCss(css: string): Root | undefined {\n try {\n return postcss.parse(css);\n } catch (error) {\n if (error instanceof postcss.CssSyntaxError) return undefined;\n throw error;\n }\n}\n\ninterface RankedValue {\n value: string;\n important: boolean;\n order: number;\n}\n\ntype Properties = Map<string, RankedValue>;\n\n/** Keep one winner per property using importance followed by stylesheet order. */\nfunction select(target: Properties, prop: string, next: RankedValue): void {\n const prev = target.get(prop);\n if (!prev || (next.important !== prev.important ? next.important : next.order >= prev.order)) {\n target.set(prop, next);\n }\n}\n\n/** Index supported top-level class rules once per document. */\nfunction collectRules(doc: Document): Map<string, Properties> {\n const byClass = new Map<string, Properties>();\n let order = 0;\n for (const style of Array.from(doc.getElementsByTagNameNS('*', 'style'))) {\n if (style.namespaceURI !== doc.documentElement.namespaceURI) continue;\n const type = style.getAttribute('type')?.trim().toLowerCase();\n const media = style.getAttribute('media')?.trim().toLowerCase();\n if ((type && type !== 'text/css') || (media && media !== 'all')) continue;\n for (const rule of parseCss(style.textContent || '')?.nodes ?? []) {\n if (rule.type !== 'rule') continue;\n const properties: Properties = new Map();\n for (const decl of rule.nodes) {\n if (decl.type !== 'decl') continue;\n const prop = decl.prop.toLowerCase();\n if (PRESENTATION.has(prop)) {\n select(properties, prop, {\n value: decl.value,\n important: !!decl.important,\n order: order++,\n });\n }\n }\n for (const selector of new Set(rule.selectors)) {\n if (!/^\\.[\\w-]+$/.test(selector)) continue;\n const name = selector.slice(1);\n const declarations = byClass.get(name) ?? new Map<string, RankedValue>();\n for (const [prop, value] of properties) select(declarations, prop, value);\n byClass.set(name, declarations);\n }\n }\n }\n return byClass;\n}\n\n/** Remove priority syntax only after resolving competing inline declarations. */\nfunction simplifyImportant(style: Root): boolean {\n const winners = new Map<string, Declaration>();\n for (const decl of style.nodes) {\n if (decl.type === 'decl' && decl.important && PRESENTATION.has(decl.prop.toLowerCase())) {\n winners.set(decl.prop.toLowerCase(), decl);\n }\n }\n if (!winners.size) return false;\n style.walkDecls((decl) => {\n const winner = winners.get(decl.prop.toLowerCase());\n if (winner === decl) decl.important = false;\n else if (winner) decl.remove();\n });\n return true;\n}\n\n/** Materialize simple class rules for rasterization while preserving authored paint. */\nexport function inlineSvgClassStyles(svg: string): string {\n if (!/<(?:[^\\s<>:]+:)?style[\\s>]/.test(svg) && !/!\\s*important/i.test(svg)) return svg;\n const doc = new DOMParser().parseFromString(svg, 'image/svg+xml');\n const byClass = collectRules(doc);\n let changed = false;\n for (const element of Array.from(doc.getElementsByTagName('*'))) {\n if (element.namespaceURI !== doc.documentElement.namespaceURI) continue;\n const properties: Properties = new Map();\n for (const name of new Set((element.getAttribute('class') || '').split(/\\s+/))) {\n for (const [prop, value] of byClass.get(name) ?? []) select(properties, prop, value);\n }\n const inline = element.getAttribute('style');\n if (inline && (properties.size || inline.includes('!'))) {\n const style = parseCss(inline);\n if (!style) continue;\n style.walkDecls((decl) => {\n properties.delete(decl.prop.toLowerCase());\n });\n if (simplifyImportant(style)) {\n element.setAttribute('style', style.toString());\n changed = true;\n }\n }\n for (const [prop, { value }] of properties) {\n if (!element.hasAttribute(prop)) {\n element.setAttribute(prop, value);\n changed = true;\n }\n }\n }\n return changed ? new XMLSerializer().serializeToString(doc) : svg;\n}\n","/**\n * SVG Color Normalization\n *\n * 将 SVG 中的非标准颜色值(hsl, hsla, rgb, rgba, 命名颜色等)\n * 转换为不透明 hex 或 rgba() 格式,以兼容不完整支持 CSS 颜色语法的 SVG 渲染引擎\n * (如 @napi-rs/canvas 底层的 Skia 不支持 hsl())\n */\n\nimport { colord, extend } from 'colord';\nimport namesPlugin from 'colord/plugins/names';\n\n// 注册 CSS 命名颜色插件(red, blue, green 等)\nextend([namesPlugin]);\n\n/**\n * 匹配 SVG 中可能包含颜色的属性名(不区分大小写)\n */\nconst COLOR_ATTR_RE =\n /\\b(?:fill|stroke|stop-color|flood-color|color|lighting-color)\\s*=\\s*([\"'])(.*?)\\1/gi;\n\n/**\n * 匹配 style 属性中的 CSS 颜色属性\n */\nconst STYLE_COLOR_PROP_RE =\n /(?:fill|stroke|stop-color|flood-color|color|lighting-color|background-color)\\s*:\\s*([^;\"'}{]+)/gi;\n\n/** Keep the URL verbatim and normalize only its optional fallback color. */\nfunction normalizePaint(value: string): string {\n const trimmed = value.trim();\n const url = /^url\\((?:\"[^\"]*\"|'[^']*'|[^)]*)\\)\\s*/i.exec(trimmed)?.[0] ?? '';\n const color = colord(trimmed.slice(url.length));\n if (!color.isValid()) return value;\n const normalized = color.toRgb().a === 1 ? color.toHex() : color.toRgbString();\n return url + normalized;\n}\n\n/**\n * 标准化 SVG 字符串中的所有颜色值\n *\n * 处理以下场景:\n * 1. 颜色属性中的颜色函数:fill=\"hsl(120, 100%, 50%)\"\n * 2. 颜色属性中的命名颜色:fill=\"red\"\n * 3. style 属性中的颜色:style=\"fill: hsl(120, 100%, 50%)\"\n *\n * @param svgString - 原始 SVG 字符串\n * @returns 颜色标准化后的 SVG 字符串\n */\nexport function normalizeSvgColors(svgString: string): string {\n // 快速路径:如果不包含任何颜色函数关键字,直接返回\n if (!/(?:hsl|rgb|color|#[\\da-f]{4}\\b|#[\\da-f]{8}\\b)/i.test(svgString)) return svgString;\n return svgString\n .replace(STYLE_COLOR_PROP_RE, (full: string, value: string) =>\n full.replace(value, () => normalizePaint(value)),\n )\n .replace(COLOR_ATTR_RE, (full: string, quote: string, value: string) =>\n full.replace(quote + value + quote, () => quote + normalizePaint(value) + quote),\n );\n}\n","import fs from 'fs/promises';\nimport path from 'path';\nimport type { ImageBuffer } from './types.js';\n\n/**\n * 获取目录中所有图片文件的路径(支持 SVG 和 PNG)\n * @param dir 目录路径\n * @returns 文件路径数组\n */\nexport async function getFilePaths(dir: string): Promise<string[]> {\n const entries = await fs.readdir(dir);\n return entries\n .filter((entry) => !entry.startsWith('.') && (entry.endsWith('.svg') || entry.endsWith('.png')))\n .map((entry) => path.join(dir, entry));\n}\n\n/**\n * 加载单个图片文件(支持 SVG 和 PNG)\n * @param filePath 文件路径\n * @returns 图片缓冲区对象\n */\nexport async function loadFile(filePath: string): Promise<ImageBuffer> {\n const buffer = await fs.readFile(filePath);\n const ext = path.extname(filePath).toLowerCase();\n const id = path.basename(filePath).replace(/\\.(svg|png)$/i, '');\n\n if (ext === '.svg') {\n return { svg: buffer, id };\n } else if (ext === '.png') {\n return { png: buffer, id };\n } else {\n throw new Error(`Unsupported file format: ${ext}`);\n }\n}\n\n/**\n * 根据 ID 排序\n * @param a 第一个图片缓冲区\n * @param b 第二个图片缓冲区\n * @returns 排序结果\n */\nexport function sortById(a: ImageBuffer, b: ImageBuffer): number {\n return b.id < a.id ? 1 : -1;\n}\n\n/**\n * 显示帮助信息\n */\nexport function showHelp(): void {\n console.log(`\nsprite-maker\n\nGenerate sprite sheets for maps and the web using SVG files as input\n\nUsage\n sprite-maker <output> <inputdir> [options]\n\nOptions\n --retina shorthand for --ratio=2\n --ratio=[n] pixel ratio (default: 1)\n --unique map identical images to multiple names\n --help, -h show help\n\nExample\n sprite-maker maki maki/\n sprite-maker maki maki/ --retina\n sprite-maker maki maki/ --ratio=3\n sprite-maker maki maki/ --unique\n `);\n}\n"],"mappings":"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","names":["fs","path","DOMParser","path","loadImage","DOMParser","loadImage","path","fs"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sakitam-gis/sprite-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A modern TypeScript CLI tool for generating sprite sheets from SVG and PNG files with advanced optimization and metadata support",
|
|
6
6
|
"author": "sakitam-fdd",
|
|
@@ -70,12 +70,14 @@
|
|
|
70
70
|
"pretest": "pnpm run build"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@xmldom/xmldom": "0.8.11",
|
|
74
73
|
"@napi-rs/canvas": "^0.1.88",
|
|
74
|
+
"@xmldom/xmldom": "0.8.11",
|
|
75
75
|
"chalk": "^5.3.0",
|
|
76
|
+
"colord": "^2.9.3",
|
|
76
77
|
"commander": "^12.0.0",
|
|
77
78
|
"json-stable-stringify": "^1.1.1",
|
|
78
79
|
"ora": "^8.0.1",
|
|
80
|
+
"postcss": "8.5.6",
|
|
79
81
|
"sharp": "0.34.5",
|
|
80
82
|
"svg-boundings": "2.0.8",
|
|
81
83
|
"svgo": "4.0.0"
|