Images in PDF

From
Jump to: navigation, search

Image XObject

Images in PDF are represented by the special type of XObject called image XObject. Like the form XObjects this type of XObjects can be used to place its content repeatedly when it’s needed, using the single registered instance contained in document's or page's resources.

Image's internal coordinate system in PDF differs from the one defined for drawing paths and other primitives and described in article Graphics System in PDF. It has its Y-axis inverted (and pointing to the bottom), similar to the coordinate system in Windows GDI. It’s important to take this into account for proper positioning of the image using regular drawing commands. We also designed several helper methods taking into account this difference and providing a more convenient way to draw an image.

Standard images in common formats

Apitron PDF Kit supports the following source image formats for creation of image XObjects: BMP, PNG, JPEG, TIFF, JP2. It has a special Image resource class defined in Apitron.PDF.Kit.FixedLayout.Resources.XObjects namespace that can be used to create such XObjects. This class has common properties such as Width, Height, Interpolate etc. accessible for developers. Images and their attributes are discussed in depth in section 8.9 “Images” of the PDF specification.

The code below demonstrates how to use image XObject for drawing images:

// create output PDF file
using (FileStream outputStream = new FileStream("xobjects.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    {
        document.Pages.Add(new Page());
        // create XObject
        Image xObject = new Image("myImageXObject","apitron.png");            

        // register XObject
        document.ResourceManager.RegisterResource(xObject);

        // append two identical images using image XObject
        document.Pages[0].Content.AppendImage("myImageXObject", 10,760, xObject.Width,xObject.Height);
        document.Pages[0].Content.AppendImage("myImageXObject", 100, 760, xObject.Width, xObject.Height);

        // save document
        document.Save(outputStream);
    }
}
 

The result is shown below; please note that we used a special method AppendImage() which performs all transformations needed to draw an image. Usually, drawing an image requires to set current scale transform because image is considered to be of size 1x1 by default. The invoked member AppendImage() is simply a wrapper that does necessary scaling for us and invokes AppendXObject().

Drawing an Image XObject

Masked images

Normally, in the opaque drawing model images occupy all the pixels below it and erase the existing background if it exists. In PDF it is possible to mark certain parts of the image with a special mask and therefore allow the existing color information to remain or use the image as a source of mask values. Different existing approaches to masking are described below. If simple masking is not what you want, then you can always go for more advanced approaches such described in the article related to transparency groups.

Stencil masked

An image itself can be used as a source of mask data marking areas where the current non-stroking color can be painted over and where it’s not allowed. Ideally, to create such mask, this image should be monochrome, but we can also accept color images converting them to black and white if needed. It creates a binary mask which can be used to paint over.

Consider the code below:

// create output PDF file
using (FileStream outputStream = new FileStream("xobjects.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    {
        document.Pages.Add(new Page());

        // create XObject defining a stencil mask
        Image stencilmask = new Image("maskXObject", "stencil_mask.png");
        stencilmask.IsStencilMask = true;
        // register xObject
        document.ResourceManager.RegisterResource(stencilmask);

        // set the non-stroking color
        document.Pages[0].Content.SetDeviceNonStrokingColor(new double[]{0.1,0.5,0.3});
        // draw using current non-stroking color and the mask set
        document.Pages[0].Content.AppendImage("maskXObject", 10, 770, stencilmask.Width,stencilmask.Height);
                                      
        document.Save(outputStream);
    }
}
 

Note that the Image.IsStencilMask property was used to define that the image should be processed as a stencil mask; this code produces the following results:

Using an image as a stencil mask for drawing

Explicitly masked

Explicit masking assumes that you would like to draw an image using a custom mask and it combines stencil masking with regular image by setting stencil mask directly to image XObject.

Let’s see the code:

// create output PDF file
using (FileStream outputStream = new FileStream("xobjects.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    {
        document.Pages.Add(new Page());

        // create XObject defining a mask
        Image stencilmask = new Image("maskXObject", "apitron_mask.png");                
        // register mask xObject
        document.ResourceManager.RegisterResource(stencilmask);                

        // create XObject for the image and assign an explicit mask to it
        Image normalImage = new Image("imageXObject", "apitron.png");
        normalImage.MaskResourceID = "maskXObject";

        // register mask XObject
        document.ResourceManager.RegisterResource(normalImage);
                
        // draw masked image and save file
        document.Pages[0].Content.AppendImage("imageXObject", 10, 770, normalImage.Width, normalImage.Height);
        document.Save(outputStream);
    }
}
 

Here we simply create two image objects and assign the first one, representing the explicit mask, to the image we want to be drawn masked using its MaskResourceID property. Results are as follows, note the “MASK” word cut in the middle of the image:

Applying an explicit mask to an image

Color key masked

It’s possible to define a mask for the image using special color that will be used as a source of data defining its shape. One may use MaskColorRanges property for it.

See the code:

using (FileStream outputStream = new FileStream("xobjects.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    { 
        document.Pages.Add(new Page());

        // create XObject
        Image normalImage = new Image("myImageXObject", "apitron.png");
        Image maskedImage = new Image("myImageXObjectMasked", "apitron.png");
        // masking colors should be specified in image colorspace and include min and max for each channel
        maskedImage.MaskColorRanges = new double[] {210, 220, 90, 115, 85, 115 };

        // register  XObjects
        document.ResourceManager.RegisterResource(normalImage);
        document.ResourceManager.RegisterResource(maskedImage);

        // append normal and masked images one by one
        document.Pages[0].Content.AppendImage("myImageXObject", 10, 760, normalImage.Width, normalImage.Height);
        document.Pages[0].Content.AppendImage("myImageXObjectMasked", 100, 760, maskedImage.Width, maskedImage.Height);

        // save document
        document.Save(outputStream);
    }
} 
 

We specify that RGB colors falling in range [(210, 90, 85), (220,115,115)] should be masked out. The result can be seen on the image below:

Applying color key mask to an image

Inline

As an alternative to the image XObject described in 8.9.5, "Image Dictionaries", an image in PDF may be specified in the form of an inline image. This type of image is being embedded directly into the content object in which it will be painted, rather than as a separate resource object which can be reused or referenced by id. Because this format gives the reader less flexibility in managing the image data, it is usually used only for small images (4 KB or less). This type of image is considered somewhat ineffective, so it's recommended to use image XObjects instead.

Code samples

Create PDF document from a set of images

Sometimes there is a need to create a PDF document from an arbitrary set of scanned receipts, photos, drawings or other useful things stored as images. While this function could be simply implemented by the code that registers a bunch of image XObjects as document's resources, creates new pages and draws images on pages one per page, there is a shortcut one might use for this task. Itäs nothing more than just a nice wrapper for the aforementioned approach.

The Page class from Fixed layout API offers CreateFromImage static method which creates a PDF page object using a given image. It accepts a FromImagePageSettings object instance as in input paramerer defining various page settings such as orientation, padding, border etc.

See the sample code below:

// create output file
using (Stream outputStream = File.Create("from_images.pdf"))
{
    // create document and add one page to it
    FixedDocument fixedDocument = new FixedDocument();
    // create a page from image
    fixedDocument.Pages.Add(Page.CreateFromImage("scan1.jpg",
    fixedDocument.ResourceManager,new FromImagePageSettings(Boundaries.Letter)    
    {
        PageOrientation = PageOrientation.Portrait,
        ScaleMode = ImageScaleMode.PreserveAspectRatio
    }));
    
    // save document
    fixedDocument.Save(outputStream);
}
 

It creates a PDF page from a given image and sets its orientation along with the scale mode.