New in Chilkat v11.7.0

PDF Text Replacement: Pdf.TextReplace

Fill a PDF template by replacing {{placeholders}} and the values after labels such as Invoice #:, directly in the page content. The new text is drawn with the font and size of the text it replaces; a check operation tells you beforehand whether every replacement is possible, and apply is all or nothing.

bool TextReplace(string op, JsonObject jsonIn, JsonObject jsonOut)

How it works, and the font rule

A PDF stores positioned glyphs drawn with particular fonts, not editable text. TextReplace finds each placeholder or label on the assembled lines of the selected pages, encodes the new value with the font the original text uses, recomputes its width from the font's metrics, and rewrites the string operands in the content stream in place.

A replacement is possible only when the new text can be drawn with that font:

  • a standard-14 font (Helvetica, Times, Courier and their bold/italic faces, not embedded) can draw any character its encoding has — the Latin-1 / WinAnsi repertoire, typically;
  • an embedded font can draw any character it has a glyph for — which for a subset font means the characters the document already uses, unless the subset happens to include more;
  • a non-standard font that is not embedded cannot be used: Chilkat has no way to know what it can draw;
  • the whole placeholder or value must be drawn in one font at one size.
Templates work best when the placeholder text is drawn in a standard-14 font or a fully embedded (not subset) font. A template produced by a word processor usually embeds subsets; if the value needs a character the subset lacks, check reports it as missingGlyph with the characters concerned.

The operations: check and apply

opWhat it doesReturns
checkFinds every placeholder and label on the selected pages and applies the font rule. Changes nothing.true when the check ran; jsonOut.ok says whether apply would succeed, and problems lists what would prevent it.
applyThe same, then makes every replacement when there are no problems. All or nothing: if any replacement fails the font rule, the PDF is unchanged.true and jsonOut.applied = true when the replacements were made; false with applied = false and the problems listed otherwise.

The replacements are made in memory and are written when WriteFile or WriteBd is called. The PDF is then saved as a full rewrite, so that the old values do not survive in an earlier revision of the file (as they would in an incremental update).

Quick start: fill a template

Chilkat.Pdf pdf = new Chilkat.Pdf();
pdf.LoadFile("invoice_template.pdf");

Chilkat.JsonObject jsonIn = new Chilkat.JsonObject();
Chilkat.JsonObject jsonOut = new Chilkat.JsonObject();
jsonIn.UpdateString("placeholders.customer", "Acme Corp");
jsonIn.UpdateString("placeholders.date", "2026-10-18");
jsonIn.UpdateString("placeholders.total", "$1,234.56");
jsonIn.UpdateString("labels[0].label", "Invoice #:");
jsonIn.UpdateString("labels[0].value", "INV-2026-0412");

if (!pdf.TextReplace("apply", jsonIn, jsonOut)) {
    Console.WriteLine(jsonOut.Emit());      // the problems
    Console.WriteLine(pdf.LastErrorText);
    return;
}
pdf.WriteFile("invoice_0412.pdf");

jsonIn

{
  "placeholders": {
    "customer": "Acme Corp",
    "date": "2026-10-18",
    "total": "$1,234.56"
  },
  "labels": [
    { "label": "Invoice #:", "value": "INV-2026-0412" }
  ]
}

jsonOut

{
  "applied": true,
  "replacements": [
    { "field": "customer", "pages": "1", "count": 1 },
    { "field": "date", "pages": "1", "count": 1 },
    { "field": "total", "pages": "1", "count": 1 },
    { "label": "Invoice #:", "pages": "1", "count": 1 }
  ],
  "warnings": [],
  "problems": [],
  "notReplaced": []
}

Placeholders

MemberValues (default in bold)Description
pagesall, first, last, odd, even, or a range list such as 1,3,5-9,12-The pages searched.
placeholdersobjectMember names are placeholder names, values the text to put in. A value may be a string, number or boolean; null means an empty string. Every occurrence on the selected pages is replaced.
placeholderSyntaxstring ({{name}})The delimiters around a placeholder name, written around the word name: [[name]], <<name>>, ${name} ...

Placeholder names are matched exactly (case-sensitive) after trimming the spaces inside the delimiters, so {{ customer }} and {{customer}} are the same placeholder.

