|
|
/** * 画布尺寸计算工具 * 根据纸张物理尺寸、DPI、缩放比例等计算画布显示尺寸 */
// 标准DPI配置
export const DPI_CONFIGS = { 152: { name: '152 DPI', description: '低分辨率打印机' }, 203: { name: '203 DPI', description: '标准热敏打印机' }, 300: { name: '300 DPI', description: '高分辨率打印机' }, 600: { name: '600 DPI', description: '超高分辨率打印机' }}
// 默认显示配置
export const DISPLAY_CONFIG = { defaultDPI: 203, // 默认DPI
maxCanvasWidth: 800, // 画布区域最大宽度
maxCanvasHeight: 600, // 画布区域最大高度
minCanvasWidth: 200, // 画布区域最小宽度
minCanvasHeight: 150, // 画布区域最小高度
padding: 40, // 画布周围的内边距
scaleFactor: 1.0 // 默认缩放因子
}
/** * 毫米转像素 * @param {number} mm - 毫米值 * @param {number} dpi - DPI值 * @returns {number} 像素值 */export function mmToPixels(mm, dpi = DISPLAY_CONFIG.defaultDPI) { return Math.round(mm * dpi / 25.4)}
/** * 像素转毫米 * @param {number} pixels - 像素值 * @param {number} dpi - DPI值 * @returns {number} 毫米值 */export function pixelsToMm(pixels, dpi = DISPLAY_CONFIG.defaultDPI) { return Math.round((pixels * 25.4 / dpi) * 10) / 10}
/** * 根据纸张物理尺寸计算精确的像素尺寸 * @param {string} paperType - 纸张类型 (如 '60x40' 或 '4x6') * @param {number} dpi - DPI值 * @param {string} orientation - 打印方向 * @returns {Object} 精确的像素尺寸 */export function calculateExactPixelSize(paperType, dpi = DISPLAY_CONFIG.defaultDPI, orientation = 'portrait') { let widthMm, heightMm
// 检查是否为英寸制纸张(如4x6)
if (isInchPaper(paperType)) { // 英寸制纸张,直接从英寸转换为像素
const [widthInch, heightInch] = paperType.split('x').map(Number)
if (!widthInch || !heightInch) { throw new Error(`Invalid inch paper type: ${paperType}`) }
// 英寸直接转像素
let pixelWidth = Math.round(widthInch * dpi) let pixelHeight = Math.round(heightInch * dpi)
// 计算对应的毫米值(用于显示)
widthMm = Math.round(widthInch * 25.4 * 10) / 10 heightMm = Math.round(heightInch * 25.4 * 10) / 10
// 横向打印时交换宽高
if (orientation === 'landscape') { [pixelWidth, pixelHeight] = [pixelHeight, pixelWidth] ;[widthMm, heightMm] = [heightMm, widthMm] }
return { width: pixelWidth, height: pixelHeight, widthMm, heightMm, dpi, orientation } } else { // 毫米制纸张,从毫米转换
const [widthMmParsed, heightMmParsed] = paperType.split('x').map(Number)
if (!widthMmParsed || !heightMmParsed) { throw new Error(`Invalid paper type: ${paperType}`) }
widthMm = widthMmParsed heightMm = heightMmParsed
// 计算精确像素尺寸
let pixelWidth = mmToPixels(widthMm, dpi) let pixelHeight = mmToPixels(heightMm, dpi)
// 横向打印时交换宽高
if (orientation === 'landscape') { [pixelWidth, pixelHeight] = [pixelHeight, pixelWidth] ;[widthMm, heightMm] = [heightMm, widthMm] }
return { width: pixelWidth, height: pixelHeight, widthMm, heightMm, dpi, orientation } }}
/** * 判断是否为英寸制纸张 * @param {string} paperType - 纸张类型 * @returns {boolean} 是否为英寸制 */function isInchPaper(paperType) { // 常见的英寸制纸张规格
const inchPapers = ['4x6', '4x4', '4x2', '3x2', '2x1', '2x3', '2x4', '6x4'] return inchPapers.includes(paperType)}
/** * 计算画布显示尺寸和缩放比例 * @param {Object} exactSize - 精确像素尺寸 * @param {Object} containerSize - 容器尺寸 {width, height} * @returns {Object} 显示配置 */export function calculateDisplaySize(exactSize, containerSize) { const { width: exactWidth, height: exactHeight } = exactSize const { width: containerWidth, height: containerHeight } = containerSize
// 计算可用空间(减去内边距)
const availableWidth = containerWidth - DISPLAY_CONFIG.padding * 2 const availableHeight = containerHeight - DISPLAY_CONFIG.padding * 2
// 计算缩放比例
const scaleX = availableWidth / exactWidth const scaleY = availableHeight / exactHeight const scale = Math.min(scaleX, scaleY, 1) // 不超过1倍缩放
// 计算显示尺寸
const displayWidth = Math.round(exactWidth * scale) const displayHeight = Math.round(exactHeight * scale)
// 确保不小于最小尺寸
const finalWidth = Math.max(displayWidth, DISPLAY_CONFIG.minCanvasWidth) const finalHeight = Math.max(displayHeight, DISPLAY_CONFIG.minCanvasHeight)
return { // 显示尺寸
displayWidth: finalWidth, displayHeight: finalHeight,
// 精确尺寸
exactWidth, exactHeight,
// 缩放信息
scale: finalWidth / exactWidth, isScaled: scale < 1,
// 适配信息
fitsInContainer: exactWidth <= availableWidth && exactHeight <= availableHeight,
// 居中位置
offsetX: Math.max(0, (containerWidth - finalWidth) / 2), offsetY: Math.max(0, (containerHeight - finalHeight) / 2) }}
/** * 获取纸张的物理信息 * @param {string} paperType - 纸张类型 * @returns {Object} 纸张物理信息 */export function getPaperPhysicalInfo(paperType) { if (paperType === 'custom') { return { name: '自定义', category: 'custom', isStandard: false } }
// 检查是否为英寸制纸张
if (isInchPaper(paperType)) { const [widthInch, heightInch] = paperType.split('x').map(Number)
// 转换为毫米用于计算
const widthMm = Math.round(widthInch * 25.4 * 10) / 10 const heightMm = Math.round(heightInch * 25.4 * 10) / 10
// 纸张分类
let category = 'shipping' // 英寸制纸张通常用于物流
if (widthInch <= 2 || heightInch <= 2) category = 'barcode' if (widthInch >= 6 || heightInch >= 6) category = 'industrial'
return { name: `${widthInch}×${heightInch}英寸 (${widthMm}×${heightMm}mm)`, widthMm, heightMm, widthInch, heightInch, category, isStandard: true, isInchPaper: true, area: widthMm * heightMm, aspectRatio: Math.round((widthMm / heightMm) * 100) / 100 } } else { // 毫米制纸张
const [widthMm, heightMm] = paperType.split('x').map(Number)
// 纸张分类
let category = 'standard' if (heightMm <= 30) category = 'barcode' if (widthMm === 57 || widthMm === 80) category = 'thermal'
return { name: `${widthMm}×${heightMm}mm`, widthMm, heightMm, category, isStandard: true, isInchPaper: false, area: widthMm * heightMm, aspectRatio: Math.round((widthMm / heightMm) * 100) / 100 } }}
/** * 计算完整的画布配置 * @param {string} paperType - 纸张类型 * @param {string} orientation - 打印方向 * @param {number} dpi - DPI值 * @param {Object} containerSize - 容器尺寸 * @param {Object} customSize - 自定义尺寸 (仅当paperType为'custom'时使用) * @returns {Object} 完整的画布配置 */export function calculateCanvasConfig(paperType, orientation = 'portrait', dpi = DISPLAY_CONFIG.defaultDPI, containerSize = null, customSize = null) { let exactSize
if (paperType === 'custom' && customSize) { // 自定义尺寸处理
const widthMm = pixelsToMm(customSize.width, dpi) const heightMm = pixelsToMm(customSize.height, dpi)
exactSize = { width: orientation === 'landscape' ? customSize.height : customSize.width, height: orientation === 'landscape' ? customSize.width : customSize.height, widthMm: orientation === 'landscape' ? heightMm : widthMm, heightMm: orientation === 'landscape' ? widthMm : heightMm, dpi, orientation } } else { // 标准纸张尺寸处理
exactSize = calculateExactPixelSize(paperType, dpi, orientation) }
// 计算显示配置
const defaultContainer = { width: DISPLAY_CONFIG.maxCanvasWidth, height: DISPLAY_CONFIG.maxCanvasHeight }
const displayConfig = calculateDisplaySize(exactSize, containerSize || defaultContainer)
// 获取纸张物理信息
const physicalInfo = getPaperPhysicalInfo(paperType)
return { // 基本信息
paperType, orientation, dpi,
// 物理信息
physical: physicalInfo,
// 精确尺寸(用于ZPL生成和坐标转换)
exact: exactSize,
// 显示尺寸(用于画布渲染)
display: displayConfig,
// 元数据
meta: { timestamp: Date.now(), isCustom: paperType === 'custom', description: `${physicalInfo.name} (${orientation === 'landscape' ? '横向' : '纵向'}) @ ${dpi}DPI` } }}
/** * 根据纸张ID计算画布配置(新增方法) * @param {number} paperId - 纸张ID * @param {string} orientation - 打印方向 * @param {number} dpi - DPI值 * @param {Object} containerSize - 容器尺寸 * @returns {Object} 完整的画布配置 */export function calculateCanvasConfigById(paperId, orientation = 'portrait', dpi = DISPLAY_CONFIG.defaultDPI, containerSize = null) { // 延迟导入避免循环依赖
const { default: dynamicPaperConfig } = require('./paperConfigDynamic.js') const paper = dynamicPaperConfig.getPaperById(paperId) if (!paper) { throw new Error(`Paper with ID ${paperId} not found`) }
// 使用纸张的毫米尺寸计算精确像素尺寸
const exactSize = { width: Math.round(paper.widthMm * dpi / 25.4), height: Math.round(paper.heightMm * dpi / 25.4), widthMm: paper.widthMm, heightMm: paper.heightMm, dpi, orientation }
// 横向打印时交换宽高
if (orientation === 'landscape') { [exactSize.width, exactSize.height] = [exactSize.height, exactSize.width] ;[exactSize.widthMm, exactSize.heightMm] = [exactSize.heightMm, exactSize.widthMm] }
// 计算显示配置
const defaultContainer = { width: DISPLAY_CONFIG.maxCanvasWidth, height: DISPLAY_CONFIG.maxCanvasHeight }
const displayConfig = calculateDisplaySize(exactSize, containerSize || defaultContainer)
// 构建物理信息
const physicalInfo = { name: paper.name, widthMm: paper.widthMm, heightMm: paper.heightMm, description: paper.description || '', isStandard: true, isCustom: false, area: paper.widthMm * paper.heightMm, aspectRatio: Math.round((paper.widthMm / paper.heightMm) * 100) / 100 }
return { // 基本信息
paperId, paperType: paper.name, // 兼容性
orientation, dpi,
// 物理信息
physical: physicalInfo,
// 精确尺寸(用于ZPL生成和坐标转换)
exact: exactSize,
// 显示尺寸(用于画布渲染)
display: displayConfig,
// 纸张对象
paper,
// 元数据
meta: { timestamp: Date.now(), isCustom: false, isDynamic: true, description: `${paper.name} (${orientation === 'landscape' ? '横向' : '纵向'}) @ ${dpi}DPI` } }}
/** * 获取推荐的DPI值 * @param {string} paperType - 纸张类型 * @returns {number} 推荐的DPI值 */export function getRecommendedDPI(paperType) { const physicalInfo = getPaperPhysicalInfo(paperType)
// 根据纸张类型推荐DPI
switch (physicalInfo.category) { case 'barcode': return 300 // 条码标签需要高精度
case 'thermal': return 203 // 热敏纸标准DPI
case 'shipping': return 203 // 物流标签标准DPI
case 'industrial': return 300 // 工业标签推荐高精度
default: return 203 // 标准标签DPI
}}
export default { DPI_CONFIGS, DISPLAY_CONFIG, mmToPixels, pixelsToMm, calculateExactPixelSize, calculateDisplaySize, getPaperPhysicalInfo, calculateCanvasConfig, calculateCanvasConfigById, getRecommendedDPI}
|