windowsforms-115318-controls-and-libraries-pdf-viewer-examples-interactivity-how-to-custom-draw-in-the-pdf-viewer.md
The following example uses coordinates obtained from a PDF Viewer to draw graphics content. Use the System.Drawing.Graphics class to draw graphics in the Paint event handler. The PDF Graphics API allows you to save graphics in a document.
Important
The Universal Subscription or an Office File API Subscription is required to use the PDF Document API in production code. Refer to the DevExpress Subscription page for pricing information.
This example does the following:
The PDF Viewer has a custom Activate Drawing button that enables drawing mode.
Handle the MouseUp, MouseMove, and MouseDown events to obtain the selected area coordinates. The PdfViewer.GetDocumentPosition method allows you to convert the retrieved coordinates to page coordinates. The GraphicsCoordinates class is used to save and restore these coordinates.
// This class is used to save
// and restore the selection area coordinates
class GraphicsCoordinates
{
public GraphicsCoordinates(int pageIndex, PdfPoint point1, PdfPoint point2) {
PageIndex = pageIndex;
Point1 = point1;
Point2 = point2;
}
public int PageIndex { get; }
public PdfPoint Point1 { get; }
public PdfPoint Point2 { get; }
public bool IsEmpty => Point1 == Point2;
}
List<GraphicsCoordinates> rectangleCoordinateList = new List<GraphicsCoordinates>();
GraphicsCoordinates currentCoordinates;
// This variable indicates whether the Drawing button
// is activated
bool ActivateDrawing = false;
// ...
void PdfViewer_MouseMove(object sender, MouseEventArgs e) {
if (currentCoordinates != null) {
UpdateCurrentRect(e.Location);
pdfViewer.Invalidate();
}
}
void pdfViewer1_MouseUp(object sender, MouseEventArgs e) {
// Convert the retrieved coordinates
// to the page coordinates
UpdateCurrentRect(e.Location);
if (currentCoordinates != null) {
if (!currentCoordinates.IsEmpty && ActivateDrawing)
// Add coordinates to the list
rectangleCoordinateList.Add(currentCoordinates);
currentCoordinates = null;
}
}
void pdfViewer1_MouseDown(object sender, MouseEventArgs e) {
var position = pdfViewer.GetDocumentPosition(e.Location, true);
currentCoordinates = new GraphicsCoordinates(position.PageNumber - 1, position.Point, position.Point);
}
void UpdateCurrentRect(Point location)
{
if (rectangleCoordinateList != null)
{
var documentPosition = pdfViewer.GetDocumentPosition(location, true);
if (currentCoordinates.PageIndex == documentPosition.PageNumber - 1)
currentCoordinates = new GraphicsCoordinates(currentCoordinates.PageIndex, currentCoordinates.Point1, documentPosition.Point);
}
}
' This class is used to save
' and restore the selection area coordinates
Private Class GraphicsCoordinates
Public Sub New(ByVal pageIndex As Integer, ByVal point1 As PdfPoint, ByVal point2 As PdfPoint)
Me.PageIndex = pageIndex
Me.Point1 = point1
Me.Point2 = point2
End Sub
Public ReadOnly Property PageIndex As Integer
Public ReadOnly Property Point1 As PdfPoint
Public ReadOnly Property Point2 As PdfPoint
Public ReadOnly Property IsEmpty As Boolean
Get
Return Point1 = Point2
End Get
End Property
End Class
Private rectangleCoordinateList As List(Of GraphicsCoordinates) = New List(Of GraphicsCoordinates)()
Private currentCoordinates As GraphicsCoordinates
' This variable indicates whether the Drawing button
' is activated
Private ActivateDrawing As Boolean = False
' ...
Private Sub PdfViewer_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
If currentCoordinates IsNot Nothing Then
UpdateCurrentRect(e.Location)
pdfViewer.Invalidate()
End If
End Sub
Private Sub pdfViewer1_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
' Convert the retrieved coordinates
' to the page coordinates
UpdateCurrentRect(e.Location)
If currentCoordinates IsNot Nothing Then
' Add coordinates to the list
If Not currentCoordinates.IsEmpty AndAlso ActivateDrawing Then rectangleCoordinateList.Add(currentCoordinates)
currentCoordinates = Nothing
End If
End Sub
Private Sub pdfViewer1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
Dim position = pdfViewer.GetDocumentPosition(e.Location, True)
currentCoordinates = New GraphicsCoordinates(position.PageNumber - 1, position.Point, position.Point)
End Sub
Private Sub UpdateCurrentRect(ByVal location As Point)
If rectangleCoordinateList IsNot Nothing Then
Dim documentPosition = pdfViewer.GetDocumentPosition(location, True)
If currentCoordinates.PageIndex = documentPosition.PageNumber - 1 Then currentCoordinates = New GraphicsCoordinates(currentCoordinates.PageIndex, currentCoordinates.Point1, documentPosition.Point)
End If
End Sub
In the PdfViewerControl.Paint event handler, check if the Drawing button is activated and use the obtained coordinates to draw a rectangle. The PdfViewer.GetClientPoint(PdfDocumentPosition) method allows you to convert the PdfDocumentPosition instance to the PointF object.
The code below paints graphics in the UI control, separate from the document content. Any painted graphics does not persist within the document.
private void activateDrawingButton_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
// Change the activation indicator
ActivateDrawing = !ActivateDrawing;
pdfViewer.Invalidate();
}
void PdfViewer_Paint(object sender, PaintEventArgs e) {
if (ActivateDrawing)
{
foreach (var r in rectangleCoordinateList)
DrawImageRectangle(e.Graphics, r);
if (currentCoordinates != null)
DrawImageRectangle(e.Graphics, currentCoordinates);
}
}
void DrawImageRectangle(Graphics graphics, GraphicsCoordinates rect)
{
PointF start = pdfViewer.GetClientPoint(new PdfDocumentPosition(rect.PageIndex + 1, rect.Point1));
PointF end = pdfViewer.GetClientPoint(new PdfDocumentPosition(rect.PageIndex + 1, rect.Point2));
// Create a rectangle where graphics should be drawn
var r = Rectangle.FromLTRB((int)Math.Min(start.X, end.X), (int)Math.Min(start.Y, end.Y), (int)Math.Max(start.X, end.X), (int)Math.Max(start.Y, end.Y));
// Draw a rectangle in the created area
graphics.DrawRectangle(new Pen(Color.Red), r);
}
Private Sub activateDrawingButton_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs)
' Change the activation indicator
ActivateDrawing = Not ActivateDrawing
pdfViewer.Invalidate()
End Sub
Private Sub PdfViewer_Paint(ByVal sender As Object, ByVal e As PaintEventArgs)
If ActivateDrawing Then
For Each r In rectangleCoordinateList
DrawImageRectangle(e.Graphics, r)
Next
If currentCoordinates IsNot Nothing Then DrawImageRectangle(e.Graphics, currentCoordinates)
End If
End Sub
Private Sub DrawImageRectangle(ByVal graphics As Graphics, ByVal rect As GraphicsCoordinates)
Dim start As PointF = pdfViewer.GetClientPoint(New PdfDocumentPosition(rect.PageIndex + 1, rect.Point1))
Dim [end] As PointF = pdfViewer.GetClientPoint(New PdfDocumentPosition(rect.PageIndex + 1, rect.Point2))
' Create a rectangle where graphics should be drawn
Dim r = Rectangle.FromLTRB(CInt(Math.Min(start.X, [end].X)), CInt(Math.Min(start.Y, [end].Y)), CInt(Math.Max(start.X, [end].X)), CInt(Math.Max(start.Y, [end].Y)))
' Draw a rectangle in the created area
graphics.DrawRectangle(New Pen(Color.Red), r)
End Sub
The graphics content created by the System.Drawing.Graphics class is not retained after the document is saved. Use the PdfDocumentProcessor and PdfGraphics instances to draw graphics content in the document and save the result.
Note
The content is drawn in the resulting document after the entire content. The drawn parts have another order in the text of the document, so the selection is executed differently for these parts compared to the surrounding text.
The following code snippet draws graphics content with the SaveDrawingAndReload method, saves the result, and reopens the PDF file. The method is executed on a button click.
private void saveGraphicsButton_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
SaveDrawingAndReload();
}
private void SaveDrawingAndReload()
{
string fileName = pdfViewer.DocumentFilePath;
pdfViewer.CloseDocument();
using (PdfDocumentProcessor processor = new PdfDocumentProcessor())
{
// Load a document to the PdfDocumentProcessor instance
processor.LoadDocument(fileName);
foreach (var rect in rectangleCoordinateList)
{
// Create a PdfGraphics object
using (PdfGraphics graph = processor.CreateGraphics())
{
PdfPage page = processor.Document.Pages[rect.PageIndex];
PdfRectangle pageCropBox = page.CropBox;
PdfPoint p1 = new PdfPoint(rect.Point1.X, pageCropBox.Height - rect.Point1.Y);
PdfPoint p2 = new PdfPoint(rect.Point2.X, pageCropBox.Height - rect.Point2.Y);
// Create a rectangle where graphics should be drawn
RectangleF bounds = RectangleF.FromLTRB(
(float)Math.Min(p1.X, p2.X), (float)Math.Min(p1.Y, p2.Y),
(float)Math.Max(p1.X, p2.X), (float)Math.Max(p1.Y, p2.Y));
// Draw a rectangle in the created area
graph.DrawRectangle(new DXPen(Color.Red), bounds);
// Draw graphics content into a file
graph.AddToPageForeground(page, 72, 72);
}
}
// Save the document
processor.SaveDocument(fileName);
}
rectangleCoordinateList.Clear();
// Open the document in the PDF Viewer
pdfViewer.LoadDocument(fileName);
}
Private Sub saveGraphicsButton_ItemClick(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs)
SaveDrawingAndReload()
End Sub
Private Sub SaveDrawingAndReload()
Dim fileName As String = pdfViewer.DocumentFilePath
pdfViewer.CloseDocument()
Using processor As PdfDocumentProcessor = New PdfDocumentProcessor()
' Load a document to the PdfDocumentProcessor instance
processor.LoadDocument(fileName)
For Each rect In rectangleCoordinateList
' Create a PdfGraphics object
Using graph As PdfGraphics = processor.CreateGraphics()
Dim page As PdfPage = processor.Document.Pages(rect.PageIndex)
Dim pageCropBox As PdfRectangle = page.CropBox
Dim p1 As PdfPoint = New PdfPoint(rect.Point1.X, pageCropBox.Height - rect.Point1.Y)
Dim p2 As PdfPoint = New PdfPoint(rect.Point2.X, pageCropBox.Height - rect.Point2.Y)
' Create a rectangle where graphics should be drawn
Dim bounds As RectangleF = RectangleF.FromLTRB(CSng(Math.Min(p1.X, p2.X)), CSng(Math.Min(p1.Y, p2.Y)), CSng(Math.Max(p1.X, p2.X)), CSng(Math.Max(p1.Y, p2.Y)))
' Draw a rectangle in the created area
graph.DrawRectangle(New DXPen(Color.Red), bounds)
' Draw graphics content into a file
graph.AddToPageForeground(page, 72, 72)
End Using
Next
' Save the document
processor.SaveDocument(fileName)
End Using
rectangleCoordinateList.Clear()
' Open the document in the PDF Viewer
pdfViewer.LoadDocument(fileName)
End Sub
The code above is similar to the code from the previous step. The main distinction is that now the graphics are added directly to the document. Although the code is mostly duplicated, using both System.Drawing.Graphics and PdfGraphics has the following advantages:
Tip
You can override the PdfViewerControl.SaveAsCommand to draw and save graphics content when the document is saved. Refer to the following example for a code snippet:
View Example: How to Draw Graphics by Coordinates Obtained from a PDF Viewer