A code that scans on screen can still fail on paper, for reasons that have nothing to do with what is in it. It was a picture and something resized it. Its black became four inks. Its margin got cropped, or it was shrunk to fit. This guide covers that print side. What to put in the code and how big it should be for the scanning distance are in how to make a QR code that scans every time.
| Check | Aim for | How to see it |
|---|---|---|
| Artwork | Vector: SVG, PDF or EPS, placed, not pasted as a PNG | pdfimages -list doesn't list it |
| Colour | Black ink only (K 100), or one dark spot ink | gs -sDEVICE=inkcov shows K only |
| Quiet zone | 4 modules round a QR code; 11 left and 7 right of an EAN-13 | Look at the layout |
| Size | A module the scanner can resolve; for EAN-13, 80 to 200 % | The numbers below |
| Proof | Scanned on paper, by several phones | Print one first |
Get the code as vector
A vector code is a set of squares or bars described as shapes. It has no resolution, so it prints exactly as sharp as the press can print, at any size. If you use InDesign, Object › Generate QR Code draws one in the layout, and its colour can be the Black swatch. Scribus has a barcode generator built in that draws EAN, UPC and QR codes as shapes. Otherwise, make the file outside the layout and place it.
zint makes QR codes and retail barcodes and, unlike qrencode, can write the
vector file in CMYK, set to black ink only. On Debian and Ubuntu,
apt install zint.
zint -b QRCODE --secure=2 --quietzones --cmyk --fg=0,0,0,100 --scalexdimdp=0.5mm,72dpi -d "https://example.com/menu" -o menu.eps
zint -b EANX --compliantheight --quietzones --cmyk --fg=0,0,0,100 --scalexdimdp=0.33mm,72dpi -d "400638133393" -o ean.eps
--secure=2 is error correction level M. --quietzones adds the clear margin
each code needs (4 modules round the QR code, 11 and 7 either side of the
EAN-13). --cmyk --fg=0,0,0,100 sets the colour to black ink only.
--scalexdimdp sets the size of one module: an EPS file measures in points, 72
to the inch, so 0.33mm,72dpi means 0.33 mm. For the EAN-13, give zint 12
digits and it adds the check digit; --compliantheight makes the bars the
height GS1 sets for that size.
Check what came out:
grep -E "BoundingBox|setcmykcolor" menu.eps ean.eps
menu.eps:%%BoundingBox: 0 0 47 47
menu.eps:0 0 0 0 setcmykcolor
menu.eps:0 0 0 1 setcmykcolor
ean.eps:%%BoundingBox: 0 0 106 74
ean.eps:0 0 0 0 setcmykcolor
ean.eps:0 0 0 1 setcmykcolor
Two colours each: 0 0 0 0 is a white box behind the code (no ink at all, so
the paper shows through), and 0 0 0 1 is 100 % black. The QR code is 33
modules of 0.5 mm, 16.5 mm with its margin. The EAN-13 is 105.7 points wide,
which is 37.29 mm, its nominal size. Then read each one back, as a scanner
would:
gs -q -dEPSCrop -r600 -sDEVICE=pnggray -o check.png ean.eps
zbarimg -q --raw check.png
4006381333931
qrencode, used in the other QR guides, writes vector SVG and EPS too
(-t SVG, -t EPS), but always in RGB black. That is fine in a design app
where you recolour the code to black ink before export. Left as it is, it
becomes four inks; see the colour section below.
Type the printed width; the Check tab gives the size of one module.
Why a PNG goes soft
A PNG is a grid of pixels, and a code only stays crisp when every module is a whole number of pixels that nothing resizes later. In print, something usually does: the layout scales the picture to fit, the PDF export downsamples it, or a compressor does. Each time, the edges of the modules become grey pixels, and a press prints grey as a pattern of dots.
A code made at 1 pixel per module and stretched to 354 pixels, as a layout app does when you scale a small image up:
qrencode -s 1 -m 4 -o tiny.png "https://example.com/menu"
magick tiny.png -resize 354x354 stretched.png
magick stretched.png -colorspace Gray -fx "u>0.02&&u<0.98" -format "%[fx:round(mean*1000)/10]%% grey pixels\n" info:
54.5% grey pixels
More than half the code is now neither black nor white. It still decodes on screen; on paper, those greys are the fuzzy edges. The same code made at 10 pixels per module has 0 % grey pixels. Even our own PNG export has a few (0.9 % at 1024 pixels), because 1024 doesn't divide into the code's 33 modules.
Going the other way is worse. Here a QR code was placed 10 mm wide as a
330-pixel PNG (menu.png, made with qrencode -s 10 -m 4), and the PDF then went through Ghostscript's /ebook setting, as
a "make it smaller" step does:
magick menu.png -units PixelsPerInch -density 838.2 qr10.pdf
gs -q -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -o qr10-ebook.pdf qr10.pdf
pdfimages -list qr10-ebook.pdf
page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------
1 0 image 59 59 gray 1 8 image no 9 0 150 150 591B 17%
1 1 smask 59 59 gray 1 8 image no 9 0 150 150 38B 1.1%
59 pixels for 33 modules: 1.8 pixels each. Rendered at 600 dpi, zbarimg read
the original and found nothing in this one. A vector code goes through the same
step untouched, because there is no image to downsample. That is also why
a print PDF should not be compressed.
If an app only takes images, make the PNG at the press resolution, with whole pixels per module, and place it at 100 %:
qrencode -s 12 -d 300 -m 4 -o menu-300.png "https://example.com/menu"
magick identify -units PixelsPerInch -format "%f: %w x %h px, %[fx:resolution.x] ppi, %[fx:round(w/resolution.x*25.4*10)/10] mm wide\n" menu-300.png
menu-300.png: 396 x 396 px, 300 ppi, 33.5 mm wide
Twelve pixels per module at 300 ppi is a 1.02 mm module. Placed at 100 %, the layout has no reason to resample it. Scale it and you are back to the grey edges.
Place it without changing it
- Place the file, scale it in proportion. In InDesign and Affinity, File › Place. Scaling a vector code changes nothing but its size. Never stretch it in one direction: in a barcode that changes the bar widths.
- Keep the quiet zone empty. No text, no frame, no photo, no edge of the card inside it. On a printed piece the code and its margin also belong inside the safe margin, 3 mm in from the trim (how to add bleed and crop marks).
- Check the PDF you send.
pdfimages -listlists every picture in a PDF, and a vector code is not one. Our Barcode Generator's PDF, for example, is a picture:
pdfimages -list ean-tool.pdf
page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------
1 0 image 234 142 rgb 3 8 image no 19 0 96 96 97.3K 100%
1 1 smask 234 142 gray 1 8 image no 19 0 96 96 32.4K 100%
The same command on zint's barcode, or on our QR generator's PDF, prints the header and nothing else.
Colour: black ink only
On a press, each ink is a separate plate, and the plates never land in exactly the same place. Text and fine detail in one ink don't care. A code in rich black (all four inks) is printed four times on top of itself, and when one plate is a fraction of a millimetre off, a coloured fringe runs along every edge and narrows the light modules and spaces. So:
- Set the code to C 0 M 0 Y 0 K 100. In a design app, apply the Black swatch, not "registration" and not a rich black.
- Or one dark spot ink, on its own plate, if the job is in spot colours.
- Watch RGB black. qrencode's SVG, converted for FOGRA39 with Ghostscript, came out at 79/70/53/98: four inks, 299 %, the same rich black as in the RGB-to-CMYK guide.
Ghostscript's inkcov shows which inks a page uses. Our QR generator's PDF,
then an RGB-black PDF made from qrencode's SVG:
gs -q -o - -sDEVICE=inkcov qr-tool.pdf
gs -q -o - -sDEVICE=inkcov menu-rsvg.pdf
0.00000 0.00000 0.00000 0.41358 CMYK OK
0.41097 0.41097 0.41097 0.41097 CMYK OK
Cyan, magenta and yellow at zero: black only. The second file uses all four. The figures are Ghostscript's quick conversion, not the printer's, so read them only for which inks are in use.
Contrast, paper and colour
- Dark on light. The code is the dark part and the paper is the light part. Navy, dark green or a dark brand colour work; a mid-tone does not.
- No red bars on a retail barcode. Most checkout scanners use red light, and GS1 grades barcodes under 660 nm red light, which makes red or orange bars look like the white spaces between them.
- Kraft and coloured stock lower the contrast, because the light modules are brown or coloured. Test a printed sample. If it is marginal, print a white box under the code (white ink, or a white label), or make the code bigger. On kraft, zint's white background box prints as no ink, so it is the paper colour.
- Don't invert unless you test it. The QR specification treats
light-on-dark as an option for readers, not a requirement.
zbarimgread our black-on-white code and found nothing in the same code inverted.
Retail barcodes: EAN-13 and UPC-A
Barcodes scanned at a checkout follow the GS1 General Specifications. These figures are from Release 26.0 (January 2026), Symbol Specification Table 1, for trade items scanned at retail point of sale:
| EAN-13 | UPC-A | |
|---|---|---|
| X-dimension (narrowest bar), minimum | 0.264 mm | 0.264 mm |
| Target (nominal, the old "100 %") | 0.330 mm | 0.330 mm |
| Maximum | 0.660 mm | 0.660 mm |
| Bar height at the target size | 22.85 mm | 22.85 mm |
| Quiet zone, left and right | 11 and 7 modules | 9 and 9 modules |
The minimum and maximum are the 80 % and 200 % magnifications that older specifications use. At the target size an EAN-13 is 37.29 mm wide including its quiet zones (113 modules), and at the minimum, 29.83 mm. The bar height does not include the digits underneath, and it scales with the X-dimension: 18.28 mm at the minimum, 45.70 mm at the maximum.
Four rules come with it:
- Don't truncate the height. GS1 says the bar height shall not be cut below its minimum. A shorter barcode can no longer be read from every angle, and is only allowed when a full one can't be printed, agreed with the retailer.
- Scale it as a whole, between 80 and 200 %, never wider or narrower alone.
- Bar width reduction is the printer's call. On offset and flexo, ink spreads and bars print wider than drawn, so packaging printers thin the bars in the master by an amount they measure on their press. Ask them; don't guess a figure.
- The number comes from GS1. A retail barcode carries a GTIN that your company licenses from its GS1 member organisation. A made-up number scans, and belongs to nobody.
The digits under the bars should be legible; GS1 recommends OCR-B. QR codes for retail packaging have their own row in the same table: for a QR code holding a GS1 Digital Link URI, an X-dimension of 0.396 to 0.990 mm, target 0.495, with a 4-module quiet zone.
For a run of retail packaging, a packaging printer or barcode software that knows GS1 sizes and bar width reduction is the safer route than any free generator, ours included.
No design software
It also makes EAN and UPC barcodes, but its defaults (a 4-module quiet zone, 10 mm bars) are below the GS1 figures above, so raise both for a retail code. Designing is free; exporting the print PDF needs an account on a paid plan. Popcorn's own page on scannable codes shows where those settings are.
Test the proof
- Check the PDF: the code isn't in
pdfimages -list, and the inks are black only. - Render the page and read the code back:
gs -q -r600 -sDEVICE=pnggray -o check.png print.pdf, thenzbarimg --raw check.png. It should print exactly what you meant to encode. - Print one copy at the real size, on the real paper, or get the printer's proof.
- Scan it with several phones, an older one among them, from the distance and angle people will really use, in the light where it will hang.
- For a retail barcode, ask your GS1 member organisation or the printer about a verification report: it grades the printed barcode against the same specification the retailer uses.
- Only then print the rest.
The rest of the file has its own checks: fonts, bleed and the PDF/X version are in how to make a print-ready PDF. For a code on a business card, see how to put a QR code on a business card, and for a logo in the middle, how to add a logo to a QR code.