Export
Pixels vs. physical space
In traditional print software, calculations between the digital space and the physical space are handled. They don't require manual calculations. If you want to print on an A4 paper, you can create a canvas that's size is A4 (297mm ✕ 210mm) with PPI (Pixels Per Inch) you desire and voilà.
However, when we create images for print in a system that is made for digital medium, like p5.js (design tool Sketch is also similar in this way), we have to make our own calculations to achieve our desired print size in high quality.
When we create a canvas in p5.js with canvas(400, 300)
and we place a rectangle using rect(200, 150, 20)
, we know how the result would look like in digital. But how do we calculate the size it will print?
Screens
Screens that we all use today are made out of pixels. CRT monitors and vector monitors are different, but we don't use them anymore. Pixels are the smallest units on screens.
Although there is a catch, an additional concept introduced by many smartphones and screens is "pixel density." Companies started to fit more pixels into small space to increase the detail of the image on their screen. A typical smartphone screen might have anything between 320 PPI and 570 PPI, whereas a 43 inch Full HD TV screen has 51 PPI. An important factor is the distance between the monitor (or print) and the viewer. A public square display doesn't need to have as many pixels per inch as much as a smartphone.
Handling all sizes across different devices would be a nightmare without some standardization. That is why pixel density is used by manufacturers to specify roughly the ratio of pixel size compared to a more standard screen (160 PPI). For example, an iPhone 11 has a pixel density of 3x. That is how we would see roughly similar (not same) sized output across different screens when they are in their native resolution. If you run displayDensity()
you would get your screen's density in p5.js.
Fortunately, all this pixel density is handled natively by software, including p5.js library, so you don't have to worry about it until you decide to do image processing or to print.
Paper sizes
International paper sizes are standardized in ISO 216. A series is the most popular one within it. North American formats are different, and I won't talk about those, but you can quickly adapt the following guidelines to fit them.
How do we calculate pixel dimensions to print on an A4?
One inch is 25.4mm. We want to print in 300 PPI. So, paper size times PPI divided by an inch, should give us the pixel sizes we need.
297mm * 300ppi / 25.4mm = 3508px
210mm * 300ppi / 25.4mm = 2480px
Ok, great, so we know that we need to create a sketch with a canvas size of 3508px by 2480px. However, a canvas of this size would probably run slowly. It'll be even slower if we want to print in bigger sizes, requiring larger canvases. To solve this, we will create our sketch in a scaled downsize so it'll be fast and easy to work. Below is a table for common A sizes. We will use 4x scaled-down dimensions as the working size for our sketch.
mm | inches | 300ppi pixels | 4x scaled-down | |
---|---|---|---|---|
A0 | 841 × 1189 | 33.1 × 46.8 | 7016 × 9920 | 1754 × 2480 |
A1 | 594 × 841 | 23.4 × 33.1 | 4960 × 7016 | 1240 × 1754 |
A2 | 420 × 594 | 16.5 × 23.4 | 3508 × 4960 | 877 × 1240 |
A3 | 297 × 420 | 11.7 × 16.5 | 2480 × 3508 | 620 × 877 |
A4 | 210 × 297 | 8.3 × 11.7 | 1754 × 2480 | 438 × 620 |
A5 | 148 × 210 | 5.8 × 8.3 | 1240 × 1754 | 310 × 219 |
A6 | 105 × 148 | 4.1 × 5.8 | 877 × 1240 | 109 × 310 |
But we still need a large image to export. So, the moment we want to export, we will scale it up to full size during one frame, export it, and then re-scale it back down. That works very well because we only need a single frame to print.
let scaleRatio = 1;
let exportRatio = 4;
let buffer;
let canvas;
let a3Paper = {
width: 3508,
height: 2480
}
function setup() {
let w = a3Paper.width / exportRatio;
let h = a3Paper.height / exportRatio;
buffer = createGraphics(w, h);
canvas = createCanvas(w, h);
// Adjust according to screens pixel density.
exportRatio /= pixelDensity();
}
function draw() {
background(220);
// Clear buffer each frame
buffer.clear();
// Transform (scale) all the drawings
buffer.scale(scaleRatio);
// Make all the drawing to the buffer instead of canvas
buffer.circle(width/2, height/2, 100);
// Draw buffer to canvas
image(buffer, 0, 0);
}
function exportHighResolution() {
scaleRatio = exportRatio;
// Re-create buffer with exportRatio and re-draw
buffer = createGraphics(scaleRatio*width, scaleRatio*height);
draw();
// Get timestamp to name the ouput file
let timestamp = new Date().getTime();
// Save as PNG
save(buffer, str(timestamp), 'png');
// Reset scaleRation back to 1, re-create buffer, re-draw
scaleRatio = 1;
buffer = createGraphics(width, height);
draw();
}
function keyReleased() {
if (key == 'e' || key == 'E') {
exportHighResolution();
}
}