{"course":{"slug":"vector-and-print","title":"Vectors, Logos and Things That Get Printed","url":"https://addaly.com/learn/vector-and-print","level":"beginner","track":"create"},"lesson":{"slug":"data-merge-and-variable-data","title":"Five hundred different cards from one template","url":"https://addaly.com/learn/vector-and-print/data-merge-and-variable-data","position":53,"of":73,"minutes":9,"answer":"Variable data printing is a template plus a clean UTF-8 CSV, and nearly every failure is a data problem, so design to the longest record.","keyIdea":"Variable data printing is a template plus a clean UTF-8 CSV, and nearly every failure is a data problem, so design to the longest record, generate QR and barcode images beforehand as a merged image field, and proof by sampling six deliberately chosen records rather than skimming all of them."},"body":{"format":"markdown","text":"## The work nobody advertises\n\nFive hundred visiting cards with different names. Three hundred certificates. Price labels for every item in a shop. Name badges for a conference. Numbered raffle tickets. Barcode or QR labels for a warehouse.\n\nThis is **variable data printing**, it is well paid, it is repeatable, and remarkably few designers offer it. The whole skill is setting up a template once and pointing it at a spreadsheet.\n\n> Figure: Five hundred cards, four steps. Steps: 1. Clean the CSV (One header row, no merged cells, UTF-8); 2. Find the longest (Sort each field by length, design to that); 3. Merge (Fields into frames, then create the document); 4. Proof six (Chosen records, not a skim of all of them). Nearly every merge failure is a data failure, which is why two of the four steps happen in the spreadsheet. Nobody checks 500 records, so the proof is six chosen ones: first, last, longest, shortest, one with an odd character, one at random.\n\n## Prepare the data first\n\nMost merge failures are data failures, so clean the CSV before you open the layout.\n\n1. **One header row**, with short names and no spaces: `name`, `designation`, `mobile`, `email`, `photo`.\n2. **No merged cells, no blank rows, no totals row** at the bottom.\n3. **Consistent case and spacing.** Run a trim on every field; trailing spaces produce mysterious alignment.\n4. **Encoding: UTF-8.** If the data has any Indic script, a name with an accent, or a rupee sign, a file saved as Latin-1 will arrive as mojibake. In LibreOffice, Save As ▸ Text CSV ▸ tick *Edit filter settings* and choose UTF-8.\n5. **Image fields** hold a file path, and InDesign requires the header to begin with `@` (`@photo`) with the paths either absolute or relative to the data file.\n6. **Look at the longest record.** Sort by the length of each field and design the template around the longest name, not the first one.\n\n## Doing it in each program\n\n**InDesign**: Window ▸ Utilities ▸ Data Merge. Select the data source, place the field placeholders into the frames, then *Create Merged Document*. Options that matter: *Records per Document Page* (one card per page, or a grid of ten on an A4 sheet), *Multiple Record Layout* with spacing, and **Preview** before merging, which is how you catch overflow.\n\n**CorelDRAW**: File ▸ Print Merge. It is good, it handles a grid of cards on a sheet, and it is why so many Indian print shops do this work in CorelDRAW rather than anything else.\n\n**Scribus**: the **ScribusGenerator** Python script, free on GitHub. Put `%VAR_name%` placeholders in the text, run the script against a CSV, and it produces one output file per record or a combined document. Less polished than the commercial tools and entirely capable.\n\n**Free, no layout application at all**: generate HTML from the CSV with a short script and render with WeasyPrint. For a thousand labels this is faster than any GUI, and the template is a text file you can version.\n\n## Overflow is the failure mode\n\nA template that fits \"Anil Kumar\" will not fit \"Lakshmi Narayanan Venkataraman\". The merged document will either overset that record silently or push text outside the frame.\n\nThree defences:\n\n- Design to the longest record, found by sorting the data.\n- Set the frame to **auto-size** where the application supports it, or set a minimum type size and allow the style to shrink by a defined amount.\n- **Check the merged output**, not the template. Which is the next section.\n\n## QR codes and barcodes per record\n\nA QR code per row is the most common request after names.\n\n- **InDesign** has a built-in QR code generator (Object ▸ Generate QR Code) but it does not read from a data field, so the practical route is to generate the images first and merge them as an `@image` field.\n- **Generate them free** with the Python `qrcode` library or `qrencode` on the command line, writing one SVG per row named after a key in the data:\n\n```bash\nwhile IFS=, read -r id url; do qrencode -t SVG -o \"qr/$id.svg\" \"$url\"; done < links.csv\n```\n\n- **Barcodes**: `zint` is free, handles EAN, Code 128, DataMatrix and the rest, and outputs vector. Get the check digit right; a barcode with a wrong check digit scans as nothing.\n- Always **test a printed sample with a real scanner** before running 5,000. Quiet zone, contrast and minimum module size all matter, and a QR code printed too small or over a pattern will not read.\n\n## Proof the merge by sampling\n\nNobody checks 500 merged records. Check these six:\n\n1. The first record.\n2. The last record.\n3. The longest name.\n4. The shortest name.\n5. Any record with an unusual character: an apostrophe, a hyphen, a non-Latin script.\n6. One at random from the middle.\n\nIf all six are right, the merge is almost certainly right. If any is wrong, the fault is systematic and you have found it before printing.\n\n## Pricing it\n\nCharge for the setup, not for the records. The template is two to four hours; the merge itself takes minutes. A client comparing you against \"someone who types 500 cards\" is comparing against three days of labour, and the honest pitch is that you are faster, cheaper and will not make a typo on card 312."},"check":{"question":"A merge of 500 name badges produces correct output except that about thirty records show their names as strings of question marks and boxes. The template is fine and those thirty names are all in Devanagari. What is the most likely cause?","options":[{"text":"The Devanagari text needs a shaping engine the merge process bypasses, so conjuncts fail to form.","correct":false,"tempting":"Failed shaping produces misplaced matras or split conjuncts, not question marks and boxes, and the characters would still be Devanagari."},{"text":"The CSV was saved in a single-byte encoding, so the Devanagari code points were lost before the layout saw them.","correct":true},{"text":"The template's font has no Devanagari glyphs, so every character falls back to the missing-glyph box.","correct":false,"tempting":"A reasonable second guess, but a missing-glyph fallback gives uniform boxes, whereas the mix of question marks and boxes points to data corrupted before rendering."},{"text":"The image field paths for those records were absolute rather than relative, so the merge substituted placeholder characters.","correct":false,"tempting":"Image paths have nothing to do with text rendering, and a broken link produces a missing image, not corrupted text."}]},"prev":{"slug":"contents-and-cross-references","title":"Contents lists and cross-references that renumber themselves","url":"https://addaly.com/learn/vector-and-print/contents-and-cross-references"},"next":{"slug":"books-signatures-and-binding","title":"Page counts, creep and the spine","url":"https://addaly.com/learn/vector-and-print/books-signatures-and-binding"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Five hundred different cards from one template', in Vectors, Logos and Things That Get Printed. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/vector-and-print/data-merge-and-variable-data (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Five hundred different cards from one template","courseTitle":"Vectors, Logos and Things That Get Printed","module":{"index":6,"slug":"layout-that-scales","title":"Documents that survive a late change"},"answer":null,"keyIdea":"Variable data printing is a template plus a clean UTF-8 CSV, and nearly every failure is a data problem, so design to the longest record, generate QR and barcode images beforehand as a merged image field, and proof by sampling six deliberately chosen records rather than skimming all of them.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"flow","steps":[{"label":"Clean the CSV","detail":"One header row, no merged cells, UTF-8"},{"label":"Find the longest","detail":"Sort each field by length, design to that"},{"label":"Merge","detail":"Fields into frames, then create the document"},{"label":"Proof six","detail":"Chosen records, not a skim of all of them"}],"title":"Five hundred cards, four steps","caption":"Nearly every merge failure is a data failure, which is why two of the four steps happen in the spreadsheet. Nobody checks 500 records, so the proof is six chosen ones: first, last, longest, shortest, one with an odd character, one at random."},"alt":"Figure: Five hundred cards, four steps. Steps: 1. Clean the CSV (One header row, no merged cells, UTF-8); 2. Find the longest (Sort each field by length, design to that); 3. Merge (Fields into frames, then create the document); 4. Proof six (Chosen records, not a skim of all of them). Nearly every merge failure is a data failure, which is why two of the four steps happen in the spreadsheet. Nobody checks 500 records, so the proof is six chosen ones: first, last, longest, shortest, one with an odd character, one at random."}},"blocks":[{"t":"h2","id":"the-work-nobody-advertises","runs":[{"text":"The work nobody advertises"}]},{"t":"p","runs":[{"text":"Five hundred visiting cards with different names. Three hundred certificates. Price labels for every item in a shop. Name badges for a conference. Numbered raffle tickets. Barcode or QR labels for a warehouse."}]},{"t":"p","runs":[{"text":"This is "},{"text":"variable data printing","b":true},{"text":", it is well paid, it is repeatable, and remarkably few designers offer it. The whole skill is setting up a template once and pointing it at a spreadsheet."}]},{"t":"h2","id":"prepare-the-data-first","runs":[{"text":"Prepare the data first"}]},{"t":"p","runs":[{"text":"Most merge failures are data failures, so clean the CSV before you open the layout."}]},{"t":"ol","start":1,"items":[[{"text":"One header row","b":true},{"text":", with short names and no spaces: "},{"text":"name","code":true},{"text":", "},{"text":"designation","code":true},{"text":", "},{"text":"mobile","code":true},{"text":", "},{"text":"email","code":true},{"text":", "},{"text":"photo","code":true},{"text":"."}],[{"text":"No merged cells, no blank rows, no totals row","b":true},{"text":" at the bottom."}],[{"text":"Consistent case and spacing.","b":true},{"text":" Run a trim on every field; trailing spaces produce mysterious alignment."}],[{"text":"Encoding: UTF-8.","b":true},{"text":" If the data has any Indic script, a name with an accent, or a rupee sign, a file saved as Latin-1 will arrive as mojibake. In LibreOffice, Save As ▸ Text CSV ▸ tick "},{"text":"Edit filter settings","i":true},{"text":" and choose UTF-8."}],[{"text":"Image fields","b":true},{"text":" hold a file path, and InDesign requires the header to begin with "},{"text":"@","code":true},{"text":" ("},{"text":"@photo","code":true},{"text":") with the paths either absolute or relative to the data file."}],[{"text":"Look at the longest record.","b":true},{"text":" Sort by the length of each field and design the template around the longest name, not the first one."}]]},{"t":"h2","id":"doing-it-in-each-program","runs":[{"text":"Doing it in each program"}]},{"t":"p","runs":[{"text":"InDesign","b":true},{"text":": Window ▸ Utilities ▸ Data Merge. Select the data source, place the field placeholders into the frames, then "},{"text":"Create Merged Document","i":true},{"text":". Options that matter: "},{"text":"Records per Document Page","i":true},{"text":" (one card per page, or a grid of ten on an A4 sheet), "},{"text":"Multiple Record Layout","i":true},{"text":" with spacing, and "},{"text":"Preview","b":true},{"text":" before merging, which is how you catch overflow."}]},{"t":"p","runs":[{"text":"CorelDRAW","b":true},{"text":": File ▸ Print Merge. It is good, it handles a grid of cards on a sheet, and it is why so many Indian print shops do this work in CorelDRAW rather than anything else."}]},{"t":"p","runs":[{"text":"Scribus","b":true},{"text":": the "},{"text":"ScribusGenerator","b":true},{"text":" Python script, free on GitHub. Put "},{"text":"%VAR_name%","code":true},{"text":" placeholders in the text, run the script against a CSV, and it produces one output file per record or a combined document. Less polished than the commercial tools and entirely capable."}]},{"t":"p","runs":[{"text":"Free, no layout application at all","b":true},{"text":": generate HTML from the CSV with a short script and render with WeasyPrint. For a thousand labels this is faster than any GUI, and the template is a text file you can version."}]},{"t":"h2","id":"overflow-is-the-failure-mode","runs":[{"text":"Overflow is the failure mode"}]},{"t":"p","runs":[{"text":"A template that fits “Anil Kumar” will not fit “Lakshmi Narayanan Venkataraman”. The merged document will either overset that record silently or push text outside the frame."}]},{"t":"p","runs":[{"text":"Three defences:"}]},{"t":"ul","items":[[{"text":"Design to the longest record, found by sorting the data."}],[{"text":"Set the frame to "},{"text":"auto-size","b":true},{"text":" where the application supports it, or set a minimum type size and allow the style to shrink by a defined amount."}],[{"text":"Check the merged output","b":true},{"text":", not the template. Which is the next section."}]]},{"t":"h2","id":"qr-codes-and-barcodes-per-record","runs":[{"text":"QR codes and barcodes per record"}]},{"t":"p","runs":[{"text":"A QR code per row is the most common request after names."}]},{"t":"ul","items":[[{"text":"InDesign","b":true},{"text":" has a built-in QR code generator (Object ▸ Generate QR Code) but it does not read from a data field, so the practical route is to generate the images first and merge them as an "},{"text":"@image","code":true},{"text":" field."}],[{"text":"Generate them free","b":true},{"text":" with the Python "},{"text":"qrcode","code":true},{"text":" library or "},{"text":"qrencode","code":true},{"text":" on the command line, writing one SVG per row named after a key in the data:"}]]},{"t":"code","lang":"bash","label":"Bash","text":"while IFS=, read -r id url; do qrencode -t SVG -o \"qr/$id.svg\" \"$url\"; done < links.csv"},{"t":"ul","items":[[{"text":"Barcodes","b":true},{"text":": "},{"text":"zint","code":true},{"text":" is free, handles EAN, Code 128, DataMatrix and the rest, and outputs vector. Get the check digit right; a barcode with a wrong check digit scans as nothing."}],[{"text":"Always "},{"text":"test a printed sample with a real scanner","b":true},{"text":" before running 5,000. Quiet zone, contrast and minimum module size all matter, and a QR code printed too small or over a pattern will not read."}]]},{"t":"h2","id":"proof-the-merge-by-sampling","runs":[{"text":"Proof the merge by sampling"}]},{"t":"p","runs":[{"text":"Nobody checks 500 merged records. Check these six:"}]},{"t":"ol","start":1,"items":[[{"text":"The first record."}],[{"text":"The last record."}],[{"text":"The longest name."}],[{"text":"The shortest name."}],[{"text":"Any record with an unusual character: an apostrophe, a hyphen, a non-Latin script."}],[{"text":"One at random from the middle."}]]},{"t":"p","runs":[{"text":"If all six are right, the merge is almost certainly right. If any is wrong, the fault is systematic and you have found it before printing."}]},{"t":"h2","id":"pricing-it","runs":[{"text":"Pricing it"}]},{"t":"p","runs":[{"text":"Charge for the setup, not for the records. The template is two to four hours; the merge itself takes minutes. A client comparing you against “someone who types 500 cards” is comparing against three days of labour, and the honest pitch is that you are faster, cheaper and will not make a typo on card 312."}]}],"check":{"question":[{"text":"A merge of 500 name badges produces correct output except that about thirty records show their names as strings of question marks and boxes. The template is fine and those thirty names are all in Devanagari. What is the most likely cause?"}],"options":[{"text":[{"text":"The Devanagari text needs a shaping engine the merge process bypasses, so conjuncts fail to form."}],"correct":false,"tempting":[{"text":"Failed shaping produces misplaced matras or split conjuncts, not question marks and boxes, and the characters would still be Devanagari."}]},{"text":[{"text":"The CSV was saved in a single-byte encoding, so the Devanagari code points were lost before the layout saw them."}],"correct":true,"tempting":null},{"text":[{"text":"The template’s font has no Devanagari glyphs, so every character falls back to the missing-glyph box."}],"correct":false,"tempting":[{"text":"A reasonable second guess, but a missing-glyph fallback gives uniform boxes, whereas the mix of question marks and boxes points to data corrupted before rendering."}]},{"text":[{"text":"The image field paths for those records were absolute rather than relative, so the merge substituted placeholder characters."}],"correct":false,"tempting":[{"text":"Image paths have nothing to do with text rendering, and a broken link produces a missing image, not corrupted text."}]}],"order":[0,3,1,2],"why":null},"prev":{"slug":"contents-and-cross-references","title":"Contents lists and cross-references that renumber themselves","minutes":8},"next":{"slug":"books-signatures-and-binding","title":"Page counts, creep and the spine","minutes":8}}}