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

576 views
Skip to first unread message

Barry O'Neill

unread,
Feb 8, 2015, 3:46:26 PM2/8/15
to pdfne...@googlegroups.com
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<Annot> 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

Ryan

unread,
Feb 12, 2015, 8:07:04 PM2/12/15
to pdfne...@googlegroups.com
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)) {/*...*/}




Message has been deleted

Barry O'Neill

unread,
Mar 3, 2015, 5:44:42 PM3/3/15
to pdfne...@googlegroups.com
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<Rect> bboxes,  ColorPt highlight_color) {

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


        public static Rect RectUnion(List<Rect> 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);

Barry O'Neill

unread,
Mar 3, 2015, 5:44:42 PM3/3/15
to pdfne...@googlegroups.com
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



On Thursday, 12 February 2015 20:07:04 UTC-5, Ryan wrote:

Barry O'Neill

unread,
Mar 3, 2015, 5:44:42 PM3/3/15
to pdfne...@googlegroups.com
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<Annot> GetAnnotsFromQuads(pdftron.PDF.PDFViewWPF.Selection selection)
        {

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

            List<Annot> annotList = new List<Annot>();

            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];

                    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



On Thursday, 12 February 2015 20:07:04 UTC-5, Ryan wrote:
Reply all
Reply to author
Forward
0 new messages