Skip to content

Adding Content to PDF Pages

gal kahana edited this page May 12, 2023 · 7 revisions

If you read in Creating PDF Pages how to create page, you probably want to know how to add content to them.
The library provides simple methods to add primitives (circle, rectangles etc.), text and images. In addition it allows you to use the PDF operators directly.
The following section discusses some of the primitives drawing methods, as well as usage of the PDF operators directly. If you want to read specifically about text placement, images/pdf files placement go for the relevant other sections.

Simple primitives drawing

You start adding content to a page by asking the PDFWriter to start a content context on the page, like this:
PageContentContext* contentContext = pdfWriter.StartPageContentContext(page);
This statement will start a page content context on the page. in the PDF file, a content stream would start to be written.
now you can use the PageContentContext object to draw items to the page. note that PageContentContext is sharing the content context implementation with XObjectContentContext through the AbstractContentContext. So to look for the possible methods you need to go to AbstractContentContext.

You can use the context high level methods for drawing rectangles, circles, squares or any polygonical paths. The following demonstrates how:

AbstractContentContext::GraphicOptions pathFillOptions(AbstractContentContext::eFill,
                                                            AbstractContentContext::eCMYK,
                                                            0xFF000000);
AbstractContentContext::GraphicOptions pathStrokeOptions(AbstractContentContext::eStroke,
                                                            AbstractContentContext::eRGB,
                                                            AbstractContentContext::ColorValueForName("DarkMagenta"),
                                                            4);
contentContext->DrawRectangle(375,220,50,160,pathFillOptions);
contentContext->DrawRectangle(375,10,50,160,pathStrokeOptions);

Note first the DrawRectangle method. This method draws a rectangle that originates from a point defined by the first 2 parameters. It then has a width and height according to the 3rd and 4th parameters, accordingly.
You can set color and drawing method to this retangle using the 5th parameters which is a structure of the GraphicOptions type. The following properties are available for GraphicOptions

  • EDrawingType drawingType – drawing type, can be either stroke, fill or clip (eStroke, eFill,eClip). Drawing a stroke means that the shape line is drawn. Fill means that the area the shape surrounds is filled with a given color. Clip means that any following drawings will be clipped to the area of the shape.
  • EColorSpace colorSpace – when using fill or stroke, this would define the color space by which the color is set. it can be either rgb (eRGB), cmyk (eCMYK) or gray (eGray).
  • unsigned long colorValue – combined color value. It’s hex value is at the length of the possible color components, where each pair of hex is a component value. for example, if the colorspace is eRGB, then the value of 0xFFCCDD, means 255 for red, 205 for green and 221 for blue.
  • double strokeWidth – for stroke drawing type, strokeWidth defines the width of the line
  • bool close – when drawing shapes that are not closed (we’ll get to that when we’ll draw polygonial paths), then the close parameter tells the library to connect the first and last points of the path, if it is not closed.

You can also draw squares, using the following method:

contentContext->DrawSquare(375,640,120,pathFillOptions);
contentContext->DrawSquare(375,440,120,pathStrokeOptions);

Using DrawSquare is very similar to using DrawRectangle, only that it has a single edge parameter, instead of width/height…because they are the same.

Drawing circles is carried out using the DrawCircle method. The following is an example:

contentContext->DrawCircle(149,300,80,pathFillOptions);
contentContext->DrawCircle(149,90,80,pathStrokeOptions);

The first and second parameters are the position of the center. The 3rd parameter is the radius.

Last but not least, you can draw polygonial paths using the DrawPath method, like this:

DoubleAndDoublePairList pathPoints;
pathPoints.push_back(DoubleAndDoublePair(75,640));
pathPoints.push_back(DoubleAndDoublePair(149,800));
pathPoints.push_back(DoubleAndDoublePair(225,640));
contentContext->DrawPath(pathPoints,pathFillOptions);

DrawPath first parameter is a list of points, where each point is defined by a pair of doubles. As always, the last parameter is an options structure.

