fix: correct print layout for traditional mini-zine folding

- Paper orientation: landscape 11"x8.5" (US Letter rotated)
- Panel size: 7cm x 10.8cm (825x1275 pixels at 300 DPI)
- Layout: 4 columns x 2 rows
- Top row (upside down): pages 1, 8, 7, 6
- Bottom row (right side up): pages 2, 3, 4, 5
- Updated folding instructions in README

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-15 18:56:45 -05:00
parent 9987ea3722
commit 1ff7a68d4f
3 changed files with 90 additions and 54 deletions

View File

@ -85,32 +85,33 @@ const imagePrompt = getImagePrompt({
## Print Layout
The output is a single PNG image with all 8 pages arranged in reading order:
The output is a single PNG image arranged for traditional mini-zine folding:
```
┌─────────────┬─────────────┐
│ Page 1 │ Page 2 │ Row 1
├─────────────┼─────────────┤
│ Page 3 │ Page 4 │ Row 2
├─────────────┼─────────────┤
│ Page 5 │ Page 6 │ Row 3
├─────────────┼─────────────┤
│ Page 7 │ Page 8 │ Row 4
└─────────────┴─────────────┘
┌──────────┬──────────┬──────────┬──────────┐
│ 1↺ │ 8↺ │ 7↺ │ 6↺ │ Top row (upside down)
│ (cover) │ (cta) │ │ │
├──────────┼──────────┼──────────┼──────────┤
│ 2 │ 3 │ 4 │ 5 │ Bottom row (right side up)
│ │ │ │ │
└──────────┴──────────┴──────────┴──────────┘
Panel size: 4.25" x 2.75" (~10.8cm x 7cm)
Total: 8.5" x 11" at 300 DPI (2550 x 3300 pixels)
Paper: 11" x 8.5" landscape (US Letter rotated)
Panel size: 7cm x 10.8cm (~2.76" x 4.25")
Total: 3300 x 2550 pixels at 300 DPI
```
## Folding Instructions
After printing, fold your zine:
1. **Accordion fold**: Fold the paper in half horizontally (hamburger style)
2. **Fold again**: Fold in half vertically (hotdog style)
3. **One more fold**: Fold in half again
4. **Cut center**: Unfold completely, cut a slit along the center fold
5. **Push and fold**: Push through the center to create the booklet
1. **Fold in half** along the long edge (hotdog fold) - brings top row to bottom
2. **Fold in half again** along the short edge
3. **Fold once more** to create a small booklet shape
4. **Unfold completely** and lay flat
5. **Cut the center slit** - cut along the middle crease between pages 3-6 and 4-5
6. **Refold and push** - fold hotdog style, then push the ends together so the cut opens into a booklet
7. **Flatten** - pages should now be in order 1→2→3→4→5→6→7→8
## Examples

View File

@ -30,23 +30,24 @@ export const DEFAULTS = {
/**
* Page dimensions in pixels at 300 DPI
* Paper is landscape orientation for traditional mini-zine folding
*/
export const DIMENSIONS = {
letter: {
width: 2550, // 8.5" x 300
height: 3300, // 11" x 300
panelWidth: 1275, // width / 2 cols
panelHeight: 825, // height / 4 rows
panelCols: 2,
panelRows: 4
width: 3300, // 11" x 300 (landscape)
height: 2550, // 8.5" x 300
panelWidth: 825, // ~7cm (width / 4 cols)
panelHeight: 1275, // ~10.8cm (height / 2 rows)
panelCols: 4,
panelRows: 2
},
a4: {
width: 2480, // 210mm at 300 DPI
height: 3508, // 297mm at 300 DPI
panelWidth: 1240,
panelHeight: 877,
panelCols: 2,
panelRows: 4
width: 3508, // 297mm at 300 DPI (landscape)
height: 2480, // 210mm at 300 DPI
panelWidth: 877,
panelHeight: 1240,
panelCols: 4,
panelRows: 2
}
};

View File

@ -1,10 +1,16 @@
/**
* MycroZine Layout Generator
*
* Creates a print-ready layout with all 8 pages on a single 8.5" x 11" sheet.
* Layout: 2 columns x 4 rows (reading order: left-to-right, top-to-bottom)
* Creates a print-ready layout with all 8 pages on a single sheet.
* Traditional 8-page mini-zine format for fold-and-cut assembly.
*
* Panel size: 4.25" x 2.75" (~10.8cm x 7cm)
* Paper: Landscape 11" x 8.5" (US Letter rotated)
* Layout: 4 columns x 2 rows
* Panel size: 7cm x 10.8cm (~2.76" x 4.25")
*
* Page arrangement for proper folding:
* Top row (upside down): 1, 8, 7, 6
* Bottom row (right side up): 2, 3, 4, 5
*/
import sharp from 'sharp';
@ -14,21 +20,30 @@ import fs from 'fs/promises';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// 8.5" x 11" at 300 DPI = 2550 x 3300 pixels
const PAGE_WIDTH = 2550;
const PAGE_HEIGHT = 3300;
// 11" x 8.5" at 300 DPI = 3300 x 2550 pixels (landscape)
const PAGE_WIDTH = 3300;
const PAGE_HEIGHT = 2550;
// Layout configuration: 2 columns x 4 rows
const COLS = 2;
const ROWS = 4;
const PANEL_WIDTH = Math.floor(PAGE_WIDTH / COLS); // 1275 pixels
const PANEL_HEIGHT = Math.floor(PAGE_HEIGHT / ROWS); // 825 pixels
// Layout configuration: 4 columns x 2 rows
const COLS = 4;
const ROWS = 2;
// Panel size: 7cm x 10.8cm at 300 DPI
// 7cm = 2.756" = 827 pixels, 10.8cm = 4.252" = 1275 pixels
const PANEL_WIDTH = Math.floor(PAGE_WIDTH / COLS); // 825 pixels (~7cm)
const PANEL_HEIGHT = Math.floor(PAGE_HEIGHT / ROWS); // 1275 pixels (~10.8cm)
// Page order for traditional mini-zine folding
// Top row (rotated 180°): pages 1, 8, 7, 6 (left to right)
// Bottom row (normal): pages 2, 3, 4, 5 (left to right)
const TOP_ROW_PAGES = [0, 7, 6, 5]; // 0-indexed: pages 1, 8, 7, 6
const BOTTOM_ROW_PAGES = [1, 2, 3, 4]; // 0-indexed: pages 2, 3, 4, 5
/**
* Create a print-ready zine layout from 8 page images
*
* @param {Object} options - Layout options
* @param {string[]} options.pages - Array of 8 page image paths (in order)
* @param {string[]} options.pages - Array of 8 page image paths (in order: 1-8)
* @param {string} [options.outputPath] - Output file path (default: output/mycrozine_print.png)
* @param {string} [options.background] - Background color (default: '#ffffff')
* @returns {Promise<string>} - Path to generated print layout
@ -47,7 +62,7 @@ export async function createPrintLayout(options) {
// Ensure output directory exists
await fs.mkdir(path.dirname(outputPath), { recursive: true });
// Load and resize all pages to panel size
// Load and resize all pages to panel size (7cm x 10.8cm)
const resizedPages = await Promise.all(
pages.map(async (pagePath) => {
return sharp(pagePath)
@ -59,17 +74,35 @@ export async function createPrintLayout(options) {
})
);
// Build composite array for all 8 pages in reading order
// Rotate top row pages 180 degrees (they need to be upside down)
const rotatedTopPages = await Promise.all(
TOP_ROW_PAGES.map(async (pageIndex) => {
return sharp(resizedPages[pageIndex])
.rotate(180)
.toBuffer();
})
);
// Build composite array with proper page arrangement
const compositeImages = [];
for (let row = 0; row < ROWS; row++) {
for (let col = 0; col < COLS; col++) {
const pageIndex = row * COLS + col; // 0-7
compositeImages.push({
input: resizedPages[pageIndex],
left: col * PANEL_WIDTH,
top: row * PANEL_HEIGHT
});
}
// Top row: pages 1, 8, 7, 6 (rotated 180°)
for (let col = 0; col < COLS; col++) {
compositeImages.push({
input: rotatedTopPages[col],
left: col * PANEL_WIDTH,
top: 0
});
}
// Bottom row: pages 2, 3, 4, 5 (normal orientation)
for (let col = 0; col < COLS; col++) {
const pageIndex = BOTTOM_ROW_PAGES[col];
compositeImages.push({
input: resizedPages[pageIndex],
left: col * PANEL_WIDTH,
top: PANEL_HEIGHT
});
}
// Create the final composite image
@ -86,8 +119,9 @@ export async function createPrintLayout(options) {
.toFile(outputPath);
console.log(`Created print layout: ${outputPath}`);
console.log(` Dimensions: ${PAGE_WIDTH}x${PAGE_HEIGHT} pixels (8.5"x11" @ 300 DPI)`);
console.log(` Panel size: ${PANEL_WIDTH}x${PANEL_HEIGHT} pixels (${COLS}x${ROWS} grid)`);
console.log(` Dimensions: ${PAGE_WIDTH}x${PAGE_HEIGHT} pixels (11"x8.5" landscape @ 300 DPI)`);
console.log(` Panel size: ${PANEL_WIDTH}x${PANEL_HEIGHT} pixels (7cm x 10.8cm)`);
console.log(` Layout: Top row [1↺, 8↺, 7↺, 6↺] | Bottom row [2, 3, 4, 5]`);
return outputPath;
}