Patterns in PDF

From
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(axial gradient) 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. See the result.

Axial Shading based on Sampled Function

The axial shading object(axial gradient) that can be created using the code below is based on a sampled function that uses SamplerDelegate to produce the color value for each calculated sample value. Samples count is the number of color components passed as the parameter.

// creates and registers axial shading object based on sampled function able to interpolate 
// between multiple colors 
private static AxialShading CreateAndRegisterAxialShadingBasedOnLinearInterpolationFunction(   
    FixedDocument doc, params object[] colors)
{
    int samplesCount = colors.Length;
    double[] domain = new double[] {0, 1};
    double step = domain[1] / (samplesCount-1);

    // create the delegate returning color value for corresponding sample
    SamplerDelegate fn = (double[] input) => {
                int k = 0;
                double tmpStep = step;                                     
                while (input[0] >= tmpStep)
                {
                    tmpStep += step;
                    ++k;
                }
                return (colors[k] as Color).Components;
            };

    // linear sampled function producing the gradient
    Function linearFn = new SampledFunction(Guid.NewGuid().ToString(), fn, domain , 
        new double[] { 0, 1, 0, 1, 0, 1 }, new[] { samplesCount }, BitsPerSample.OneByte);

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

    doc.ResourceManager.RegisterResource(linearFn);
    doc.ResourceManager.RegisterResource(axialShadingLinear);

    return axialShadingLinear;
}
 

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

Function-based shading based on PostScript function

For the function-based shadings the color at every point of the domain gets defined by the specified mathematical function and this function doesn't have the requirement to be smooth or continuous. This kind is the most generic of the available shading types and can be used to define shadings that can't be adequately described in any other way. We used a separate PostScript function for each of the color components to produce the final color.

// creates and registers function-based shading object
private static Shading CreateAndRegisterFunctionBasedShading(FixedDocument doc)
{
    // create post script functions for each color component
    // it's possible to use only for one function and calculate all components at once
    // the function used for R is 1- ((x-90)/90)^2 + ((y-90)/90)^2)
    PostScriptFunction psFunctionR = new PostScriptFunction(Guid.NewGuid().ToString(), 
        new double[] {0, 180, 0, 180},
        new double[] {0, 1}, "{90 sub 90 div dup mul exch 90 sub 90 div dup mul add 1 exch sub}");
    // the function used for G is 1- ((x-90)/60)^2 + ((y-90)/60)^2)
    PostScriptFunction psFunctionG = new PostScriptFunction(Guid.NewGuid().ToString(), 
        new double[] {0, 180, 0, 180},
        new double[] {0, 1}, "{90 sub 60 div dup mul exch 90 sub 60 div dup mul add 1 exch sub}");
    // the function used for G is 1- ((x-90)/30)^2 + ((y-90)/30)^2)
    PostScriptFunction psFunctionB = new PostScriptFunction(Guid.NewGuid().ToString(), 
        new double[] {0, 180, 0, 180},
        new double[] {0, 1}, "{90 sub 30 div dup mul exch 90 sub 30 div dup mul add 1 exch sub}");

    // create shading based on three functions
    FunctionShading functionShading = new FunctionShading(Guid.NewGuid().ToString(),
        PredefinedColorSpaces.RGB,
        new Boundary(180, 180), RgbColors.Green.Components,
        new string[] {psFunctionR.ID, psFunctionG.ID, psFunctionB.ID});

    // register functions and shading object
    doc.ResourceManager.RegisterResource(psFunctionR);
    doc.ResourceManager.RegisterResource(psFunctionG);
    doc.ResourceManager.RegisterResource(psFunctionB);
    doc.ResourceManager.RegisterResource(functionShading);

    return functionShading;
}
 

The domain for each function is the same as shading’s boundary, so the shading’s color is defined at each point inside this boundary. See the result.

Radial shading based on PostScript function

Radial shadings or radial gradients define a color blend that varies between the two circles. Shadings of this type are commonly used to depict three-dimensional spheres and cones. For the code sample below, we used the PostScript function that produces the output color using the simple formula: (1-x) for the red component, and zero for others.

