Patterns in PDF

From
Revision as of 17:26, 26 February 2018 by Adminko (talk | contribs) (Shading patterns)
Jump to: navigation, search

Pattern Colorspace

While everyone can imagine what a pattern is, a pattern colorspace looks very strange from the first sight. PDF treats patterns as regular colors defined in so-called Pattern Colorspace. Therefore if something needs to be filled or stroked with the pattern, this pattern is being selected as either non-stroking or stroking color and the current colorspace is being set to a special type Pattern.

Tiling patterns

In PDF, it’s possible to draw repeating content using objects called tiling patterns, and for the detailed explanation see section 8.7.3 “Tiling Patterns” of the specification. Tiling patterns can be of two types, colored and uncolored. The first one contains all color information needed to draw itself and is a self-contained entity while the second allows you to specify custom colors for its stroking and non-stroking operations making it context dependent.

The code below draws objects using both types of tiling patterns on PDF page one by one:

// open and load the file
using (FileStream outputStream = new FileStream("patterns.pdf", FileMode.Create))
{
    // create new PDF document
    using(FixedDocument document = new FixedDocument())
    {
        Page page = new Page(new PageBoundary(Boundaries.A4));
        document.Pages.Add(page);
                
        // set stroking settings
        page.Content.SetLineWidth(2);
        page.Content.SetLineDashPattern(new float[] { 2, 2, 4, 2 }, 3);
        page.Content.SetLineCapStyle(LineCapStyle.Round);
        page.Content.SetLineJoinStyle(LineJoinStyle.Bevel);                

        // draw colored pattern
        DrawColoredTilingPattern(page, document);

        // draw uncolored pattern
        DrawUncoloredTilingPattern(page, document);

        document.Save(outputStream);
    }
}
 

Corresponding drawing functions for each pattern type are defined in the subsections below.

Colored

This pattern contains all color information needed to draw itself and doesn’t require external parameters to be set for drawing.

See the code below:

private static void DrawColoredTilingPattern(Page page, FixedDocument document)
{
    // Create and register colored tiling pattern object
    string colorPatternId = "ColoredTilingPattern";

    TilingPattern coloredPattern = new TilingPattern(colorPatternId, new Boundary(0, 0, 30, 20), 30, 20);

    // path defining pattern content
    Path patternPath = new Path();
    patternPath.AppendRectangle(-15, -5, 25, 7);
    patternPath.AppendRectangle(15, -5, 25, 7);
    patternPath.AppendRectangle(-15, 15, 25, 7);
    patternPath.AppendRectangle(15, 15, 25, 7);
    patternPath.AppendRectangle(0, 5, 25, 7);

    // set gray color as fill color for the pattern  content
    coloredPattern.Content.SetDeviceNonStrokingColor(0.7);
    coloredPattern.Content.FillAndStrokePath(patternPath);

    // register pattern
    document.ResourceManager.RegisterResource(coloredPattern);

    // Set colored Pattern as fill color for the rect (!)
    page.Content.SetNonStrokingColorSpace(PredefinedColorSpaces.Pattern);
    page.Content.SetNonStrokingColor(colorPatternId);

    // Use RGB color to stroke the rect
    page.Content.SetStrokingColorSpace(PredefinedColorSpaces.RGB);
    page.Content.SetStrokingColor(1, 0.5, 0);

    // Draw rectangle
    Path path = new Path();
    path.AppendRectangle(100, 730, 200, 100);
    page.Content.FillAndStrokePath(path);
}
 

The resulting image showing the rectangle filled using the colored tiling pattern is below.

Colored tiling pattern

Uncolored

Don't be misleaded by the name, these patterns do have colors, but the uncolored tiling patterns differ from the colored in way of specifying its stroking and non-stroking colors. It requires the colorspace to be set that will define the actual colors used inside the pattern. Using Fixed layout API it can be achieved by using one of the predefined pattern colorspaces, e.g. RGBPattern.

The code below draws the uncolored pattern and fills the rect using it:

