Placement of widgets to move with PDFViewCtrl

Q: I am trying to place a shape on the PDFViewCtrl (or PDFViewWPF) and I want it to move with the control as I scroll, zoom, and re-size. How can I do this?

On some viewers, shapes can be placed directly on the DPFViewCtrl, using PDFViewCtrl.GetCanvas(). Doing this means that scrolling will be taken care of automatically. This is possible for example on our WinRT and WPF solutions. If a shape is placed on a canvas, you can translate it’s offset to screen coordinates by using PDFViewCtrl.GetHScrollPos() and PDFViewCtrl.GetVScrollPos(). That is:

CanvasX = ScreenX + PDFViewCtrl.GetHScrollPos()
CanvasY = ScreenY + PDFViewCtrl.GetVSCrollPos()

Once the shape is added, you can store a point representing the top left corner and, for example, a height and width normalized by zoom factor (e.g. width or height at zoom factor 1).
Assuming your shape is somewhere on page 5, then you can get the page point as follows:

double x = ScreenX;
double y = ScreenY;
PDFViewCtrl.ConvScreenPtToPagePt(ref x, ref y, 5);
PageX = x;
PageY = y;

When the control has been scaled or re-sized (or scrolled if you don’t have access to the canvas) you can do the following:

double x = PageX;
double y = PageY;

PDFViewCtrl.ConvPAgePtToScreeenPt(ref x, ref y, 5);

ScreenX = x;

ScreenY = y;
// get the new width and height using the new zoom factor.

They way to receive notifications for when the control has re-sized or zoomed will vary depending on platform. For example, in WinRT and WPF, you can subscribe to the PDFViewCtrl’s (PDFViewWPF’s) events directly, while on Android you have to create a custom tool.
A: On all our platforms, the most reliable way to do this is to store the widget’s position relative to the underlying document. For example, if you want a rectangle to black out or highlight a specific part of a page, you can store the rectangle’s top left corner in page space and then each time the control is scrolled, zoomed, or re-sized, you can calculate the new position in screen space and move your shape there.

WinRT Specific solution:

On WinRT, you will need the canvas acquired from PDFViewCtrl.GetAnnotationCanvas().

So, let’s say you want to place a widget on the top left corner of page 1. What you can do then is you can record that point as a reference in page space.

mPDFView.DocLockRead();

pdftron.PDF.Page page = mPDFView.GetDoc().GetPage(1);

pdftron.PDF.Rect rect = page.GetCropBox();

pdftron.Common.DoubleRef x1 = new pdftron.Common.DoubleRef(rect .x1);
pdftron.Common.DoubleRefy1 = new pdftron.Common.DoubleRef(rect .y1);
pdftron.Common.DoubleRef x2 = new pdftron.Common.DoubleRef(rect .x2);
pdftron.Common.DoubleRefy2 = new pdftron.Common.DoubleRef(rect .y2);

mPDFView.ConvPagePtToScreenPt(x1, y1, 1);
mPDFView.ConvPagePtToScreenPt(x2, y2, 1);

double sx = mPDFView.GetHScrollPos();

double sy = mPDFView.GetVScrollPos();

rect.Set(min_x + sx, min_y + sy, max_x + sx, max_y + sy);

rect.Normalize();

mPDFView.UnlockDocRead();

rect.x1 and rect.y1 now point to the top left corner of the page.

Canvas canvas = mPDFView.GetAnnotationCanvas();
myWidget.SetValue(Canvas.LeftProperty, rect.x1);

myWidget.SetValue(Canvas.TopProperty, rect.y1);

This will give you the point in page space. Now, you can subscribe to size changed and zoom changed events and so on (OnScale, OnSize, OnViewChanged) to resize and reposition your widget by re-using the code above.

Here are clearer instructions for adding a Widget that is fixed on a page using PDFViewWPF.

  1. Add a new variable to your class that references PDFViewWPF

private Rectangle _myWidget = null;

  1. Create an UpdateWidget method that converts from PDF page coordinates to screen coordinates and then applies scroll positioning

`
private void UpdateWidget()
{
if (_myWidget == null) return;

// 2 inches from corner, and 2 inches wide/high
double x1 = 144.0;
double y1 = 144.0;
double x2 = x1 + 144.0;
double y2 = y1 + 144.0;

Current_View.ConvPagePtToScreenPt(ref x1, ref y1, 1);
Current_View.ConvPagePtToScreenPt(ref x2, ref y2, 1);
pdftron.PDF.Rect r = new pdftron.PDF.Rect(x1, y1, x2, y2);
r.Normalize();

_myWidget.Width = r.Width();
_myWidget.Height = r.Height();
Canvas.SetLeft(_myWidget, r.x1 + Current_View.GetHScrollPos());
Canvas.SetTop(_myWidget, r.y1 + Current_View.GetVScrollPos());
}
`

  1. Call the UpdateWidget() method in listeners for the following events.
    PDFViewWPF.LayoutChanged PDFViewWPF.CurrentZoomChanged PDFViewWPF.CurrentPageNumberChanged

  2. Finally add the Widget to the PDFViewWPF canvas, for example on the PDFViewWPF.OnSetDoc event.

`
_myWidget = new Rectangle();
_myWidget.Fill = new SolidColorBrush() { Color = Colors.Blue };
_myWidget.Fill.Opacity = 0.3;
Canvas canvas = Current_View.GetCanvas();
canvas.Children.Add(_myWidget);
UpdateWidget();

`

Attached is fully working version of PDFViewControl.xaml.cs that you can swap into the PDFViewWPFTest sample project.

PDFViewControl.xaml.cs (35.9 KB)