// creates and registers radial shading object
private static Shading CreateAndRegisterRadialShading(FixedDocument doc)
{
    // create simple PS function that returns the following RGB color value: [(1-x), 0, 0]
    PostScriptFunction psFunction = new PostScriptFunction(Guid.NewGuid().ToString(),
        new double[]{0,1},new double[]{0,1,0,1,0,1}, "{1 exch sub 0 0}");

    RadialShading radialShading = new RadialShading( Guid.NewGuid().ToString(),
        PredefinedColorSpaces.RGB,new Boundary(180,180), RgbColors.White.Components,
        new double[]{120,110,0,90,90,90},new string[]{psFunction.ID});
            
    // register function and shading
    doc.ResourceManager.RegisterResource(psFunction);
    doc.ResourceManager.RegisterResource(radialShading);

    return radialShading;
}
 

This shading is defined using the rectangular boundary [0, 0,180,180] and two circles defined by their centers and radii. See the result.

Shading Pattern

The shading pattern is like any other pattern except it’s based on the shading, see 8.7 Patterns section of the PDF specification for the details. Thinking abstractly, the pattern is a like a regular color with the difference that it depends on the point's coordinates. In order to use it, you have to set the current stroking or non-stroking colorspace to a special value returned by the PredefinedColorspaces::Pattern property. We can use any shading object as a base for the shading pattern.

The code below demonstrates this approach step by step:

// create shading object
Shading functionShading = CreateAndRegisterFunctionBasedShading(doc);

// register shading pattern based on existing function-based shading
ShadingPattern shadingPattern = new ShadingPattern(Guid.NewGuid().ToString(),functionShading.ID);
doc.ResourceManager.RegisterResource(shadingPattern);
 

We used the shading created by one of the functions described earlier, created ShadingPattern object based on it and registered it as a document’s resource. After that we can start using this pattern. Let’s create a FormXObject as the container for our graphical content and use the shading pattern created above as the text fill color.

// creates a reusable piece of content (FormXObject)
private static FixedContent CreateAndRegisterXObject(FixedDocument doc, ShadingPattern fillColor)
{
    ClippedContent content = new ClippedContent(Path.CreateRoundRect(0, 0, 180, 180, 10, 10, 10, 10));

    content.SaveGraphicsState();

    // draw gray rect as a background for our text
    content.SetDeviceNonStrokingColor(RgbColors.LightGray.Components);
    content.FillPath(Path.CreateRect(0, 0, 180, 180));
            
    // draw text using shading pattern as a fill 
    content.Translate(3, 85);
    TextObject txt = new TextObject(StandardFonts.Helvetica, 25);
    // set the fill colorspace to Pattern in order to use the pattern as a color
    txt.SetNonStrokingColorSpace(PredefinedColorSpaces.Pattern);
    // set our pattern as a fill color
    txt.SetNonStrokingColor(fillColor.ID);
    txt.AppendText("Shading pattern");
    content.AppendText(txt);
    content.RestoreGraphicsState();

    FixedContent formXObject = new FixedContent(Guid.NewGuid().ToString(), new Boundary(180, 180), content);
    doc.ResourceManager.RegisterResource(formXObject);

    return formXObject;
}
 

And draw it:

// draw the sample FormXObject demonstrating text fill using shading pattern
firstPage.Content.AppendXObject(CreateAndRegisterXObject(doc,shadingPattern).ID, 390, 460);
 

Resulting text filled with this shading pattern is shown in the resulting document produced by this code sample.

Code sample demonstrating all shading types

The code below draws all shadings described in sections above placed on one PDF page including shading pattern.

