Transparency in PDF

From
Jump to: navigation, search

Blend modes

Blend mode is a setting one may use for the graphics state object that defines how the final color will be calculated based on the predefined formulas. A Normal blend mode is the typical setting and all of them can be found in section 11.3.4 Blend Mode of the PDF specification. It's implemented in the Fixed layout API as theGraphicsState.BlendMode property.

Alpha

Transparent objects can be drawn using the current alpha setting stored in the graphics state object. There are two values of type double ranging from 0.0 to 1.0 and indicating the degree of transparency that should be applied to the filled or stroked content.

The code below illustrates this concept:

using (FileStream outputStream = new FileStream("outfile.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    {
        document.Pages.Add(new Page());
              
        // register graphics state resource
        GraphicsState gs = new GraphicsState("gs01") {CurrentStrokingAlpha = 0.3, CurrentNonStrokingAlpha = 0.3};                
        // register graphics state resource
        document.ResourceManager.RegisterResource(gs);

        Content content = document.Pages[0].Content;

        // select current colors
        content.SetDeviceStrokingColor (new double[]{1,0,0});
        content.SetDeviceNonStrokingColor(new double[] { 0, 1, 0 });

        // create rect path
        Path rect = new Path();
        rect.AppendRectangle(0,0,100,100);

        content.Translate(10,730);
        // fill and stroke this path, it creates an opaque rect
        content.FillAndStrokePath(rect);

        // transparency will be set using the graphics state resource
        content.SetGraphicsState("gs01");
        // select current colors
        content.SetDeviceStrokingColor(new double[] { 0, 1, 0 });
        content.SetDeviceNonStrokingColor(new double[] { 1, 0, 0 });
    
        // draw a transparent rect
        content.Translate(80, -20);
        content.FillAndStrokePath(rect);
    
        // save document
        document.Save(outputStream);
    }
}
 

It produces the following results, see the image below:

Transparent object drawn using Normal blend mode

Transparency in PDF is outlined in section 11 “Transparency” of the specification and describes many other things to consider except the basic alpha settings. As an example, current alpha could be combined with any blending mode described in section 11.3.5 “Blend Mode” to achieve various visual effects. There are also transparency groups and soft mask objects which are explained the the next sections of the current article.

If we would change the blending mode in code sample produced the image above from Normal to Exclusion using the code below:

    // register graphics state resource
    GraphicsState gs = new GraphicsState("gs01") {CurrentStrokingAlpha = 0.3, CurrentNonStrokingAlpha = 0.3, BlendMode = BlendMode.Exclusion};
 

Then results would be as follows:

Transparent object drawn using Exclusion blend mode

Transparency Group XObjects

A Transparency Group XObject is a special type of Group Form XObjects (see the section 8.10.3 “Group XObjects” in PDF specification) that can be used to group graphical elements together and have extended support for transparency-related operations. Transparency groups and their properties are described in section 11.6.6 “Transparency Group XObjects” of the PDF specification. Any group XObject is a form XObject with an additional attribute describing its type. Since there is only one type of Group XObjects defined in PDF specification so far, it only makes sense to discuss transparency groups.

Apitron PDF Kit supports the creation of transparency groups which can be used for various reasons. As an example you may check an article below describing soft masks and use them to create advanced masking effects. Transparency groups can be created using TransparencyGroup class provided by Fixed layout API and their properties can be changed via Attributes property.

The most important properties of a transparency group define whether the group should be treated as “isolated” or “knockout” or both. These terms describe an interaction between group as a whole with its background or/and other elements on page and also the interaction between the objects(drawings) added into this group. These topics are quite complicated and are explained in details in sections 11.2 “Overview of Transparency”, 11.4.5 “Isolated Groups”, 11.4.6 “Knockout Groups” of the PDF specification. See also figures L.16 and L.17 on page 741 of PDF specification.

Draw objects using Transparency Group

The code sample below demonstrates how to use transparency group for drawing several objects at once and its differences from the “normal” drawing when transparency becomes involved. It draws two identical sets of objects over the same image and sets the current alpha state, so that we'll have:

  • a transparency group containing colored rectangles, on the left
  • a set of ungrouped colored rectangles, on the right
