Generating Notepaper with PdfSharp
PdfSharp is a lovely library that lets you generate/draw pdf documents programmatically. I used it to generate some notepaper and found it easy to work with once the basics of the objects are understood. We need to create a document, give it some properties, then we can save it.
using PdfSharp.Drawing;
using PdfSharp.Pdf;
...
PdfDocument document = new PdfDocument
{
PageLayout = PdfPageLayout.SinglePage,
PageMode = PdfPageMode.UseNone
};
document.Save(filename);
The next obvious thing is to add some pages. Each page can have it's own size and orientation.
PdfPage newPage = document.AddPage();
newPage.Size = PdfSharp.PageSize.A4;
newPage.Orientation = PdfSharp.PageOrientation.Landscape;
Now that we have a page or two, we want to put some content into them. PdfSharp has a number of objects that are analogs of the GDI drawing objects in Windows, things like Graphics, Pens, Brushes etc. and a set of similar methods to draw lines, rectangles, ellipses.
In order to be able to draw some notepaper, I wanted to be able to have a white border around each page and maybe print on all the page or print an A5 signature as two halves of a landscape A4 page. This meant I needed something to represent one or more printable areas for each page.
public class PrintableArea
{
public int Left { get; set; }
public int Right { get; set; }
public int Top { get; set; }
public int Bottom { get; set; }
public static IEnumerable<PrintableArea> FromPage(PdfPage page, int border, bool split)
{
var areas = new List<PrintableArea>();
if (split)
{
PrintableArea pane1 = new PrintableArea
{
Left = border,
Right = ((int)page.Width / 2) - border,
Top = border,
Bottom = (int)page.Height - border
};
PrintableArea pane2 = new PrintableArea
{
Left = pane1.Right + (2 * border),
Right = (int)page.Width - border,
Top = border,
Bottom = (int)page.Height - border
};
areas.Add(pane1);
areas.Add(pane2);
}
else
{
PrintableArea single = new PrintableArea
{
Left = border,
Right = (int)page.Width - border,
Top = border,
Bottom = (int)page.Height - border
};
areas.Add(single);
}
return areas;
}
}
Each kind of paper - graph, dotted, music manuscript, lined - implies an interface. Let's call it IPageRenderer.
public interface IPageRenderer
{
int PageBorder { get; set; }
void Render(PdfPage page, IEnumerable<PrintableArea> areas);
}
So now we can treat a list of renderings as a group using a page generator.
public class PageGenerator
{
public bool SplitPages { get; set; }
public PdfSharp.PageSize PageSize { get; set; }
public PdfSharp.PageOrientation PageOrientation { get; set; }
public int PageBorder { get; set; }
public void GeneratePages(PdfDocument document, int pages, IEnumerable<IPageRenderer> renderers)
{
foreach(var renderer in renderers)
{
renderer.PageBorder = this.PageBorder;
for (int page = 0; page < pages; ++page)
{
PdfPage newPage = document.AddPage();
newPage.Size = this.PageSize;
newPage.Orientation = this.PageOrientation;
var areas = PrintableArea.FromPage(newPage, this.PageBorder, this.SplitPages);
renderer.Render(newPage, areas);
}
}
}
}
...
var renderers = new List<IPageRenderer>();
renderers.Add(new ManuscriptPaperRenderer(58, 6));
renderers.Add(new DottedPaperRenderer(15, 1));
renderers.Add(new LinedPaperRenderer(18));
renderers.Add(new GraphPaperRenderer(16));
PageGenerator pageGenerator = new PageGenerator
{
PageBorder = pageBorder,
PageOrientation = pageOrientation,
PageSize = PdfSharp.PageSize.A4,
SplitPages = splitPages
};
int pagesEach = 1;
pageGenerator.GeneratePages(document, pagesEach, renderers);
Finally, the implementation of each of the different types of renderer.
Music Manuscript
public class ManuscriptPaperRenderer : IPageRenderer
{
private readonly int staveSpacing;
private readonly int lineSpacing;
public ManuscriptPaperRenderer(int staveSpacing, int lineSpacing)
{
this.staveSpacing = staveSpacing;
this.lineSpacing = lineSpacing;
}
public int PageBorder { get; set; }
public void Render(PdfPage page, IEnumerable<PrintableArea> areas)
{
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
XPen pen = new XPen(XColors.Black, 0.0);
foreach (PrintableArea area in areas)
{
for (int horizontalLine = area.Top; horizontalLine < area.Bottom; horizontalLine += this.staveSpacing)
{
DrawStave(gfx, pen, horizontalLine, area.Left, area.Right, this.lineSpacing);
}
}
}
}
private static void DrawStave(XGraphics graphics, XPen pen, int topLine, int start, int end, int lineSpacing)
{
int linesPerStave = 5;
for (int horizontalLine = 0; horizontalLine < linesPerStave; horizontalLine++)
{
graphics.DrawLine(pen, start, topLine + (horizontalLine * lineSpacing), end, topLine + (horizontalLine * lineSpacing));
}
// draw end caps to lines
graphics.DrawLine(pen, start, topLine, start, topLine + ((linesPerStave - 1) * lineSpacing));
graphics.DrawLine(pen, end, topLine, end, topLine + ((linesPerStave - 1) * lineSpacing));
}
}
Dotted
public class DottedPaperRenderer : IPageRenderer
{
private readonly int gridSize;
private readonly int dotSize;
public DottedPaperRenderer(int gridSize, int dotSize)
{
this.gridSize = gridSize;
this.dotSize = dotSize;
}
public int PageBorder { get; set; }
public void Render(PdfPage page, IEnumerable<PrintableArea> areas)
{
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
XPen pen = new XPen(XColors.LightGray, 0.0);
foreach (PrintableArea area in areas)
{
for (int x = area.Left; x < area.Right; x += this.gridSize)
{
for (int y = area.Top; y < area.Bottom; y += this.gridSize)
{
gfx.DrawEllipse(pen, x, y, this.dotSize, this.dotSize);
}
}
}
}
}
}
Lined
public class LinedPaperRenderer : IPageRenderer
{
private readonly int lineSpacing;
public LinedPaperRenderer(int lineSpacing)
{
this.lineSpacing = lineSpacing;
}
public int PageBorder { get; set; }
public void Render(PdfPage page, IEnumerable<PrintableArea> areas)
{
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
XPen pen = new XPen(XColors.LightGray, 0.0);
foreach (PrintableArea area in areas)
{
for (int horizontalLine = area.Top; horizontalLine < area.Bottom; horizontalLine += this.lineSpacing)
{
gfx.DrawLine(pen, area.Left, horizontalLine, area.Right, horizontalLine);
}
}
}
}
}
Graph
public class GraphPaperRenderer : IPageRenderer
{
private readonly int gridSize;
public GraphPaperRenderer(int gridSize)
{
this.gridSize = gridSize;
}
public int PageBorder { get; set; }
public void Render(PdfPage page, IEnumerable<PrintableArea> areas)
{
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
XPen pen = new XPen(XColors.LightGray, 0.0);
foreach(PrintableArea area in areas)
{
for (int verticalLine = area.Left; verticalLine < area.Right; verticalLine += gridSize)
{
gfx.DrawLine(pen, verticalLine, area.Top, verticalLine, area.Bottom);
}
for (int horizontalLine = area.Top; horizontalLine < area.Bottom; horizontalLine += gridSize)
{
gfx.DrawLine(pen, area.Left, horizontalLine, area.Right, horizontalLine);
}
}
}
}
}
Here are some samples of each type of pdf for download: