How can I get the Rect from a text selections Quads ?

Hi,

I’m trying to find out if a selected word (double-click) is part of an highlight annotation.

It would seem that the way to do it is to get a Rect for the click word, and then compare that to all the highlight rects in the document, and if it’s within any of the existing highlight Rect’s, then I know that specific highlight is the one the user clicked on/within.

I see “Quads” as a property of the selection, but annotations are saved with Rec’s…so I need to get Rec equivalent to the Quads.

I’m using PDFViewWPF, and C#.

This what I have currently:

_pdfToolManager.CreateTool(pdftron.PDF.Tools.ToolManager.ToolType.e_text_select, null, true);

_pdfViewer.MouseUp += CurrentTool_MouseLeftButtonUp;

.
.
.
double[] selectedWordQuads = selection.GetQuads();
Rect selectedWordRect = ??? //CODE FOR GET RECT FROM QUADS

…and then call…

private Annot GetAnnotationFromRect(Rect pageCoordinate, List allDocAnnotations)
{

for (var i = 0; i < allDocAnnotations.Count; i++)
{
Annot annot = allDocAnnotations[i];

Rect rect = new Rect();
rect.x1 = annot.GetRect().x1;
rect.y1 = annot.GetRect().y1;
rect.x2 = annot.GetRect().x2;
rect.y2 = annot.GetRect().y2;

if (pageCoordinate.x1 >= rect.x1 && pageCoordinate.x2 <= rect.x2 && pageCoordinate.y1 >= rect.y1 && pageCoordinate.y2 <= rect.y2)
{
Console.WriteLine(“getAnnotationByMouseEvent - ANNOTATION EXISTS FOR CLICKED WORD”);
return annot;
}
}

return null;
}

Thanks,

Barry

You can run through the array of doubles that from the quads, and for every pair, make a Rect, and add that to a new array of Rects. Sorry this isn’t done for you.

An option to improve throughput, is to create a single bounding box of the quads, and then get candidate annotations. Then run again, but against each individual quad/rect to find the match.

Note, instead of doing the math at the end with all four rect points, you can just do the following.

Rect intersection = new Rect(); if(intersection.IntersectRect(selection_rect, annot_rect)) {/*...*/}

Do you have a more full example of this ?

I have my captured selection, and I need a pdftron.PDF.Rect from it:

double[] quads = selection.GetQuads();
int numQuads = quads.Length / 8;

if (quads.Length >= 8)
{

for (int i = 0; i < numQuads; i++)
{
quadNumber = i * 8;

double x1 = quads[quadNumber + 0];
double y1 = quads[quadNumber + 1];

double x2 = quads[quadNumber + 2];
double y2 = quads[quadNumber + 3];

double x3 = quads[quadNumber + 4];
double y3 = quads[quadNumber + 5];

double x4 = quads[quadNumber + 6];
double y4 = quads[quadNumber + 7];

pdftron.PDF.Rect pdftronRect = new Rect();
pdftronRect…

…what happens next ?

Thanks,

Barry

I came up with this, this afternoon, and it seems to work fairly well. It will give me an List of annots for the selection:

private List GetAnnotsFromQuads(pdftron.PDF.PDFViewWPF.Selection selection)
{
//http://www.pdftron.com/pdfnet/samplecode/PDFViewControl.xaml.cs.html

//THIS WILL CAPTURE THE WINDOWS SELECTION’S RECTS AND CONVERT THEM TO PDFtron ANNOT OBJECTS

List annotList = new List();

int quadNumber = 0;
int pageNumber = selection.GetPageNum();
double[] quads = selection.GetQuads();
int numQuads = quads.Length / 8;

if (quads.Length >= 8) //must have at least 8 points to be valid
{
Console.WriteLine("GetRectsFromQuads - numQuads: " + numQuads.ToString());

for (int i = 0; i < numQuads; i++)
{
quadNumber = i * 8;

double x1 = quads[quadNumber + 0];
double y1 = quads[quadNumber + 1];

double x2 = quads[quadNumber + 2];
double y2 = quads[quadNumber + 3];

double x3 = quads[quadNumber + 4];
double y3 = quads[quadNumber + 5];

double x4 = quads[quadNumber + 6];
double y4 = quads[quadNumber + 7];

//https://groups.google.com/forum/#!searchin/pdfnet-sdk/rect$20from$20quads/pdfnet-sdk/fP_cynRYurE/r8XczPrGOVEJ
double rectX1 = Math.Min(Math.Min(Math.Min(quads[quadNumber + 0], quads[quadNumber + 2]), quads[quadNumber + 4]), quads[quadNumber + 6]);
double rectX2 = Math.Max(Math.Max(Math.Max(quads[quadNumber + 0], quads[quadNumber + 2]), quads[quadNumber + 4]), quads[quadNumber + 6]);
double rectY1 = Math.Min(Math.Min(Math.Min(quads[quadNumber + 1], quads[quadNumber + 3]), quads[quadNumber + 5]), quads[quadNumber + 7]);
double rectY2 = Math.Max(Math.Max(Math.Max(quads[quadNumber + 1], quads[quadNumber + 3]), quads[quadNumber + 5]), quads[quadNumber + 7]);

Rect selectionRect = new Rect(rectX1, rectY1, rectX2, rectY2);
//Console.WriteLine("GetRectsFromQuads - aRect: " + rectX1.ToString() + " | " + rectY1.ToString() + " | " + rectX2.ToString() + " | " + rectY2.ToString());

ColorPt defaultColour = new ColorPt(250, 246, 5, 0.3);;
Annot highlightAnnot = CreateHighlightAnnot(_pdfDocument, selectionRect, defaultColour);

annotList.Add(highlightAnnot);

//--------------------------------------------------------------------
//DEBUG TEST - REVERSE OF PDFtron BACK TO WINDOWS SELECTION
//(this should highlight the exact same text just selected)

List<System.Windows.Shapes.Rectangle> _onScreenSelection = new List<System.Windows.Shapes.Rectangle>();
System.Windows.Controls.Canvas annotCanvas = _pdfViewer.GetCanvas();

foreach (Annot annot in annotList)
{
Rect aRect = annot.GetRect();

pdftron.SDF.Obj quadPoints = annot.GetSDFObj().PutArray(“QuadPoints”);
quadPoints.PushBackNumber(aRect.x1);
quadPoints.PushBackNumber(aRect.y2);
quadPoints.PushBackNumber(aRect.x2);
quadPoints.PushBackNumber(aRect.y2);
quadPoints.PushBackNumber(aRect.x1);
quadPoints.PushBackNumber(aRect.y1);
quadPoints.PushBackNumber(aRect.x2);
quadPoints.PushBackNumber(aRect.y1);

quadNumber = i * 8;
x1 = quads[quadNumber + 0];
y1 = quads[quadNumber + 1];

x2 = quads[quadNumber + 2];
y2 = quads[quadNumber + 3];

x3 = quads[quadNumber + 4];
y3 = quads[quadNumber + 5];

x4 = quads[quadNumber + 6];
y4 = quads[quadNumber + 7];

_pdfViewer.ConvPagePtToScreenPt(ref x1, ref y1, pageNumber);
_pdfViewer.ConvPagePtToScreenPt(ref x2, ref y2, pageNumber);
_pdfViewer.ConvPagePtToScreenPt(ref x3, ref y3, pageNumber);
_pdfViewer.ConvPagePtToScreenPt(ref x4, ref y4, pageNumber);

double left, right, top, bottom;

left = Math.Min(x1, Math.Min(x2, Math.Min(x3, x4)));
right = Math.Max(x1, Math.Max(x2, Math.Max(x3, x4)));
top = Math.Min(y1, Math.Min(y2, Math.Min(y3, y4)));
bottom = Math.Max(y1, Math.Max(y2, Math.Max(y3, y4)));

System.Windows.Rect windowsRect = new System.Windows.Rect(left, top, right - left, bottom - top);

System.Windows.Shapes.Rectangle highlight = new System.Windows.Shapes.Rectangle();
highlight.Fill = new System.Windows.Media.SolidColorBrush() { Color = System.Windows.Media.Colors.Red };
highlight.Fill.Opacity = 0.3;
highlight.Width = windowsRect.Width;
highlight.Height = windowsRect.Height;
System.Windows.Controls.Canvas.SetLeft(highlight, windowsRect.Left + _pdfViewer.GetHScrollPos());
System.Windows.Controls.Canvas.SetTop(highlight, windowsRect.Top + _pdfViewer.GetVScrollPos());
annotCanvas.Children.Add(highlight);
_onScreenSelection.Add(highlight);
}
//--------------------------------------------------------------------

}

}

Console.WriteLine("GetRectsFromQuads - TOTAL RECTS FOR HIGHLIGHT: " + annotList.Count.ToString());

return annotList;
}

//HELPERS

// Use PDFNet to generate appearance stream for highlight annotation.
public pdftron.SDF.Obj CreateHighlightAppearance(PDFDoc doc, pdftron.PDF.Rect bbox, ColorPt higlight_color)
{

ElementBuilder build = new ElementBuilder();
ElementWriter writer = new ElementWriter();
writer.Begin(doc);

// Draw background
Element element = build.CreateRect(bbox.x1 - 2, bbox.y1, bbox.x2 + 2, bbox.y2);
element.SetPathFill(true);
element.SetPathStroke(false);
GState gs = element.GetGState();
gs.SetFillColorSpace(ColorSpace.CreateDeviceRGB());
gs.SetFillColor(higlight_color);
gs.SetBlendMode(GState.BlendMode.e_bl_multiply);
writer.WriteElement(element);
pdftron.SDF.Obj stm = writer.End();

build.Dispose();
writer.Dispose();

// Set the bounding box
stm.PutRect(“BBox”, bbox.x1, bbox.y1, bbox.x2, bbox.y2);
stm.PutName(“Subtype”, “Form”);
return stm;
}

// Create Highlight Annotation.
public Annot CreateHighlightAnnot(PDFDoc doc, pdftron.PDF.Rect rect, ColorPt highlight_color)
{
Annot a = Annot.Create(doc, Annot.Type.e_Highlight, rect);
a.SetColor(highlight_color);
a.SetAppearance(CreateHighlightAppearance(doc, rect, highlight_color));

pdftron.SDF.Obj quads = a.GetSDFObj().PutArray(“QuadPoints”);
quads.PushBackNumber(rect.x1);
quads.PushBackNumber(rect.y2);
quads.PushBackNumber(rect.x2);
quads.PushBackNumber(rect.y2);
quads.PushBackNumber(rect.x1);
quads.PushBackNumber(rect.y1);
quads.PushBackNumber(rect.x2);
quads.PushBackNumber(rect.y1);
return a;
}

…so that takes care of “You can run through the array of doubles that from the quads, and for every pair, make a Rect, and add that to a new array of Rects. Sorry this isn’t done for you.”

So, in your opinion what would be the most efficient way to look for over-lapping annotations at this point ?

A “bounding box” sounds interesting, but it can’t be a “x1, y1, x2, y2” rectangle because that will potentially include words that weren’t selected (highlighted) by the user, they just happened to exist int he same overall area…

thanks,

Barry

This helped…gleaned it from another posting (RectUnion is the meat of it, and what I am now using as a parent Rect to all the indovidual Rects withing from each created Annot, from the Quads):

private static Annot CreateBoundingAnnot(PDFDoc doc, List bboxes, ColorPt highlight_color) {

Annot boundingAnnot = Annot.Create(doc, Annot.Type.e_Highlight, RectUnion(bboxes));
return boundingAnnot;
}

public static Rect RectUnion(List boxes)
{
double y1 = boxes.Select(box => box.y1).Min();
double y2 = boxes.Select(box => box.y2).Max();
double x1 = boxes.Where(box => box.y1 == y1).Select(box => box.x1).Min();
double x2 = boxes.Where(box => box.y2 == y2).Select(box => box.x2).Max();
return new Rect(x1, y1, x2, y2);
}