Example: square-bracket placeholders, numbers and booleans as values

jsonIn

{
  "placeholderSyntax": "[[name]]",
  "placeholders": {
    "qty": 12,
    "unitPrice": "9.50",
    "expedited": true,
    "notes": null
  }
}

Effect on the page

Quantity: [[qty]]           ->  Quantity: 12
Unit price: [[unitPrice]]   ->  Unit price: 9.50
Expedited: [[expedited]]    ->  Expedited: true
Notes: [[notes]]            ->  Notes: 

Labels

A label is text already on the page, such as Invoice #: or Date, that is followed by a value. TextReplace replaces the rest of the label's line with the new value; when nothing follows the label, the value is inserted after it (with a space). Matching ignores case, extra spaces and a trailing colon — the same rules as ExtractText's label.

MemberValues (default in bold)Description
labels[i].labelstringThe label as it appears on the page. Required.
labels[i].valuestringThe new value (may be empty, which clears the old value).
labels[i].occurrencefirst, all, or a numberWhich occurrences are replaced, counted in reading order (top to bottom, left to right within a row) across the selected pages: 2 is the second occurrence.

Example: three labels, one of them every time it occurs

jsonIn

{
  "labels": [
    { "label": "Invoice #:", "value": "INV-2026-0412" },
    { "label": "Date", "value": "October 18, 2026" },
    { "label": "Customer ID:", "value": "C-8841", "occurrence": "all" },
    { "label": "Phone:", "value": "555 0100", "occurrence": 2 }
  ]
}

jsonOut (check)

{
  "applied": false,
  "ok": true,
  "replacements": [
    { "label": "Invoice #:", "pages": "1", "count": 1 },
    { "label": "Date", "pages": "1", "count": 1 },
    { "label": "Customer ID:", "pages": "1,2", "count": 3 },
    { "label": "Phone:", "pages": "2", "count": 1 }
  ],
  "warnings": [],
  "problems": [],
  "notReplaced": []
}
A label whose value is on the next line (Bill To followed by an address block) is not a replaceable value: the rest of the label's line is empty, so the value would be inserted after the label. Use placeholders for such fields.

Alignment

The new text rarely has the width of the old. align decides where it sits:

alignEffect
auto (default)A placeholder keeps its right edge when it is right-aligned with other text on the page (a column of amounts), stays centered when its line is centered on the page, and otherwise keeps its left edge. A label's value is always left-aligned after the label.
leftThe left edge stays where it was.
rightThe right edge stays where it was (amounts, totals).
centerThe center stays where it was (titles).

jsonIn: a column of amounts

{
  "align": "right",
  "placeholders": { "subtotal": "$1,100.00", "tax": "$134.56", "total": "$1,234.56" }
}

Effect

Subtotal     {{subtotal}}        Subtotal      $1,100.00
Tax               {{tax}}   ->   Tax             $134.56
Total           {{total}}        Total         $1,234.56

When the new text is wider than the old and would run into neighbouring text or off the page, the replacement is still made and an overlap warning is reported, so that the application can shorten the value or choose a different template.

Unused placeholders and unmatched values

MemberValues (default in bold)Description
unusedPlaceholderserror, blank, keepA placeholder found on the page that has no value in placeholders: a problem with the reason unusedPlaceholder, replaced by nothing, or left as it is.
unmatchedValueserror, ignoreA value whose placeholder or label is not found on the selected pages: a problem with the reason notFound, or ignored.

Example: a partial fill

jsonIn

{
  "placeholders": { "customer": "Acme Corp" },
  "unusedPlaceholders": "keep",
  "unmatchedValues": "ignore"
}

Effect

Only {{customer}} is replaced; {{date}} and {{total}} stay on the page for a later pass, and a value for a placeholder the template does not have is not an error.

Example: the defaults catch a typo in the template

jsonIn

{ "placeholders": { "customer": "Acme Corp", "total": "$1,234.56" } }

jsonOut (check)

{
  "applied": false,
  "ok": false,
  "replacements": [ { "field": "customer", "pages": "1", "count": 1 } ],
  "warnings": [],
  "problems": [
    { "field": "totl", "reason": "unusedPlaceholder", "page": 1 },
    { "field": "total", "reason": "notFound" }
  ],
  "notReplaced": []
}

