RusKnyaz / Optimus

Optimus is headless Web Browser fully implemented on .net.
MIT License
81 stars 8 forks source link

GetBoundingClientRect seems to be always 0 #51

Closed vivek306 closed 3 years ago

vivek306 commented 3 years ago

Can you please give an example for getting the co-ordinates of html elements?

var engine = EngineBuilder.New().UseJint()// Enable JavaScripts execution..Build(); // Builds the Optimus engine.
var page = await engine.OpenUrl("http://google.com");
var element = engine.Document.GetElementById("hplogo");
var boundingBox = element.GetBoundingClientRect();
RusKnyaz commented 3 years ago

The Optimus itself does not make layout or rendering.

vivek306 commented 3 years ago

Thanks :) is there any rendering engine that can be attached to this?

RusKnyaz commented 3 years ago

There is Knyaz.Optimus.Graphics. But first of all, it is too unstable. Secondly, there is no way to connect to the Optimus to enable GetBoundingClientRect. I will think about how to add such a feature.

RusKnyaz commented 3 years ago

New API in Optimus

//Define own layout service
class MyDocumentLayout: ILayoutService
{
    public RectangleF[] GetElementBounds(Element element)
    {
        //todo: evaluate element rectangles
    }
}
...
//Attach layout service to the document using extension method
engine.Document.AttachLayoutService(new MyDocumentLayout());
RusKnyaz commented 3 years ago

New API in Optimus.Graphics

using Knyaz.Optimus.Graphics;

//create document layout and attach it to the document
engine.Document.CreateLayout();
vivek306 commented 3 years ago

hey the latest version 0.2.0-alpha-93 does not seem to have CreateLayout. @RusKnyaz can you please let me know when you are planning to launch the new API for Graphics?

RusKnyaz commented 3 years ago

I uploaded latest 0.2.0-alpha-109. Check it.

vivek306 commented 3 years ago

sorry i am still not sure on how to evaluate the element rectangles. Currently i set it up as follows and it crashes with "Value cannot be null. (Parameter 'source')"

                var engine = EngineBuilder.New().Build();
                var page = await engine.OpenUrl("http://google.com");
                var doc = page.Document;

                var myLayout = new MyDocumentLayout();
                doc.AttachLayoutService(myLayout);
                var layout = doc.CreateLayout();

                var element = engine.Document.GetElementById("hplogo");
                var value =  myLayout.GetElementBounds(element);

MyDocumentLayout is the same as you recommended above but with following changes

    class MyDocumentLayout : ILayoutService
    {
        public System.Drawing.RectangleF[] GetElementBounds(Knyaz.Optimus.Dom.Elements.Element element)
        {
            var boundingBox = element.GetBoundingClientRect();

            return new [] { new System.Drawing.RectangleF(boundingBox.X, boundingBox.Y, boundingBox.Width, boundingBox.Height) };
        }
    }
RusKnyaz commented 3 years ago

doc.AttachLayoutService should be used if you want to use your own rendering engine. doc.CreateLayout should be used if you want to use Knyaz.Optimus.Graphics rendering engine.

You shouldn't use both at the same time. Try to remove these two lines:

var myLayout = new MyDocumentLayout();
doc.AttachLayoutService(myLayout);

If the problem persists, please attach a full stack trace.

vivek306 commented 3 years ago

great, that makes more sense

                var engine = EngineBuilder.New().Build();
                var page = await engine.OpenUrl("http://google.com");
                var doc = page.Document;
                var layout = doc.CreateLayout();
                var element = engine.Document.GetElementById("hplogo");
                var boundingBox = element.GetBoundingClientRect();

I tried using Knyaz.Optimus.Graphics rendering engine and it gives the following error after "GetBoundingClientRect"

   at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at Knyaz.Optimus.Graphics.DocumentLayout.Knyaz.Optimus.ILayoutService.GetElementBounds(Element elt)
   at Knyaz.Optimus.Dom.Elements.Element.GetBoundingClientRect()

Do u recommend any other rendering engine that works well with ILayoutService?

RusKnyaz commented 3 years ago

My bad. Layout process must be done before. You have to call Refresh method to preform html document layout within a given window size, Update your code:

doc.CreateLayout().Refresh(new Size(1000,1000));

My library is no so popular. So, there is only one rendering engine. And this engine doesn't do a lot. Therefore, be prepared for the fact that the result will be different from what is expected, or even be empty rectangle.

vivek306 commented 3 years ago

Great this works :)

Is there a way to save the layout as picture to get an idea of how it rendered the html?

RusKnyaz commented 3 years ago

Yes, there is.

var bitmap = engine.ToImage(1000, 1000);//Window's size
bitmap.Save("goo.bpm", ImageFormat.Bmp);