138 lines
3.9 KiB
TypeScript
138 lines
3.9 KiB
TypeScript
import path from "path";
|
|
import { getAllPagePaths, readFileAsBuffer, savePrintLayout } from "./storage";
|
|
|
|
// Dynamic import of the ES module mycro-zine library
|
|
async function importMycroZine() {
|
|
// The mycro-zine library is in the parent directory
|
|
const libPath = path.resolve(process.cwd(), "..", "src", "layout.mjs");
|
|
|
|
try {
|
|
const module = await import(libPath);
|
|
return module;
|
|
} catch (error) {
|
|
console.error("Failed to import mycro-zine library:", error);
|
|
throw new Error("Could not load mycro-zine library");
|
|
}
|
|
}
|
|
|
|
export interface PrintLayoutOptions {
|
|
zineId: string;
|
|
zineName?: string;
|
|
background?: string;
|
|
}
|
|
|
|
export async function createPrintLayoutForZine(
|
|
options: PrintLayoutOptions
|
|
): Promise<{ filepath: string; buffer: Buffer }> {
|
|
const { zineId, zineName = "mycrozine", background = "#ffffff" } = options;
|
|
|
|
// Get all page image paths
|
|
const pagePaths = await getAllPagePaths(zineId);
|
|
|
|
if (pagePaths.length !== 8) {
|
|
throw new Error(`Expected 8 pages, got ${pagePaths.length}`);
|
|
}
|
|
|
|
// Read all page images as buffers
|
|
const pageBuffers = await Promise.all(pagePaths.map((p) => readFileAsBuffer(p)));
|
|
|
|
// Import the layout module
|
|
const layoutModule = await importMycroZine();
|
|
|
|
// Create the print layout using the existing library
|
|
// The library expects either file paths or buffers
|
|
const outputBuffer = await layoutModule.createPrintLayout({
|
|
pages: pageBuffers,
|
|
zineName,
|
|
background,
|
|
returnBuffer: true, // We'll need to add this option to the library
|
|
});
|
|
|
|
// Save the print layout
|
|
const filepath = await savePrintLayout(zineId, outputBuffer);
|
|
|
|
return { filepath, buffer: outputBuffer };
|
|
}
|
|
|
|
// Alternative: Create print layout directly with Sharp if library doesn't support buffer return
|
|
import sharp from "sharp";
|
|
|
|
export async function createPrintLayoutDirect(
|
|
zineId: string,
|
|
zineName: string = "mycrozine"
|
|
): Promise<{ filepath: string; buffer: Buffer }> {
|
|
const pagePaths = await getAllPagePaths(zineId);
|
|
|
|
if (pagePaths.length !== 8) {
|
|
throw new Error(`Expected 8 pages, got ${pagePaths.length}`);
|
|
}
|
|
|
|
// Print layout dimensions (300 DPI, 11" x 8.5")
|
|
const PRINT_WIDTH = 3300;
|
|
const PRINT_HEIGHT = 2550;
|
|
const PANEL_WIDTH = 825;
|
|
const PANEL_HEIGHT = 1275;
|
|
|
|
// Page arrangement for proper folding:
|
|
// Top row (rotated 180°): P1, P8, P7, P6
|
|
// Bottom row (normal): P2, P3, P4, P5
|
|
const pageArrangement = [
|
|
// Top row
|
|
{ page: 1, col: 0, row: 0, rotate: 180 },
|
|
{ page: 8, col: 1, row: 0, rotate: 180 },
|
|
{ page: 7, col: 2, row: 0, rotate: 180 },
|
|
{ page: 6, col: 3, row: 0, rotate: 180 },
|
|
// Bottom row
|
|
{ page: 2, col: 0, row: 1, rotate: 0 },
|
|
{ page: 3, col: 1, row: 1, rotate: 0 },
|
|
{ page: 4, col: 2, row: 1, rotate: 0 },
|
|
{ page: 5, col: 3, row: 1, rotate: 0 },
|
|
];
|
|
|
|
// Create base canvas
|
|
const canvas = sharp({
|
|
create: {
|
|
width: PRINT_WIDTH,
|
|
height: PRINT_HEIGHT,
|
|
channels: 4,
|
|
background: { r: 255, g: 255, b: 255, alpha: 1 },
|
|
},
|
|
});
|
|
|
|
// Prepare composites
|
|
const composites: sharp.OverlayOptions[] = [];
|
|
|
|
for (const { page, col, row, rotate } of pageArrangement) {
|
|
const pageBuffer = await readFileAsBuffer(pagePaths[page - 1]);
|
|
|
|
// Resize page to panel size, maintaining aspect ratio
|
|
let processedPage = sharp(pageBuffer).resize(PANEL_WIDTH, PANEL_HEIGHT, {
|
|
fit: "cover",
|
|
position: "center",
|
|
});
|
|
|
|
// Rotate if needed
|
|
if (rotate !== 0) {
|
|
processedPage = processedPage.rotate(rotate);
|
|
}
|
|
|
|
const pageData = await processedPage.toBuffer();
|
|
|
|
composites.push({
|
|
input: pageData,
|
|
left: col * PANEL_WIDTH,
|
|
top: row * PANEL_HEIGHT,
|
|
});
|
|
}
|
|
|
|
// Composite all pages
|
|
const outputBuffer = await canvas.composite(composites).png().toBuffer();
|
|
|
|
// Save the print layout
|
|
const filepath = await savePrintLayout(zineId, outputBuffer);
|
|
|
|
return { filepath, buffer: outputBuffer };
|
|
}
|
|
|
|
export { createPrintLayoutDirect as createZinePrintLayout };
|