Introduction
PDF files are becoming more and more popular; we may need a library to manipulate these PDF files in our application without using Acrobat SDK. Although, there are such libraries available I thought of creating my own. The library supports Unicode inside PDF objects and selection of encoding, so that I can use Chinese characters freely in a PDF document.
The library is written in C# and requires .NET 2.0.
This project is intended to achieve all the capabilities specified in PDF 1.6 Reference, and provides tools to create, read, and manipulate PDF files. This library is only a startup.
The current capabilities:
- Basic data types (Number, String, DateTime, Array, Dictionary, Stream...)
- File structure and document structure
- Path construction and painting
- Graphics state
- Transformation matrices
- Form XObjects
- Text state
- Text objects
- Type 1 fonts
- Composite font from type 0 CIDFont
- Document outline and PageLables
- ViewerPreferences
Using the code
- Coordinate system: The coordinates are specified in the user space:
- The origin is located at the bottom-left corner of the page;
- X axis extends from left to right and Y axis from bottom to top;
- The length unit can be set using
Page.UserUnit
, and the default value is 1/72 inch.
- Fonts and XObjects can't be used directly. They need to be added to the document's resource dictionary and each of them should be associated with a key, and then used in the page content.
The following example shows a basic usage of this library:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using AnotherPDFLib;
using AnotherPDFLib.PdfObjects;
using AnotherPDFLib.PdfText;
using AnotherPDFLib.PdfGraphics;
namespace AnotherPDFLibTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
PdfDocument pdfdoc;
PdfPage page;
string PageNumberFormat;
private void buttonCreateDocument_Click(object sender, EventArgs e)
{
pdfdoc = new PdfDocument(PaperSize.A4);
pdfdoc.DocumentInfo.Creator = Application.ExecutablePath;
pdfdoc.DocumentInfo.Author = "Liu Junfeng";
pdfdoc.Catalog.PageMode = PageMode.UseOutlines;
PageLabelRange firstrange =
new PageLabelRange(0, NumberingStyle.RomanLowercase);
PageLabelRange secondrange =
new PageLabelRange(3, NumberingStyle.ArabicDecimal);
pdfdoc.PageLabels.Add(firstrange);
pdfdoc.PageLabels.Add(secondrange);
comboBoxFont.Items.Clear();
AddFont(StandardFont.Courier);
AddFont(StandardFont.Helvetica);
AddFont(ChineseFont.AdobeSong);
buttonNewPage_Click(null, null);
numericUpDownX.Maximum = (decimal)pdfdoc.PageSize.Width;
numericUpDownY.Maximum = (decimal)pdfdoc.PageSize.Height;
numericUpDownX.Value = 100;
numericUpDownY.Value = numericUpDownY.Maximum - 100;
}
void AddFont(PdfFont font)
{
string fontID = pdfdoc.AddFont(font);
string fontItem =
fontID + "(" + font.BaseFont + ")";
comboBoxFont.Items.Add(fontItem);
}
private void Form1_Load(object sender, EventArgs e)
{
PageNumberFormat = this.Text + " (Page {0})";
comboBoxEncoding.Items.AddRange(
new object[]
{ "ASCII", "GB2312", "GB18030", "UTF-16BE" });
comboBoxEncoding.SelectedIndex = 0;
PdfWriter.ListDictionary = false;
PdfWriter.ListContentStream = true;
}
private void comboBoxEncoding_SelectedIndexChanged(
object sender, EventArgs e)
{
switch (comboBoxEncoding.SelectedItem.ToString())
{
case "ASCII":
break;
case "GB2312":
ChineseFont.AdobeSong.Encoding =
CMap.ChineseSimplified.GB_EUC_H;
break;
case "GB18030":
ChineseFont.AdobeSong.Encoding =
CMap.ChineseSimplified.GBK_EUC_H;
break;
default:
ChineseFont.AdobeSong.Encoding =
CMap.ChineseSimplified.UniGB_UTF16_H;
break;
}
}
private void buttonSave_Click(object sender, EventArgs e)
{
try
{
string file = textBoxFile.Text;
pdfdoc.Save(file);
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
}
private void buttonNewPage_Click(object sender, EventArgs e)
{
if (pdfdoc == null)
{
MessageBox.Show("Document not created!");
return;
}
page = pdfdoc.NewPage();
PdfOutlineItem outlineitem = new PdfOutlineItem();
outlineitem.Title = "Page " + pdfdoc.PageCount;
outlineitem.Dest = Destination.FitPage(page);
pdfdoc.Outlines.Append(outlineitem);
TextState ts = new TextState();
ShowPageNumber();
}
void ShowPageNumber()
{
this.Text = string.Format(PageNumberFormat, pdfdoc.PageCount);
}
private void buttonAddText_Click(object sender, EventArgs e)
{
if (pdfdoc == null)
{
MessageBox.Show("Document not created!");
return;
}
if (comboBoxFont.SelectedIndex == -1)
{
MessageBox.Show("No font selected!");
return;
}
double tx = (double)numericUpDownX.Value;
double ty = (double)numericUpDownY.Value;
string font =
comboBoxFont.SelectedItem.ToString().Split('(')[0];
int size = (int)numericUpDownSize.Value;
Encoding encoding =
Encoding.GetEncoding(comboBoxEncoding.Text);
PdfText pdfText = new PdfText(encoding);
pdfText.Begin();
pdfText.TextState.Font = new TextFont(font, size);
pdfText.TextState.RenderingMode = GetRenderMode();
pdfText.TextState.LineHeight = (int)size * 1.5;
pdfText.StartNewLine(tx, ty);
foreach (string line in richTextBox.Lines)
{
pdfText.ShowText(line);
pdfText.StartNewLine();
}
pdfText.End();
page.Content.Add(pdfText);
}
RenderMode GetRenderMode()
{
RenderMode mode = RenderMode.None;
if (checkBoxFill.Checked)
{
mode &= RenderMode.Fill;
}
if (checkBoxStroke.Checked)
{
mode &= RenderMode.Stroke;
}
if (checkBoxClipPath.Checked)
{
mode &= RenderMode.ClipPath;
}
return mode;
}
private void buttonnDrawGraph_Click(object sender, EventArgs e)
{
Path path = new Path();
SubPath subpath = new SubPath(120, 800);
subpath.LineTo(225, 700);
subpath.LineTo(330, 800);
path.AddSubPath(subpath);
path.AddRectangle(100, 500, 250, 200);
path.AddRectangle(120, 520, 210, 160);
path.Stroke();
page.Content.Add(path);
Template.FilledSquare.Matrix = TMatrix.Scale(0.05, 0.01);
Template.FilledTriangle.Matrix = TMatrix.Scale(0.02, 0.02);
string square = pdfdoc.AddXObject(Template.FilledSquare);
string triangle = pdfdoc.AddXObject(Template.FilledTriangle);
page.Content.GraphicsState.Push();
page.Content.GraphicsState.CTM = TMatrix.Translation(200, 550);
page.Content.PaintXObject(square);
page.Content.GraphicsState.CTM = TMatrix.Translation(-30, 50);
page.Content.PaintXObject(triangle);
page.Content.GraphicsState.CTM = TMatrix.Translation(90, 0);
page.Content.PaintXObject(triangle);
page.Content.GraphicsState.Pop();
}
private void buttonRemovePage_Click(object sender, EventArgs e)
{
pdfdoc.PageTree.Kids.Items.Remove(page);
pdfdoc.PdfObjects.Remove(page.Identifier);
pdfdoc.PdfObjects.Remove(page.Content.Identifier);
ShowPageNumber();
}
private void buttonRotatePage_Click(object sender, EventArgs e)
{
page.Rotate += 90;
}
}
}
Template.FilledSquare
and Template.FilledTriangle
are defined as FormXOjbects
:
public class Template
{
static FormXObject square;
public static FormXObject FilledSquare
{
get
{
if (square == null)
{
square = new FormXObject();
square.BoundingBox = new PdfRectangle(0, 0, 1000, 1000);
Path path = new Path();
path.AddRectangle(0,0,1000,1000);
path.Fill();
square.Content.Add(path);
}
return square;
}
}
static FormXObject triangle;
public static FormXObject FilledTriangle
{
get
{
if (triangle == null)
{
triangle = new FormXObject();
triangle.BoundingBox =
new PdfRectangle(0, 0, 1000, 1000);
SubPath subpath = new SubPath(0, 0);
subpath.LineTo(500, 1000);
subpath.LineTo(1000, 0);
subpath.Close();
Path path = new Path();
path.Add(subpath);
path.Fill();
triangle.Content.Add(path);
}
return triangle;
}
}
The sample output
ToDo's
- Color space and color values
- Insert images
- TrueType font
- Annotations
- Stream filters
- ...