Technical Article

PDF Text Clipping: Why the Clip Is Applied at ET

Two lines of text are supposed to reveal an image, yet a PDF renderer shows an empty patch. The image can be fine: its visible area is determined by the text clip. PDFlibPas collects the glyph outlines within the text object and applies their combined clipping path at ET, before the image is painted

Text can define a shape as well as paint one

The Tr operator chooses a text rendering mode. Modes 0–3 cover ordinary fill, stroke, both, or neither; modes 4–7 also contribute outlines to the clipping path for the fonts used in this example. Mode 7 is convenient here because the glyphs establish a clip without being painted as visible text themselves. The letters may seem to contain an image, but that image is a later painting operation passing through the shapes left by the text. Font selection, glyph positioning and the text matrix still determine those shapes. One exception deserves attention before applying this explanation to another document. The stream below uses Helvetica, a non-Type 3 font; for Type 3 glyphs, ISO 32000-2 section 9.3.6 specifies that modes 3 and 7 do not paint the glyphs, and modes 4–7 do not add their outlines to the text clipping path

Read the stream from BT through ET

The stream below shows two text operations inside a single BT/ET pair. It assumes /F1 resolves to a usable font and the page's existing graphics state permits drawing at the coordinates shown

q
BT
/F1 48 Tf
7 Tr
72 700 Td (FIRST) Tj
0 -60 Td (SECOND) Tj
ET
0 0 1 rg
0 600 400 160 re f
Q

Read the sequence in order. BT begins the text object; the two Tj operations place the words at different heights; ET closes the object and applies the clip. The blue rectangle is filled afterward, so its visible portions fall within both sets of glyph outlines. The surrounding q and Q protect the rest of the page by saving and restoring the graphics state. ISO 32000-2 section 9.3.6 defines the rendering modes, and the PDF Association's graphics operators reference gives a compact lookup for the operators in the stream

Why combine glyph outlines before clipping?

A tempting implementation intersects the clip with the first line as soon as its glyphs arrive, then intersects that result with the second line. For two vertically separated lines, the intersection can be empty. The subsequent rectangle may be drawn correctly and still leave no visible blue. The text object instead accumulates the participating outlines until ET; only then is their combined area intersected with the clip already in force. Both lines can contribute. The old clip remains important because text cannot expose an area excluded earlier in the graphics state. A single-line page is a weak test of this particular operation—both algorithms can appear correct when there is only one set of glyphs. A producer may split even one visible line into several text-showing operations to adjust positioning or change a font, so the relevant boundary is the whole text object, from BT to its matching ET

Keep the rendering mode fixed while you build the two pages. Mode 7 gives us a text-shaped clip without an ordinary text fill; other clipping modes can also paint the glyphs, making the output look different for a reason unrelated to accumulation. In the example, FIRST and SECOND occupy different vertical positions, and the rectangle covers both. If a line disappears, follow the entire text object before changing that rectangle. The /F1 resource matters too: substituting a different font changes the outlines used to build the clipping area

Render the clipping page with PDFlibPas

The first Delphi routine opens the PDF and renders page 1 to a PNG at 144 DPI. When you investigate a change to the renderer, use the same input page, and keep the page dimensions fixed so the letter positions remain easy to identify

uses
  SysUtils, PDFlibrary;

procedure RenderClipSample(const PdfFile, ImageFile: string);
var
  Pdf: TPDFlib;
begin
  Pdf := TPDFlib.Create;
  try
    if Pdf.LoadFromFile(PdfFile, '') <> 1 then
      raise Exception.Create('Cannot open the PDF');
    if Pdf.RenderPageToFile(144, 1, PDF_RENDER_PNG, ImageFile) <> 1 then
      raise Exception.Create('Cannot render page 1');
  finally
    Pdf.Free;
  end;
end;

The second routine creates either stream from an existing PDF. Its first page needs a valid /F1 font resource and a page box covering the listed coordinates. Because you are replacing page content with this routine, pass a distinct output filename and use a source document prepared for this exercise

procedure WriteClipSample(const SourcePdf, OutputPdf: string;
  AddSecondLine: Boolean);
var
  Pdf: TPDFlib;
  Content: AnsiString;
begin
  Pdf := TPDFlib.Create;
  try
    if Pdf.LoadFromFile(SourcePdf, '') <> 1 then
      raise Exception.Create('Cannot open the source PDF');
    if Pdf.SelectPage(1) <> 1 then
      raise Exception.Create('Cannot select page 1');
    Content := 'q'#10'BT'#10'/F1 48 Tf'#10'7 Tr'#10 +
      '72 700 Td (FIRST) Tj'#10;
    if AddSecondLine then
      Content := Content + '0 -60 Td (SECOND) Tj'#10;
    Content := Content + 'ET'#10'0 0 1 rg'#10 +
      '0 600 400 160 re f'#10'Q'#10;
    if Pdf.SetPageContentFromString(Content) <> 1 then
      raise Exception.Create('Cannot set page content');
    if Pdf.SaveToFile(OutputPdf) <> 1 then
      raise Exception.Create('Cannot save the sample PDF');
  finally
    Pdf.Free;
  end;
end;

Call WriteClipSample twice: use False for the one-line version and True for the two-line version. Give each output its own filename, then pass those PDFs to RenderClipSample. Both pages use the same font resource, rectangle and graphics-state operators; the added second Tj is the change under examination

What else can restrict the clipping path?

Before investigating image compression or color conversion, substitute a solid rectangle and follow the graphics state around the text. Is the fill visible now? The font resource and text matrix determine which outlines are available and where they land; a wrong mapping or an unexpected transform can place the clip away from the rectangle. Check for an earlier path clip too. A Q executed before the fill can restore a different graphics state, so trace the nesting of q/Q alongside BT/ET. Once the rectangle appears through the letters, put the image or shading back and investigate its own transparency and color data. We would start with the solid fill: it removes image decoding from the geometry question and makes the boundary of each letter easy to see

Render one line and two

We rendered the two PDF pages with Helvetica at 144 DPI. The first PNG showed blue inside FIRST; in the second, blue appeared inside both FIRST and SECOND. Those results came from the test described above. A third page can add a rectangular clip before BT: only glyph regions inside that earlier clip should receive the blue fill. After Q, draw another mark outside the text and check that the restored graphics state leaves it visible. This third case exercises the interaction with an existing clip rather than only accumulation within the text object

For a page extracted from a larger PDF, carry its font resource and inherited page settings into the reduced file. Resource names are resolved in context, so a page that still says /F1 is not equivalent if that name now points to a different font. The same applies to the page box and rotation when two renderings are placed side by side. A crop can hide an otherwise correct second line. If you create a reduced page for diagnosis, keep the original text matrix and the coordinates of both text-showing operations. Moving only the blue rectangle may make the output visible while concealing the original positioning problem. Check the glyph positions before altering the page box; the crop and the text matrix answer different questions. A second line above the visible page boundary can still contribute to the clip even when you cannot see it in the rendered page. For text inside a Form, trace its invocation, transform, graphics state and resource dictionary through to the operation that paints the image or rectangle

The content stream and clipping guide follows the surrounding graphics state; vector graphics and shading covers content that can be painted through a clip. The rendering API used here belongs to PDF Library for Delphi