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