Document Automation

Column Mapping for PDF Templates: Stop Renaming Headers Forever

Renato Mateus · Founder, RMMS.Cloud
·8 min read
  • column mapping
  • template
  • Google Sheets
  • PDF generation
  • DocForge

Why ops keeps breaking your "perfect" template

You launch the PDF template. Everyone loves it. Two weeks later finance inserts a column between B and C "just for the new tax field," every invoice now ships with the wrong total, and the spreadsheet owner is blamed for "breaking the export."

This is not a finance problem; it's a binding problem. The template referenced column letters (B, C) instead of the meaning of the data. The fix is header-based mapping with explicit, versioned bindings.

Three mapping models, only one survives

  • Positional (avoid): placeholder reads B2; breaks on any column insertion.
  • Header-keyed (better): placeholder reads row["Customer Name"]; survives reorder but breaks on rename.
  • Mapped (best): a stored mapping links {{customer_name}} → header Customer Name; rename the header, update the mapping once, all templates keep working.

The mapping editor needs three jobs

  1. Discover headers: read the first non-empty row of the selected sheet and list candidates.
  2. Bind placeholders: for each template variable, pick the header (or constant) that fills it.
  3. Detect drift: warn when a stored binding references a header that no longer exists; offer the closest match by fuzzy distance.

Type coercion is part of mapping

A column that looks like "1,234.56" might be a string in the sheet but needs to be a number in the PDF for formatting and totals. The mapping carries a type per variable:

  • number with locale-aware parsing (comma vs dot decimal).
  • date with explicit input/output format—never let JS auto-parse.
  • currency bundles amount + currency code; downstream formatting picks the right symbol.
  • boolean normalizes "yes/no", "true/false", "1/0".

Conditional sections kill duplicated templates

Teams clone the template "for the version with shipping" or "for the EU version." Each clone drifts. A robust mapping supports flags that toggle entire sections:

  • {{#if has_shipping}} ... {{/if}} reads a boolean column.
  • {{#each line_items}} ... {{/each}} repeats a nested table.
  • {{#if region == "EU"}} ... {{/if}} shows the VAT block only when needed.

Mapping versioning saves audits

The mapping itself should be versioned—stored alongside the template with a hash. When a PDF is generated, the run log records template version and mapping version. If a regulator asks "why did this invoice look different in March vs. May?", you have the answer.

Real-world test cases to add

  • Insert a column between two existing ones—batch must still work.
  • Rename a header to a typo—generator warns instead of silently shipping wrong data.
  • Add 1,000 rows with mixed locales—numbers and dates format correctly.
  • Hide a column—generator still resolves it.

Where DocForge fits

DocForge uses header-keyed, versioned mapping with type coercion, conditional sections, and drift detection—so the day finance reorders the sheet, the batch keeps shipping. Install on Google Workspace and stop apologizing for "the export."