pdf links

PDF Rendering
Convert PDF to Image (.NET)
Convert PDF to image on Android (Xamarin)
Convert PDF to image on iOS (Xamarin)
Convert PDF to image in Windows Store apps (.NET)
Convert PDF to image in Windows Phone apps (.NET)
PDF to image in Universal Windows Store apps (.NET)
Free PDF Viewer control for Windows Forms (.NET)
How to integrate PDF Viewer control in WPF app (.NET)
Creating WPF PDF Viewer supporting bookmarks (.NET)
Cross-platform PDF Viewer using GTK# (MONO)
Silverlight PDF viewer control (Silverlight 5)
Multithreaded PDF rendering (.NET)
Convert pdf to image in Silverlight app (C# sample)
How to set fallback fonts for PDF rendering (C#)
Avoiding the out-of-memory exception on rendering (C#)
PDF viewer single page application (WebAPI, AngularJS)
PDF viewer control for Windows 10 universal applications
Use custom ICC profile for CMYK to RGB conversion
PDF layers - separate images, text, annotations, graphics

PDF Forms Creation PDF Security
Conversion to PDF/A
Other topics
PDF Document Manipulation PDF Content Generation
Fixed and Flow layout document API (.NET)
Creation of grids and tables in PDF (C# sample)
How to create interactive documents using Actions (C# sample)
Text flow effects in PDF (C# sample)
How to generate ordered and bulleted lists in PDF (C# sample)
Convert HTML to PDF using flow layout API (C# sample)
How to use custom fonts for PDF generation (.NET)
Create document with differently sized pages (C#)
Create PDF documents using MONO (C#/MONO/Windows/OSX)
How to use background images for content elements (C#/PDF Kit/FlowLayout)
Add transparent images to PDF document (C#)
Draw round rect borders in PDF documents(C#)
ICC color profiles and and ICC based colors in PDF (C#)
How to use bidirectional and right to left text in PDF (C#)
Create PDF documents from XML templates (C# sample)
How to resize PDF pages and use custom stamps (C#)
Add header and footer to PDF page (.NET sample)
How to use clipping mask for drawing on PDF page
Fill graphics path with gradient brushes in PDF (Shadings)
Apitron PDF Kit and Rasterizer engine settings
Add layers to PDF page (optional content, C# sample)
How to create free text annotation with custom appearance
PDF Content Extraction PDF Navigation
PDF to TIFF conversion
Contact us if you have a PDF related question and we'll cover it in our blog.

2016-01-21

How to implement gradient fills using shadings and shading patterns in PDF

Introduction
 

PDF is not only about text data storage, but it also specifies a rich set of drawing operations making it possible to achieve very complex visual effects. In this article we’ll talk about its gradient filling and stroking capabilities. Using the API provided by Apitron PDF Kit for .NET component you’ll be 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 a called a Shading, and you may read the low level details involved in article 8.7.4 Shading Patterns in PDF specification.

The 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. You can use different functions to calculate the value for color components independently or at once. 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 is a relatively simple shape whose geometry is the same as that of the gradient fill itself 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 current user space applying the additional clipping path given as a parameter if needed.

Another way to use the shading is to define a ShadingPattern. Similar to the TilingPattern object it can be used as a color for fill and stroke operations if you set the current stroking or non-stroking colorspace as PredefinedColorpaces::Pattern. This way you’ll be able to draw whatever you want using the shading as color provider for stroked or filled point.

Both methods have own pros and cons and it’s up to you to choose the one that works best it your situation. Read further to see various functions types and shadings types in action.  

Axial Shading based on Exponential interpolation function


Here we define the axial shading object that does what it name says - defines a color blend along a line between two points, optionally extended beyond the boundary points by continuing the boundary colors. Exponential interpolation function is being used to calculate 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 rectangular boundary [0, 0,180,180] and two points which define the interpolation axis (0,90) and (180,90).


Axial Shading based on Sampled Function


The shading object created here is based on sampled function that uses SamplerDelegate to produce the color value for each calculated sample value. Samples count is the number of color passed as the parameter.

// creates and registers axial shading object based on sampled function able to interpolate
// between multiple colors 
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 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).


Function-based shading with PostScript function



In function-based shadings, the color at every point in the domain is defined by a specified mathematical function. The function need not be smooth or continuous. This type is the most general of the available shading types and is useful for shadings that cannot be adequately described with any of the other types. 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 within each point of it.


Radial shading based on PostScript function


Radial shadings define a color blend that varies between two circles. Shadings of this type are commonly used to depict three-dimensional spheres and cones. Here we used a simple PostScript function that produces the output color using the simple formula: (1-x) for the red component, and zeros 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 rectangular boundary [0, 0,180,180] and two circles defined by their centers and radii.


Shading Pattern


The shading pattern is like any other pattern except it’s based on shading, see 8.7 Patterns section of the PDF specification for the details. Thinking abstractly, the pattern is a like a color, depending on the location you fill or stoke in. 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 as a base for the shading pattern. See the code below:

// 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 and use the pattern as a text fill.

// 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);


Sample program (shadings)


The code below draws all shadings described above on one 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 created PDF document:

Pic. 1 Shadings and shading patterns

Pic. 1 Shadings and shading patterns


The complete code sample can be found in our github repo.

Conclusion


Using Apitron PDF Kit for .NET you can create advanced PDF export tools, for example modules for CAD software, graphical editors, or document export routines. Its API is flexible enough, and allows you to implement complex visual effects using graphical operations described in the PDF specification.

Downloadable version of this article can be found by the following link [PDF].

No comments:

Post a Comment