另一个 PDFLib - 第 1 部分:显示文本和绘制图形
一个用于手动创建PDF文件的.NET库。
引言
PDF文件越来越受欢迎;在我们的应用程序中,我们可能需要一个库来操作这些PDF文件,而无需使用Acrobat SDK。虽然有这样的库可用,但我还是想自己创建一个。该库支持PDF对象内的Unicode和编码选择,以便我可以在PDF文档中自由使用中文字符。
该库是用C#编写的,需要.NET 2.0。
该项目的目标是实现PDF 1.6参考规范中指定的所有功能,并提供创建、读取和操作PDF文件的工具。这个库只是一个起点。
当前功能
- 基本数据类型(数字、字符串、日期时间、数组、字典、流...)
- 文件结构和文档结构
- 路径构建和绘制
- 图形状态
- 变换矩阵
- Form XObjects
- 文本状态
- 文本对象
- Type 1字体
- 来自Type 0 CIDFont的复合字体
- 文档大纲和PageLables
- ViewerPreferences
使用代码
- 坐标系:坐标在用户空间中指定
- 原点位于页面的左下角;
- X轴从左到右延伸,Y轴从下到上延伸;
- 可以使用
Page.UserUnit
设置长度单位,默认值为1/72英寸。
- 字体和XObjects不能直接使用。它们需要添加到文档的资源字典中,并且每个字体/XObject都应与一个键关联,然后在页面内容中使用。
以下示例显示了此库的基本用法
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;
//set PdfWriter formattings
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();
//add outline item
PdfOutlineItem outlineitem = new PdfOutlineItem();
outlineitem.Title = "Page " + pdfdoc.PageCount;
outlineitem.Dest = Destination.FitPage(page);
pdfdoc.Outlines.Append(outlineitem);
TextState ts = new TextState();
//set page level TextState
//...
//page.Content.AddGraphicObject(ts);
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);
//pdfText.TextMatrix = TMatrix.Translation(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(0, 0);
//subpath.AddLine(200, 300);
//path.AddSubPath(subpath);
//path.AddRectangle(200, 300, 100, 100);
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
和Template.FilledTriangle
被定义为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;
}
}
示例输出
待办事项
- 颜色空间和颜色值
- 插入图像
- TrueType字体
- 注解
- 流过滤器
- ...