strtolower(trim((string)$h)), $hdr);
foreach (['data1','data2','data3','data4','barcode'] as $req) {
if (!in_array($req, $headers, true)) $errors[] = "Missing required header: {$req}.";
}
if ($errors) { fclose($fh); return []; }
while (($row = fgetcsv($fh, 0, $delimiter)) !== false) {
if ($row === [null] || $row === false) continue;
if (count(array_filter($row, fn($v) => trim((string)$v) !== '')) === 0) continue; // skip blank line
$assoc = [];
foreach ($required as $key) {
$idx = array_search($key, $headers, true);
$assoc[$key] = $idx !== false && isset($row[$idx]) ? trim((string)$row[$idx]) : '';
}
if (implode('', $assoc) === '') continue;
$rows[] = $assoc;
}
fclose($fh);
if (empty($rows)) $errors[] = "CSV contained no usable rows after the header.";
return $rows;
}
function render_datamatrix_svg(string $content): string {
global $autoloadOk;
if (!$autoloadOk) return '';
$barcode = new Barcode();
$obj = $barcode->getBarcodeObj(
'DATAMATRIX',
$content,
-1, -1, 'black', [0,0,0,0]
);
$obj->setBackgroundColor('white');
return $obj->getSvgCode();
}
/* ---------------- Sheet Template System ---------------- */
// Default preset: Demco 1.5" x 1.0" on Letter (guess: 5x8 grid, 0.5" margins, 0.125" gaps).
// You can adjust these in the UI and re-preview until alignment is perfect.
$defaults = [
'page_width_in' => 8.5,
'page_height_in' => 11.0,
'margin_top_in' => 0.5,
'margin_right_in' => 0.5,
'margin_bottom_in' => 0.5,
'margin_left_in' => 0.5,
'cols' => 5,
'rows' => 8,
'label_w_in' => 1.5,
'label_h_in' => 1.0,
'gap_h_in' => 0.125, // horizontal gap between columns
'gap_v_in' => 0.125, // vertical gap between rows
'barcode_h_in' => 0.30, // barcode height inside 1.0" label
'font_in' => 0.12, // approx 8.6pt
'show_outlines' => '1'
];
// Pull current template params from POST (template form) or keep defaults
function valf($name, $def) {
return isset($_POST[$name]) && $_POST[$name] !== '' ? $_POST[$name] : $def;
}
$page_width_in = (float) valf('page_width_in', $defaults['page_width_in']);
$page_height_in = (float) valf('page_height_in', $defaults['page_height_in']);
$margin_top_in = (float) valf('margin_top_in', $defaults['margin_top_in']);
$margin_right_in = (float) valf('margin_right_in', $defaults['margin_right_in']);
$margin_bottom_in = (float) valf('margin_bottom_in', $defaults['margin_bottom_in']);
$margin_left_in = (float) valf('margin_left_in', $defaults['margin_left_in']);
$cols = max(1, (int) valf('cols', $defaults['cols']));
$rows = max(1, (int) valf('rows', $defaults['rows']));
$label_w_in = (float) valf('label_w_in', $defaults['label_w_in']);
$label_h_in = (float) valf('label_h_in', $defaults['label_h_in']);
$gap_h_in = (float) valf('gap_h_in', $defaults['gap_h_in']);
$gap_v_in = (float) valf('gap_v_in', $defaults['gap_v_in']);
$barcode_h_in = (float) valf('barcode_h_in', $defaults['barcode_h_in']);
$font_in = (float) valf('font_in', $defaults['font_in']);
$show_outlines = isset($_POST['show_outlines']) ? '1' : $defaults['show_outlines'];
// Records pipeline (single or CSV)
$errors = [];
$records = [];
$isPosted = ($_SERVER['REQUEST_METHOD'] === 'POST');
$data1 = $_POST['data1'] ?? '';
$data2 = $_POST['data2'] ?? '';
$data3 = $_POST['data3'] ?? '';
$data4 = $_POST['data4'] ?? '';
$barcodeText = $_POST['barcode'] ?? '';
$mode = $_POST['mode'] ?? '';
if ($isPosted && $mode === 'csv') {
if (!isset($_FILES['csvfile']) || !is_uploaded_file($_FILES['csvfile']['tmp_name'])) {
$errors[] = "Please choose a CSV file to upload.";
} else {
$size = $_FILES['csvfile']['size'] ?? 0;
if ($size > 10 * 1024 * 1024) { $errors[] = "CSV too large (max 10MB)."; }
else { $records = read_csv_assoc($_FILES['csvfile']['tmp_name'], $errors); }
}
} elseif ($isPosted && $mode === 'single') {
if ($barcodeText === '') $errors[] = "Please provide a Barcode value.";
else {
$records[] = ['data1'=>$data1,'data2'=>$data2,'data3'=>$data3,'data4'=>$data4,'barcode'=>$barcodeText];
}
}
// Single-label physical dimensions (as fed by the label printer: 3.7cm wide × 2.5cm tall)
// Content is rotated 90° so it reads correctly when applied to a spine.
$single_feed_w_cm = 3.7;
$single_feed_h_cm = 2.5;
// After rotation, the visible label area is 2.5cm wide × 3.7cm tall
$single_label_w_cm = 2.5;
$single_label_h_cm = 3.7;
$is_single = ($mode === 'single' && !empty($records));
// Compute pagination (CSV/sheet mode only)
$perPage = $cols * $rows;
$pages = [];
if ($records && !$is_single) {
$pages = array_chunk($records, $perPage);
}
?>
Create spine labels with tracking barcodes.
composer require tecnickcom/tc-lib-barcode