The report (jsonOut)

MemberDescription
appliedThe replacements were made (apply only; always false for check).
okcheck only: no problems, so apply would succeed.
replacements[i]One entry per placeholder or label that was (or would be) replaced: field (the placeholder name) or label, pages (such as 1,3) and count.
problems[i]What prevents apply: field or label, reason, page, and for font problems font and chars.
warnings[i]Replacements made (or to be made) that may not look right; the same members as a problem.
notReplaced[i]Placeholders found outside the page content: field and where.

Problems and warnings

reasonMeaningWhat to do
notFoundThe placeholder or label was not found on the selected pages.Check the name, the pages, or set unmatchedValues to ignore.
unusedPlaceholderA placeholder on the page has no value.Supply the value, or set unusedPlaceholders to blank or keep.
missingGlyphThe embedded font has no glyph for one or more characters of the value (chars).Use characters the font has, or a template whose placeholder text is in a standard-14 or fully embedded font.
notInEncodingThe standard font's encoding has no code for one or more characters of the value (chars), such as € or Cyrillic in a WinAnsi-encoded Helvetica.Use characters the encoding has.
nonStandardUnembeddedFontThe text is drawn with a font that is neither standard-14 nor embedded (font), so what it can draw is unknown.Produce the template with embedded fonts.
unsupportedFontThe font cannot be used for new text: a Type 3 font, or a font program that could not be read.Use a different font for the placeholder text.
placeholderSpansFontsThe placeholder or value is drawn with more than one font or size (a word processor often does this at a style boundary), so there is no single font to draw the replacement with.Re-type the placeholder in the template so that it is one run of text.
overlap (warning)The new text runs into neighbouring text or off the page.Shorten the value, or widen the field in the template.

Example: a check that fails the font rule

jsonIn

{ "placeholders": { "customer": "Müller GmbH", "total": "€1.234,56" } }

jsonOut (check)

{
  "applied": false,
  "ok": false,
  "replacements": [],
  "warnings": [],
  "problems": [
    { "field": "customer", "reason": "missingGlyph", "chars": "ü",
      "font": "ABCDEF+OpenSans-Regular", "page": 1 },
    { "field": "total", "reason": "notInEncoding", "chars": "€",
      "font": "Helvetica", "page": 1 }
  ],
  "notReplaced": []
}

Placeholders outside the page content

A template may carry its placeholders in places other than the drawn text. TextReplace reports them in notReplaced and leaves them alone, so that the application can update them with the methods made for them:

whereFound inUpdate it with
metadataThe document's Title, Subject, Author, Keywords, Creator or Producer.UpdateMetadata
linkTargetThe URL of a link annotation.(a later release)
formFieldThe value of a form field.(a later release)
"notReplaced": [
  { "field": "customer", "where": "metadata" },
  { "field": "trackingUrl", "where": "linkTarget" }
]

Saving, signatures and permissions

  • apply changes the PDF in memory; call WriteFile or WriteBd to save it. The file is written as a full rewrite, so earlier revisions holding the old values are discarded.
  • Signed PDFs. Rewriting the page content invalidates signatures, so apply is refused on a signed PDF while the PreserveSignatures property is true (the default). Set it to false to replace text in a signed PDF and accept that its signatures become invalid. check works on any PDF.
  • Encrypted PDFs. The PDF must have been opened (UserPassword / OwnerPassword), and apply requires owner rights or the modify permission. Chilkat does not ignore owner restrictions.
  • After apply, ExtractText returns the new values, and the fonts' ToUnicode maps are extended so that the new text is searchable and copyable in viewers.

Option reference

{
  "pages": "all",
  "placeholderSyntax": "{{name}}",
  "placeholders": { "name": "value", ... },
  "labels": [ { "label": "Invoice #:", "value": "INV-1", "occurrence": "first" }, ... ],
  "align": "auto",                    // auto | left | right | center
  "unusedPlaceholders": "error",      // error | blank | keep
  "unmatchedValues": "error"          // error | ignore
}

Reference documentation: Pdf.TextReplace. Related: ExtractText, Watermark, Protection, Optimize, Pdf class overview.

New in Chilkat v11.7.0.