印刷制作パイプライン、デザイン ファイル処理 (サイズ変更、カラー変換、DPI 検証)、RIP (ラスター イメージ プロセッサ)、カラー管理 (ICC プロファイル、CMYK 変換)、印刷キュー管理、品質検証、印刷準備完了ファイルの生成。
ファッション デザインとプリント オン デマンド システム アーキテクチャ — ドメイン分析から生産まで
パート 4: 注文の処理と履行
xdev.asia
1. 印刷物制作パイプラインの概要
Design File → Print Production Pipeline → Print-ready File for Printer
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Design │──▶│ File │──▶│ Color │──▶│ RIP │──▶│ Print │
│ Input │ │ Prep │ │ Mgmt │ │ Process │ │ Queue │
│ │ │ │ │ │ │ │ │ │
│ - PNG │ │ - Resize │ │ - ICC │ │ - Raster │ │ - FIFO │
│ - SVG │ │ - DPI │ │ - sRGB→ │ │ - Halftone│ │ - Priority│
│ - PDF │ │ - Bleed │ │ CMYK │ │ - Spot │ │ - Batch │
│ - AI │ │ - Flatten│ │ - Gamut │ │ color │ │ - Status │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
2. ファイル処理サービス
interface PrintFileProcessor {
process(input: DesignFile, spec: PrintSpec): Promise<PrintReadyFile>;
}
interface DesignFile {
url: string;
format: 'png' | 'svg' | 'pdf' | 'psd' | 'ai';
dimensions: { width: number; height: number };
dpi: number;
colorSpace: string;
fileSize: number;
}
interface PrintSpec {
targetDPI: number; // 300 for most, 600 for fine art
targetColorSpace: 'rgb' | 'cmyk';
maxFileSize: number; // MB
bleed: number; // mm
trimMarks: boolean;
// Product-specific
printArea: {
width: number; // inches
height: number; // inches
};
// Print method constraints
printMethod: PrintMethod;
maxColors?: number; // For screen print
minLineWidth?: number; // mm — thin lines may not print
// Supplier requirements
supplierFormat: 'png' | 'pdf' | 'tiff';
supplierMaxDPI: number;
supplierNaming: string; // e.g., "{orderId}_{sku}_{side}.png"
}
async function processDesignFile(
input: DesignFile,
spec: PrintSpec,
): Promise<PrintReadyFile> {
const validationResult = await validateInput(input, spec);
if (!validationResult.valid) {
throw new PrintFileValidationError(validationResult.errors);
}
// 1. Load image in full resolution
let image = await loadImage(input.url);
// 2. DPI check & resize if needed
if (input.dpi < spec.targetDPI) {
// Upscale using AI (Real-ESRGAN) if DPI too low
if (input.dpi < 150) {
image = await aiUpscale(image, { targetDPI: spec.targetDPI });
}
}
// 3. Resize to exact print dimensions
const targetWidth = Math.round(spec.printArea.width * spec.targetDPI);
const targetHeight = Math.round(spec.printArea.height * spec.targetDPI);
image = await resize(image, targetWidth, targetHeight, { fit: 'contain' });
// 4. Add bleed area
if (spec.bleed > 0) {
image = await addBleed(image, spec.bleed, spec.targetDPI);
}
// 5. Color space conversion
if (spec.targetColorSpace === 'cmyk' && input.colorSpace !== 'cmyk') {
image = await convertToColorSpace(image, {
from: 'srgb',
to: 'cmyk',
iccProfile: 'USWebCoatedSWOP',
renderingIntent: 'perceptual',
});
}
// 6. Flatten layers (remove transparency where needed)
if (spec.printMethod === 'sublimation') {
image = await flattenOnWhite(image);
}
// 7. Add trim marks if required
if (spec.trimMarks) {
image = await addTrimMarks(image, spec.bleed);
}
// 8. Export in supplier format
const outputBuffer = await exportFile(image, {
format: spec.supplierFormat,
dpi: spec.targetDPI,
quality: 100, // Lossless for printing
compression: spec.supplierFormat === 'tiff' ? 'lzw' : 'none',
});
return {
buffer: outputBuffer,
format: spec.supplierFormat,
width: targetWidth,
height: targetHeight,
dpi: spec.targetDPI,
colorSpace: spec.targetColorSpace,
fileSizeBytes: outputBuffer.length,
checksum: calculateSHA256(outputBuffer),
};
}
3. カラーマネジメント (ICC プロファイル)
interface ColorManagementSystem {
convertColorSpace(
image: Buffer,
from: string,
to: string,
profile: ICCProfile,
): Promise<Buffer>;
softProof(image: Buffer, printerProfile: ICCProfile): Promise<Buffer>;
checkGamut(image: Buffer, targetProfile: ICCProfile): Promise<GamutReport>;
}
// ICC Profile cho từng printer/method
const ICC_PROFILES: Record<string, ICCProfile> = {
// Standard profiles
srgb: { name: 'sRGB IEC61966-2.1', path: '/profiles/sRGB.icc' },
adobe_rgb: { name: 'Adobe RGB (1998)', path: '/profiles/AdobeRGB1998.icc' },
// CMYK printing profiles
us_web_coated: { name: 'USWebCoatedSWOP', path: '/profiles/USWebCoatedSWOP.icc' },
fogra39: { name: 'FOGRA39 (ISO Coated v2)', path: '/profiles/CoatedFOGRA39.icc' },
// Textile-specific profiles (custom per printer)
epson_f2100_cotton: { name: 'Epson F2100 Cotton', path: '/profiles/EpsonF2100Cotton.icc' },
kornit_atlas_max: { name: 'Kornit Atlas MAX', path: '/profiles/KornitAtlasMAX.icc' },
};
interface GamutReport {
totalPixels: number;
outOfGamutPixels: number;
outOfGamutPercentage: number;
problematicAreas: Array<{
region: { x: number; y: number; width: number; height: number };
originalColor: string;
mappedColor: string;
deltaE: number;
}>;
overallQuality: 'excellent' | 'good' | 'acceptable' | 'poor';
}
4. RIP(ラスターイメージプロセッサ)
// RIP: Convert design → printer-ready raster data
interface RIPProcessor {
process(printFile: PrintReadyFile, printerConfig: PrinterConfig): Promise<RIPOutput>;
}
interface PrinterConfig {
model: string; // 'Kornit Atlas MAX', 'Epson F2100'
printMethod: PrintMethod;
resolution: number; // 600, 1200, 1440 DPI
headType: string; // Print head technology
// Ink channels
inkChannels: InkChannel[];
// Quality settings
passCount: number; // Number of print passes
printSpeed: 'draft' | 'standard' | 'high_quality';
}
interface InkChannel {
name: string; // 'Cyan', 'Magenta', 'Yellow', 'Black', 'White'
type: 'cmyk' | 'spot' | 'white_underbase';
}
interface RIPOutput {
// Separated channel data
channels: Array<{
name: string;
data: Buffer; // Monochrome raster for this channel
}>;
// White underbase (for dark garments)
whiteUnderbase?: {
data: Buffer;
coverage: number; // 0-100%
chokeAmount: number; // pixels to shrink (prevent white showing at edges)
};
printPlan: {
totalPasses: number;
estimatedTime: Duration;
inkUsage: Record<string, number>; // ml per channel
};
}
// White underbase generation for DTG on dark garments
async function generateWhiteUnderbase(
design: Buffer,
options: UnderbaseOptions,
): Promise<Buffer> {
// 1. Extract alpha channel (where design exists)
const alpha = await extractAlphaChannel(design);
// 2. Apply choke (shrink white layer slightly)
// Prevents white ink from showing at design edges
const choked = await erode(alpha, options.chokePixels);
// 3. Adjust density based on garment color
// Darker garment = more white needed
const density = options.garmentBrightness < 0.3 ? 1.0
: options.garmentBrightness < 0.6 ? 0.7
: 0.4;
const underbase = await adjustDensity(choked, density);
return underbase;
}
5. 印刷キュー管理
// Print queue: Schedule và prioritize print jobs
interface PrintQueue {
addJob(job: PrintJob): Promise<string>;
getNextBatch(printerId: string, maxItems: number): Promise<PrintJob[]>;
updateJobStatus(jobId: string, status: PrintJobStatus): Promise<void>;
}
interface PrintJob {
id: string;
orderId: string;
subOrderId: string;
// Product info
productType: string;
garmentColor: string;
garmentSize: string;
// Print specification
printFile: PrintReadyFile;
printMethod: PrintMethod;
printAreas: string[]; // ['front', 'back']
// Scheduling
priority: PrintPriority;
dueDate: Date;
// Status
status: PrintJobStatus;
assignedPrinter?: string;
startedAt?: Date;
completedAt?: Date;
}
// Batch optimization: Group similar jobs together
function optimizePrintBatch(jobs: PrintJob[]): PrintBatch[] {
// Group by: same garment type + same color + same print method
// This minimizes printer changeover time
const groups = groupBy(jobs, j =>
`${j.productType}:${j.garmentColor}:${j.printMethod}`
);
return Object.entries(groups).map(([key, groupJobs]) => ({
batchId: generateBatchId(),
key,
jobs: groupJobs.sort((a, b) => a.priority - b.priority),
estimatedTime: estimateBatchTime(groupJobs),
setupRequired: {
garmentChange: true,
inkChange: false, // Same method = same ink setup
platenChange: groupJobs[0].garmentSize !== groupJobs[1]?.garmentSize,
},
}));
}
6. まとめ
| ステージ | 主要なプロセス | 出力 |
| ファイル処理 | サイズ変更、DPI検証、平坦化 | 標準形式のファイル |
| カラーマネジメント | ICCプロファイル、sRGB→CMYK、色域マップ | 色が正確なファイル |
| RIP処理 | チャンネルセパレーション、ハーフトーン、アンダーベース | プリンター対応ラスター |
| 品質検証 | DPIチェック、細線、色域 | 合否レポート付き |
| 印刷キュー | 優先スケジューリング、バッチ最適化 | 最適化された印刷バッチ |