private static void DrawUncoloredTilingPattern(Page page, FixedDocument document)
{
    // Create and register uncolored tiling pattern object
    string uncoloredPatternId = "UnColoredTilingPattern";

    TilingPattern uncoloredPattern = new TilingPattern(uncoloredPatternId, new Boundary(0, 0, 30, 20), 30, 20, false);

    // path defining pattern content
    Path patternPath = new Path();
    patternPath.AppendRectangle(-15, -5, 25, 7);
    patternPath.AppendRectangle(15, -5, 25, 7);
    patternPath.AppendRectangle(-15, 15, 25, 7);
    patternPath.AppendRectangle(15, 15, 25, 7);
    patternPath.AppendRectangle(0, 5, 25, 7);
            
    uncoloredPattern.Content.FillAndStrokePath(patternPath);

    // register pattern
    document.ResourceManager.RegisterResource(uncoloredPattern);

    // Set uncolored Pattern as fill color space for the rect and RGB colorspace for its internal colors (!)
    page.Content.SetNonStrokingColorSpace(PredefinedColorSpaces.RGBPattern);
    // set pattern as fill color using its id and also set its internal fill color 
    page.Content.SetNonStrokingColor(uncoloredPatternId, 0.1, 0.7, 0.2);
    // set stroking color as device RGB
    page.Content.SetDeviceStrokingColor(1,0.5,0);
            
    // Draw rectangle
    Path path = new Path();
    path.AppendRectangle(350, 730, 200, 100);
    page.Content.FillAndStrokePath(path);
}
 

You see that after setting the RGBPattern colorspace as a non-stroking colorspace, we also set our pattern as the current non-stroking color along with specifying the internal fill color for it.

The resulting image that shows the rectangle filled with the uncolored tiling pattern is below:

Uncolored tiling pattern

Uncolored patterns are a good way to reuse various repeating drawings and make them use different stroking and non-stroking colors assigned on demand.

Shading patterns

Shading patterns provide a smooth transition between colors across an area to be painted, independent of the resolution of any particular output media and without specifying the number of steps in the color transition. Patterns of this type can be used to create complex fills and have several subtypes described in section 8.7.4.5 “Shading Types” of the specification. Using the API provided by Apitron PDF Kit one is able to create advanced color gradient fills based on standard linear and exponential interpolation functions as well as pure PostScript function-based color transition effects.

In PDF, the object used to create a complex fill is called Shading, and you may read about all low level details involved in the section 8.7.4 Shading Patterns of the PDF specification. Every Shading object requires a function object that defines the color transformation needed to create the final color at the each point where the shading is defined. Functions are described in section 7.10 Functions of the PDF specification. Using various function types it becomes possible to implement linear, bilinear and exponential interpolation as well as other color effects. One can use different functions to calculate the final value for each color component, or use the same function to calculate all of them. Currently available function types are: Sampled, Exponential, StitchingFunction and PostScript. Shading types available are: axial, radial and function-based.

When the area to be painted has a relatively simple form and matches that of the gradient fill itself(think of a fill as a separate graphical object having desired appearance and overlapping the area to be filled) or you have a simple clipping path for it, the ClippedContent::PaintShading() function may be used instead of the usual painting approach. It accepts a Shading object as an operand and applies the corresponding gradient fill directly to the current user space applying the additional clipping path given as a parameter if needed.

Another way to use shadings is to define a ShadingPattern. Similar to the TilingPattern object, it can be used as a color for filling and stroking operations if you set the current stroking or non-stroking colorspace to PredefinedColorpaces::Pattern. This way you’ll be able to draw whatever you want using the specified shading pattern as a color provider for the stroked or filled path(or both). Both methods have their own pros and cons and it’s up to you to choose the one that works best in your situation. Sections below describe various functions types and shadings types in further details and sample code.

Axial Shading based on Exponential interpolation function

In the code sample below we define the axial shading object that does what it name says - defines a color blend along the straight line between the two points, optionally extended beyond the boundary points by continuing(repeating) the boundary colors. The Exponential interpolation function is being used to calculate the color transition.

The formula used to produce each color sample is yj = C0j + xN × (C1j − C0j), and input and output values are limited by the domain and range intervals ([0,1] in this case).

// creates and registers axial shading object based on exponential function
private static Shading CreateAndRegisterAxialShadingBasedOnExponentialFunction(FixedDocument doc, Color beginColor, Color endColor)
{
    // exponential function producing the gradient
    Function expFn = new ExponentialFunction(Guid.NewGuid().ToString(), beginColor.Components, 
        endColor.Components, 3, new double[] { 0, 1 });            

    // axial shading demonstrating exponential interpolation between two colors
    AxialShading axialShadingExp = new AxialShading( Guid.NewGuid().ToString(),
        PredefinedColorSpaces.RGB, new Boundary(0, 0, 180, 180), RgbColors.Green.Components, 
        new double[] { 0, 90, 180, 90 }, new string[] { expFn.ID });

    doc.ResourceManager.RegisterResource(expFn);
    doc.ResourceManager.RegisterResource(axialShadingExp);

    return axialShadingExp;
}
 

This shading is defined using the rectangular boundary [0, 0,180,180] and two points which define the interpolation axis (0,90) and (180,90) respectively.