Programmatically Generating Staff Badges from CSV with ImageMagick

Programmatically Generating Staff Badges from CSV with ImageMagick

Tags
PHPImageMagick

I was given the task to design and produce staff badges. This is typically a print task I’d do in Indesign, but I was hope was to automate the entire process so that I could send data via API (or CSV) from our CRM directly to PDF ready for production. (Additionally, Indesign Data Merge doesn’t handle dual-sided imposition for when you need two sided name badges).

Pseudo-Code Overview

Read/Parse CSV → Generate Name Badge Images (individually) → Comp them together into PDF → Send to print server ✨

// read the csv file
$csv = parseCSV("listOfUsersBadges.csv");

// loop through results and draw each card
foreach($csv as $row){

	// create image from template
	$image = new Imagick();
	$image->readImage("template.png");

	// create new draw object
	$draw = new ImagickDraw();

	// align text to center
	$draw->setTextAlignment(\Imagick::ALIGN_CENTER);

	// set font (from custom file)
	$draw->setFont('Avenir.ttf');

	// set size and weight
	$draw->setFontSize(120);
	$draw->setFontWeight(551);

	// add first name and last name to image
	$image->annotateImage($draw, 380, 765, 0, $row["First name"]);
	$image->annotateImage($draw, 380, 895, 0, $row["Last name"]);

	// save image out
	$image->writeImages("badge.png", false);

}

Now that we’ve dynamically generated the individual name tags we just need to merge them into a dual-sided document with correct margins, bleed, etc.

$imageDirectory = "Individual Badges";
$backPageLeftOffset = 0;
$cardWidth = 820;
$cardHeight = 1295;

// storage
$imageArray = array();
$cards = array();
$pdfCount = 0;
$pagesArray = array();

// delete pages
$images = glob("$imageDirectory/" . "*.png");

foreach($images as $image)
{
	array_push($imageArray, str_replace("$imageDirectory/", "", $image));
}
// split by every 4 cards to a page
$cards = array_chunk($imageArray, 4);


// loop through each 4 twice

foreach($cards as $cardList){
	//place the front cards
	placeCardsInPDF($cardList);
	//place the back cards
	placeCardsInPDF($cardList, true);
}

function placeCardsInPDF($cards, $reversed = false){
	global $pdfCount, $backPageLeftOffset, $pagesArray, $cardWidth, $cardHeight;
	$pdfCount++;
	$imageLeftMargin = 435;
	$imageTopMargin = 20;

	$spaceBetweenImages = 8;		
	
	$image = new Imagick();
	$image->readImage("Templates/Tag Template.png");
	
	// top left
	$image = compositeImage($image, $cards[($reversed ? 1 : 0)], ($reversed ? $backPageLeftOffset : 0) + $imageLeftMargin, $imageTopMargin);
	// top right
	$image = compositeImage($image, $cards[($reversed ? 0 : 1)], ($reversed ? $backPageLeftOffset : 0) + $imageLeftMargin + $spaceBetweenImages + $cardWidth, $imageTopMargin);
	//bottom left
	$image = compositeImage($image, $cards[($reversed ? 3 : 2)], ($reversed ? $backPageLeftOffset : 0) + $imageLeftMargin, $imageTopMargin + $cardHeight + $spaceBetweenImages+5);
	//bottom right
	$image = compositeImage($image, $cards[($reversed ? 2 : 3)], ($reversed ? $backPageLeftOffset : 0) + $imageLeftMargin + $spaceBetweenImages + $cardWidth, $imageTopMargin + $cardHeight + $spaceBetweenImages+5);

	$image->writeImage("page$pdfCount.png");
	array_push($pagesArray, "page$pdfCount.png");
}

function compositeImage($image, $cardName, $x, $y){
	global $cardHeight, $cardWidth, $tagType;
	if(!$cardName){
		return $image;
	}
	$card = new Imagick("$imageDirectory/$cardName");

	$card->scaleImage($cardWidth, 0);
	$padding = new Imagick("Templates/$tagType Padding.png");
	$padding->scaleImage(906, 0);
	$padding->compositeImage($card, Imagick::COMPOSITE_DEFAULT, 43, 43);
	$padding->scaleImage(840, 0);
	$image->compositeImage($padding, Imagick::COMPOSITE_DEFAULT, $x, $y);
	return $image;
}

function combineImages(){
	global $pagesArray;
	$pdf = new Imagick($pagesArray);
	$pdf->setImageFormat('pdf');
	$pdf->writeImages('namebadges.pdf', true);
}

combineImages();

And VOILÀ 🪄 you have a PDF of name badges from the list of names in your CSV file.