static void Main(string[] args)
{                                              
    // create document object
    FixedDocument doc = new FixedDocument();

    // create shading objects
    Shading axialShadingExp = CreateAndRegisterAxialShadingBasedOnExponentialFunction(doc,RgbColors.Red, RgbColors.Black);

    Shading axialShadingLinear = CreateAndRegisterAxialShadingBasedOnLinearInterpolationFunction(doc, new object[] { RgbColors.Red, RgbColors.Black });

    Shading axialShadingLinearMultipleColors = CreateAndRegisterAxialShadingBasedOnLinearInterpolationFunction( doc,
       new object[] { RgbColors.Red, RgbColors.Green, RgbColors.Blue, RgbColors.Black });
            
    Shading radialShading = CreateAndRegisterRadialShading(doc);

    Shading functionShading = CreateAndRegisterFunctionBasedShading(doc);
            
    // register shading pattern based on existing function-based shading
    ShadingPattern shadingPattern = new ShadingPattern(Guid.NewGuid().ToString(),functionShading.ID);
    doc.ResourceManager.RegisterResource(shadingPattern);

    // create document page
    Page firstPage = new Page();

    // draw shadings
    DrawShading(firstPage, axialShadingExp.ID, 10, 650);
    DrawShading(firstPage, axialShadingLinear.ID, 200, 650);
    DrawShading(firstPage, axialShadingLinearMultipleColors.ID, 390, 650);
    DrawShading(firstPage, functionShading.ID, 10, 460);
    DrawShading(firstPage, radialShading.ID, 200, 460);

    // draw the sample xObject demonstrating text fill using shading pattern
    firstPage.Content.AppendXObject(CreateAndRegisterXObject(doc,shadingPattern).ID, 390, 460);
            
    // add page to document
    doc.Pages.Add(firstPage);

    // save document
    using (Stream stream = File.Create("out.pdf"))
    {
        doc.Save(stream);
    }
    Process.Start("out.pdf");
}

// draws shading object on page at the specified coordinates 
private static void DrawShading(Page page, string resourceName, double xOffset, double yOffset)
{
    page.Content.SaveGraphicsState();
    page.Content.Translate(xOffset, yOffset);
    page.Content.PaintShading(resourceName, Path.CreateRoundRect(0, 0, 180, 180, 10, 10, 10, 10));
    page.Content.RestoreGraphicsState();
}      
  


The image below demonstrates the generated PDF document, there we have (from left to right, top to bottom): Axial Shading based on Exponential interpolation function, Axial Shading based on Sampled Function,Function-based shading based on PostScript function, Radial shading based on PostScript function, another Radial shading based on PostScript function, and Shading Pattern used as a text fill color.

Shadings and shading patterns

The complete code sample can be found in our Apitron PDF Kit samples repository.

Patterns in Flow layout API

Using patterns to fill elements background

Imagine that you’d like to fill the background of a PDF document page or any of its elements with some repeating pattern e.g. snowflakes, to create a postcard or winter sale newsletter. You could use BackgroundImage property which every ContentElement provides, but it’s not always helpful and is limited being a raster drawing (implies scaling limitations) and also takes storage space.

The Color class defined in Flow layout API supports specifiying colors in Pattern colorspace and, therefore it can be used to fill elements background by all pattern types supported in PDF (it can even be used as text color or border color because it’s essentialy a color).

See the sample below that fills document background with pattern based on image:

using (FileStream fs = new FileStream(@"output.pdf", FileMode.Create))
{
    // create documents resource manager and register pattern image
    ResourceManager resourceManager = new ResourceManager();
    resourceManager.RegisterResource(new Apitron.PDF.Kit.FixedLayout.Resources.XObjects.Image("snowflakes", "snowflakes.png",true));
    
    // create colored tiling pattern, draw image inside and register it.
    TilingPattern pattern = new TilingPattern("myPattern",new Boundary(0,0,100,100),100,100,true);
    pattern.Content.AppendImage("snowflakes",0,0,100,100);
    resourceManager.RegisterResource(pattern);
    
    // create document
    FlowDocument document = new FlowDocument(){Padding = new Thickness(10)};
    
    // set color, defined as PATTERN color as a background color for document
    document.Background = new Color(PredefinedColorSpaces.Pattern,"myPattern");
    
    // add sample text block
    document.Add(new TextBlock("Sample for background patterns"){Color = RgbColors.Red});
    
    // generate document
    document.Write(fs, resourceManager, new PageBoundary(Boundaries.A4));
}
 

Resulting PDF file looks as follows:

Use pattern background color for content elements