When you are done adding content to the page, just call EndPageContentContext. like this:
pdfWriter.EndPageContentContext(contentContext);

After finishing with writing the content, it will be a good idea to now write the page object, although it can be delayed for even later…don’t worry, the page content will be maintained.

For a complete example of using the high level methods and context see here.

Using PDF operators

Each page defines content in one or more streams, each stream content contains a sequence of calls to PDF operators.
The library objects allow you to add content to pages objects by calling methods which have the same names as matching PDF operators. you should refer to the PDF reference for guidance of what the operators do (go to the downloads section here, or go to Adobe’s site). These commands can be used to draw more sophisiticated graphics than the above defined primitives.

You place operators using the same object used for the simple primitives drawing – the content context.

For example, to draw a square in cyan color use the following code:

// draw a 100X100 points cyan square
contentContext->q();
contentContext->k(100,0,0,0);
contentContext->re(100,500,100,100);
contentContext->f();
contentContext->Q(); 

this sequence of commands saves the current graphic state, then sets the color to CMYK based cyan, then draws a rectangle which bottom left cornet is 100,500 and dimensions are 100 in width and 100 in height. Finally it restores the graphic state.

When you are done adding content to the page, just call EndPageContentContext. like this:
pdfWriter.EndPageContentContext(contentContext);

let’s see all the code together, including the page object creation:

PDFPage* page = new PDFPage();
page->SetMediaBox(PDFRectangle(0,0,595,842));
PageContentContext* contentContext = pdfWriter.StartPageContentContext(page); 
contentContext->q();
contentContext->k(100,0,0,0);
contentContext->re(100,500,100,100);
contentContext->f();
contentContext->Q();
pdfWriter.EndPageContentContext(contentContext);
pdfWriter.WritePageAndRelease(page); 

One thing you might want to do is to interrupt the writing of a page content stream. This is normally useful if you are writing a page content, and then realize that you need to write a new object, in order to use in the page stream. For example, if you need to write a form xobject description while writing the page, and that form is used in the page.
What you can do is use the fact that PDF pages can contain multiple content stream. The library allows you to temporarily stop writing content to the page, and restart writing later. This is implemented by stopping the write of one content stream and when writing resumes start a new one.
To temporarily pause writing a content stream, use the PausePageContentContext method of PDFWriter. For example, the following code starts by drawing a rectangle in cyan (like the example above), then pauses the stream, and then continue with drawing a red rectangle. This sequence will result in two content streams for the page:

PageContentContext* contentContext = pdfWriter.StartPageContentContext(page); 
contentContext->q();
contentContext->k(100,0,0,0);
contentContext->re(100,500,100,100);
contentContext->f();
contentContext->Q();
// pause writing
pdfWriter.PausePageContentContext(contentContext);
// continue by simply issuing more drawing commands on the context
contentContext->q();
contentContext->k(0,100,100,0);
contentContext->re(200,600,200,100);
contentContext->f();
contentContext->Q();
// finally end the content
pdfWriter.EndPageContentContext(contentContext);

For a complete code sample of creating simple content for a page, check SimpleContentPageTest

Note that advanced users can inject code directly to the content context. Use the WriteFreeCode method (2 overloads) of AbstractContentContext :

void WriteFreeCode(const string& inFreeCode);
void WriteFreeCode(IByteReader* inFreeCodeSource);

Another extensibility option for AbstractContentContext is to listen to its “q” and “Q” pairs. “q” is used for saving the graphic state, and “Q” to restore it. You may wish to track calls to them in order to implement your graphic state management in case you are implementing complex graphic features (I use it for font support). In this case you want to check out the IContentContextListener interface, which provides events on both. You should implement it in your class, and then add/remove it from the AbstractContentContext object using:

void AddContentContextListener(IContentContextListener* inExtender);
void RemoveContentContextListener(IContentContextListener* inExtender);

An interesting part of adding content to a page is adding text. Look in Text support for more information on text placement.

Clone this wiki locally