// create output file
using (Stream outputStream = File.Create("transparency_group.pdf"))
{
    // create document and add one page to it
    using(FixedDocument fixedDocument = new FixedDocument())
    {
        fixedDocument.Pages.Add(new Page());

        fixedDocument.ResourceManager.RegisterResource(new Apitron.PDF.Kit.FixedLayout.Resources.XObjects.Image("logo", "apitron.png"));
        // create graphics state, it will be used to define group transparency
        GraphicsState graphicsState = new GraphicsState("semiTransparent"){CurrentNonStrokingAlpha = 0.7};
        fixedDocument.ResourceManager.RegisterResource(graphicsState);                            

        // create transparency group and draw several rects inside it
        TransparencyGroup transparencyGroup = new TransparencyGroup("myGroup", new Boundary(0,0,150,150));                
        fixedDocument.ResourceManager.RegisterResource(transparencyGroup);                
                
        // draw colored rects inside the transparency group
        Path path = new Path();
        path.AppendRectangle(20, 20, 50, 50);

        Action<ClippedContent,double, double, double[]> drawRect = (content,tx, ty, color) =>
        {
            content.Translate(tx, ty);
            content.SetDeviceNonStrokingColor(color);
            content.FillAndStrokePath(path); 
        };
                
        drawRect(transparencyGroup.Content,0, 0, new double[] {1, 0, 0});
        drawRect(transparencyGroup.Content,30, 30, new double[] { 0, 1, 0 });
        drawRect(transparencyGroup.Content,30, 30, new double[] { 0, 0, 1 });

        ClippedContent pageContent = fixedDocument.Pages[0].Content;
    
        // draw group over an image (1)
        pageContent.SaveGraphicsState();
        // draw bg image
        pageContent.Translate(10, 680);
        pageContent.AppendImage("logo",0,0,150,150);
        // set state and draw transparency group
        pageContent.SetGraphicsState("semiTransparent");                
        pageContent.AppendXObject("myGroup");
        pageContent.RestoreGraphicsState();

        // draw rects over an image (2)
        pageContent.SaveGraphicsState();
        // draw bg image
        pageContent.Translate(170, 680);
        pageContent.AppendImage("logo", 0, 0, 150, 150);

        // set state  and draw colored rects directly on page
        pageContent.SetGraphicsState("semiTransparent");
        drawRect(pageContent, 0, 0, new double[] { 1, 0, 0 });
        drawRect(pageContent, 30, 30, new double[] { 0, 1, 0 });
        drawRect(pageContent, 30, 30, new double[] { 0, 0, 1 });
        pageContent.RestoreGraphicsState();
        // save document
        fixedDocument.Save(outputStream);
    }
}
 

This code produces the following results:

Draw objects using Transparency Group

Note that rectangles on the left, which were included into the transparency group, were treated as a whole transparent thing, while rectangles on the right were treated as separate transparent objects. This sample demonstrates one of the possible applications of transparency groups.

Soft masks

Using images it's possible to create a stencil mask using raster data as a source for mask values and draw through it with current non-stroking color or apply an explicit mask to achieve the same effect but for the image itself. But stencil masks are quite limited in terms of defining transparency. You can only indicate whether the particular location within the mask is fully transparent or not, making it impossible to define a mask containing semi-transparent areas.

Soft masks, discussed here, are designed for complex masking effects which are impossible to achieve with any other type of masking. The word "soft" emphasizes that the mask value at a given point is not limited to just 0.0 or 1.0 but can be represented by intermediate fractional values as well. Such a mask is typically the only means of providing position-dependent opacity values, since elementary objects do not have the any intrinsic properties specifying opacity. It is fully described in section 11.5 “Soft Masks” of the PDF specification.

A special class in Fixed layout API was designed to work with these masks and it’s called not surprisingly SoftMask, it’s a resource object that in its turn uses TransparencyGroup instance as a source for soft mask values. TransparencyGroup is special kind of a form XObject which is being discussed above (Transparency Group XObjects) and, among other things, can provide soft mask values derived either from its alpha or luminosity calculated for any given point. It implements ClippedContent and therefore can contain the same drawing commands as other descendants like Page for example.

To apply a SoftMask while drawing, one have to create a GraphicsState object and apply it by calling the SetGraphicsState() function on the desired ClippedContent instance before any further drawing occurs.

Create soft mask from an arbitrary drawing

Consider the following code that draws two rectangles. The red one on the left is drawn without any masking and the blue rectangle on the right using the circle-shaped soft mask. Luminosity values are used to derive soft mask from the transparency group containing the drawn circle.

// create output PDF file
using (FileStream outputStream = new FileStream("softmask.pdf", FileMode.Create, FileAccess.Write))
{
    // radius of the circle
    double radius = 100;
    // circle constant
    double r_c = 0.5522847498*radius;
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    {
        document.Pages.Add(new Page());
                                                                               
        // an object to be used as source of soft mask values,
        // these values will be provided by calculating its luminocity at the given point
        SoftMask softMask = new SoftMask("softMask", SoftMaskSubtype.Luminosity, new Boundary(200, 200));
        // set the background color so areas where nothing will be painted will have max luminocity
        softMask.BackgroundColor = new double[]{1};

        // draw to transparency group XObject that soft mask will use to derive values from                
        Content maskContent = softMask.Group.Content;

        // create new path representing a circle
        Path path = new Path(0, radius);
        path.AppendCubicBezier(r_c, radius, radius, r_c, radius, 0);
        path.AppendCubicBezier(radius, -r_c, r_c, -radius, 0, -radius);
        path.AppendCubicBezier(-r_c, -radius, -radius, -r_c, -radius, 0);
        path.AppendCubicBezier(-radius, r_c, -r_c, radius, 0, radius);
                
        // move it to the center of the mask area
        maskContent.Translate(100,100);
        // set current non-stroking color             
        maskContent.SetDeviceNonStrokingColor(new double[] { 0.6 });
        // stroke path
        maskContent.FillPath(path);

        // register soft mask resource
        document.ResourceManager.RegisterResource(softMask);

        // create new graphics state and prepate it to use soft mask
        GraphicsState maskingState = new GraphicsState("gsMask");
        maskingState.SoftMaskResourceID = softMask.ResourceID.ID;
        // register mask state
        document.ResourceManager.RegisterResource(maskingState);
                
        ClippedContent pageContent = document.Pages[0].Content;
        // select current color and drawing origin
        pageContent.SetDeviceNonStrokingColor(new double[] { 1, 0, 0 });                
        pageContent.Translate(10, 630);

        // draw non-clipped RED rect
        Path rect1 = new Path();
        rect1.AppendRectangle(0, 0, 200, 200);
        pageContent.FillAndStrokePath(rect1);   

        pageContent.Translate(100,-20);

        // select current color 
        pageContent.SetDeviceNonStrokingColor(new double[] { 0, 0.5, 0.7 });                
        // apply masking graphics state that sets the soft mask
        pageContent.SetGraphicsState(maskingState.ResourceID.ID);               
                
        // draw BLUE rect using the soft mask set
        Path rect2 = new Path();
        rect2.AppendRectangle(0, 0, 200, 200);
        pageContent.FillAndStrokePath(rect2);                
        document.Save(outputStream);
    }
}
 

The resulting PDF document looks as follows:

Create soft mask from an arbitrary drawing

Applying soft mask to an image

It’s possible to assign a soft mask to an image XObject and it can be done by associating a soft-mask image with it. [[Images in PDF#Soft-mask image|Soft-mask image] is an auxiliary image XObject which resource ID is being assigned to the SoftMaskResourceID property of the parent image XObject. If you'd like to know more about how images are defined in PDF, please read the Images in PDF article.

For the soft-mask image sample see the code below:

using (FileStream outputStream = new FileStream("softmask.pdf", FileMode.Create, FileAccess.Write))
{             
    using(FixedDocument document = new FixedDocument())
    {
        document.Pages.Add(new Page());
        // create image XObject to be used as a mask and register it
        Image softMaskImage = new Image("softMaskImage", "softmask.png");
        document.ResourceManager.RegisterResource(softMaskImage);                    
        // create image XObject and register it
        Image image = new Image("image","image.png");                     
        document.ResourceManager.RegisterResource(image);                     

        // create masked image XObject, register it and set the mask
        Image maskedImage = new Image("maskedImage","image.png");
        // set soft mask resource id to the id of the masking image
        maskedImage.SoftMaskResourceID = softMaskImage.ResourceID.ID;           
        document.ResourceManager.RegisterResource(maskedImage);                            
        // draw images and save file
        document.Pages[0].Content.AppendImage(image.ResourceID.ID,10,680,150,150);
        document.Pages[0].Content.AppendImage(softMaskImage.ResourceID.ID, 170, 680, 150, 150);
        document.Pages[0].Content.AppendImage(maskedImage.ResourceID.ID, 330, 680, 150, 150);                
        document.Save(outputStream);
    }
}
 

The resulting document, shown below, contains three images: original, soft mask and the masked result.

Applying soft mask to an image

Support for transparent images

Apitron PDF Kit provides support for images which already have transparency data included along with the color data. Transparent PNGs or BMPs are a good example. It can be enabled on creation of the image XObject by setting useTransparency parameter to true. It means that you don't have to manually create soft masks for these images, both Fixed layout API and Flow layout API will do it automatically for you.

See the code below:

using (FileStream outputStream = new FileStream("softmask.pdf", FileMode.Create, FileAccess.Write))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument()
    {
        document.Pages.Add(new Page());              
        // create image XObject and set it to use embedded transparency data 
        Image maskedImage = new Image("maskedImage", "colored_bubbles.png",true);
        document.ResourceManager.RegisterResource(maskedImage);                
        // create rect
        Path path = new Path();
        path.AppendRectangle(0,0,150,150);                                

        ClippedContent pageContent = document.Pages[0].Content;
        // draw dark red rect and transparent image over it
        pageContent.Translate(10,680);
        pageContent.SetDeviceNonStrokingColor(new double[]{0.5,0,0});
        pageContent.FillPath(path);
        pageContent.AppendImage(maskedImage.ResourceID.ID,0,0,150,150);
        // draw gray rect and transparent image over it
        pageContent.Translate(160,0);
        pageContent.SetDeviceNonStrokingColor(new double[]{0.5,0.5,0.5});
        pageContent.FillPath(path);
        pageContent.AppendImage(maskedImage.ResourceID.ID,0,0,150,150);
        document.Save(outputStream);
    }
}
 

The images below demonstrate a source image in PNG format with included opacity drawn over two different filled rectangles.

Support for images with opacity data
PNG with opacity data