getting absolute position

Help
unstools
2007-06-11
2013-04-29
  • unstools
    unstools
    2007-06-11

    I'm newbie to cobra and want to get some help from you.

    I want to convert html's rendered snapshot to another xml format but it needs absolute position of each element. So, I planned to use cobra to get absolute position of each html element without real rendering to Swing GUI.
    I think cobra support this operation, but I cannot find the way to get this information.

    So, how can I do that? Is there any easy way to get that information?

    Thank you.

     
    • xamjadmin
      xamjadmin
      2007-06-11

      The information is there, but you might need to make some classes public. Each DOM element has a getUINode() method. This will normally return an instance of RElement, which in turn has a getBounds() method. The bounds are not absolute, but absolute bounds can be calculated by looking at ancestor bounds.

       
      • unstools
        unstools
        2007-06-13

        Thank you for you reply.

        I have some questions again.
        (1) it seems that Cobra rendering feature only works while real GUI window is appered. I've tested with frame.setVisible(false); but result of UINode.getBounds() is not correct. Is there another method to get position information without GUI window?
        (2) In case of following html code, "font <b>strong</b> test", all getUINode() result are null - for "font", "<b>", "strong", and "test". How can I get the exact position of each #text node?

        I use following code.

            public void checkDocument(Document document)
            {
                checkNode((Node)document);
            }
            public void checkNode(Node node)
            {
                for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling())
                {
                    System.out.println("========");
                    System.out.println(child.getNodeName() + " : " + child.getNodeValue());
                    UINode uinode = ((NodeImpl)child).getUINode();
                    RElement relement = (RElement)uinode;
                    if (relement != null)
                        System.out.println("x:" + relement.getBounds().getX() + " y:" + relement.getBounds().getY());
                   
                    checkNode(child);
                }
            }

         
    • xamjadmin
      xamjadmin
      2007-06-13

      Hi again. The way it works is that there's a renderer tree that decorates the HTML DOM. The renderer tree is built when layout happens, specifically when HtmlBlockPanel.doLayout() is called (which should be called in the GUI thread). If getUINode() is null on a HTML DOM node, it means that layout has not occurred.

      There is a way for you to create a renderer tree without creating a HtmlPanel or a HtmlBlockPanel. You can directly create an instance of RBlock and call one of its layout() methods. For that you do have to provide implementations of the RenderableContainer and FrameContext interfaces. RBlock will be the root of the tree and you can get children with the getRenderables() method.

       
      • unstools
        unstools
        2007-06-14

        Question about your reply. You said, if getUINode() is null, layout has not occurred. But I think text related node does not provide UINode infomation.
        I have tested following html "<html><head><title>X</title></head><body> font <b>strong</b> test</body>" and get the result as below. <body> element returns UINode infomation, it means layout is done, I guess. This test code is called by AWT thread after window is displayed.

        ========
        html : null
        ========
        head : null
        ========
        title : null
        ========
        #text : X
        ========
        body : null
        x:8.0 y:8.0
        ========
        #text :  font
        ========
        b : null
        ========
        #text : strong
        ========
        #text :  test
        ========
        #text :

        And your advice about 2nd question, I will survey more.

        Thank you.

         
    • xamjadmin
      xamjadmin
      2007-06-14

      You're correct. Only block-level nodes have a UI node, e.g. divs, tables, table cells, lists, etc. You can find the position of a particular word (RWord) or a line (RLine) but it's not that easy to find the position of a text node or an inline node. It's easier to go from position to enclosing node.

       
    • stringout
      stringout
      2007-07-07

      Hi
      Im running in the same kind of problem, I need to get the position of every image within the page.

      Its my second day of cobra and i like it very much.

      ------------
      This is my code:

          Document parsedDocument = util.getParsedDocument(url);
          NodeListImpl images = (NodeListImpl)parsedDocument.getElementsByTagName("img");
         
          for(int j = 0; j < images.getLength();j++){
          HTMLImageElementImpl image = (HTMLImageElementImpl)images.item(j);
          UINode uiNode = image.getUINode();
          if (uiNode!=null)
              System.out.println("X: "uiNode.getBounds().getX()+"Y:" +uiNode.getBounds().getY());
          }
      ----------
      I read the posts before : but im kind of lost of how to get my code working.

      A little help would be appreciated.

      Thank you.
      good work i like cobra!

       
    • xamjadmin
      xamjadmin
      2007-07-08

      Hi again.

      Images do have UI nodes associated with them, so in your case that's doable. However, you need to have the document render first. Parsing is not enough.

      To render, one way is to create a HtmlBlockPanel, call setRootNode on it, and then doLayout(). This should be done in the GUI thread, by calling EventQueue.invokeLater().

      There's also a way to render without involving Swing components (other than Swing components that are necessarily created for inputs and images). See the method testRendererLoop() in MemoryTest.java. Basically, it does this:

      final FrameContext frameContext = new LocalFrameContext();
      final RenderableContainer renderableContainer = new LocalRenderableContainer();
      ...
      RBlock block = new RBlock((NodeImpl) doc, 0, rcontext.getUserAgentContext(), rcontext, frameContext, renderableContainer, "vertical");
      block.layout(100, 100);

       
    • stringout
      stringout
      2007-07-09

      Hi thank you for your help,
      it was very easy to find what you told me. Here is part of the memorytest code that I use to find the position of the images. Now the UINode of every image is not null but the position returned by each UINode is 0.

      ----------------------------------------------------------------------------------
      Test Page: http://www.pickargentina.com/test/imagesTest.html
      OutPut: (for every image).
      X: 0.0 Y: 0.0.
      ----------------------------------------------------------------------------------
      CODE:

      final FrameContext frameContext = new LocalFrameContext();
      final RenderableContainer renderableContainer = new LocalRenderableContainer();
      bin = new ByteArrayInputStream(content);
      Document document = builder.parse(new InputSourceImpl(bin, url.toExternalForm(), "ISO-8859-1"));
      final Document parsedDocument = document;

      RBlock block = new RBlock((NodeImpl) parsedDocument, 0, rcontext.getUserAgentContext(), rcontext, frameContext, renderableContainer, null);
      block.layout(500, 500);

      NodeListImpl images = (NodeListImpl)parsedDocument.getElementsByTagName("img");          for(int j = 0; j < images.getLength();j++){
           HTMLImageElementImpl image = (HTMLImageElementImpl)images.item(j);
           UINode uinode = image.getUINode();
           System.out.println("Source: "+image.getSrc()+" X: "+uinode.getBounds().getX()+"Y:" 
                              +uinode.getBounds().getY());
         
         }
      -----------------------------------------------------------------------------------
      Im missing something, here?

      Thanks for your help.
      esteban

       
    • stringout
      stringout
      2007-07-13

      S.O.S
      I need to get those positions. If its not implemented could you guide me where to start looking for implementing that functionality?
      thanks
      esteban

       
    • I forgot to mention that the UINode bounds are relative to its parent. You need to cast UINode to BoundableRenderable and call getGUIPoint(0,0) instead. I went ahead and tested it. You can use the following code with 0.97.

      package org.lobobrowser.cobratest;

      import java.io.InputStream;
      import java.net.HttpURLConnection;
      import java.net.URL;
      import java.net.URLConnection;

      import org.lobobrowser.html.*;
      import org.lobobrowser.html.test.*;
      import org.lobobrowser.html.domimpl.*;
      import org.lobobrowser.html.gui.*;
      import org.lobobrowser.html.parser.DocumentBuilderImpl;
      import org.lobobrowser.html.parser.InputSourceImpl;
      import org.lobobrowser.html.renderer.*;
      import org.lobobrowser.util.io.IORoutines;
      import org.w3c.dom.Document;

      import java.util.logging.*;
      import java.io.*;
      import javax.swing.*;
      import java.awt.*;

      /**
      * Checks for memory leaks.
      */
      public class ImageLocation {
          private static final Logger logger = Logger.getLogger(ImageLocation.class.getName());
         
          /**
           * @param args
           */
          public static void main(String[] args) throws Exception {
              ImageLocation mt = new ImageLocation();
              mt.showImagePositions();
          }
         
          public static final String TEST_URL = "http://lobobrowser.org/java-browser.jsp";

          private void showImagePositions() throws Exception {
              URL url = new URL(TEST_URL);
              URLConnection connection = url.openConnection();
              connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible;) Cobra/0.96.1+");
              connection.setRequestProperty("Cookie", "");
              if(connection instanceof HttpURLConnection) {
                  HttpURLConnection hc = (HttpURLConnection) connection;
                  hc.setInstanceFollowRedirects(true);
                  int responseCode = hc.getResponseCode();
                  logger.info("process(): HTTP response code: " + responseCode);
              }
              InputStream in = connection.getInputStream();
              byte[] content;
              try {
                  content = IORoutines.load(in, 8192);
              } finally {
                  in.close();
              }
              //String source = new String(content, "ISO-8859-1");
              //long time1 = System.currentTimeMillis();
              logger.info("Content size: " + content.length + " bytes.");
              final FrameContext frameContext = new LocalFrameContext();
              final RenderableContainer renderableContainer = new LocalRenderableContainer();
              ByteArrayInputStream bin = new ByteArrayInputStream(content);
              UserAgentContext ucontext = new SimpleUserAgentContext();
              HtmlRendererContext rcontext = new SimpleHtmlRendererContext(new HtmlPanel());
              DocumentBuilderImpl builder = new DocumentBuilderImpl(ucontext);
              Document document = builder.parse(new InputSourceImpl(bin, url.toExternalForm(), "ISO-8859-1"));
              final Document parsedDocument = document;

              RBlock block = new RBlock((NodeImpl) parsedDocument, 0, rcontext.getUserAgentContext(), rcontext, frameContext, renderableContainer, RBlock.OVERFLOW_NONE);
              block.layout(500, 500);

              NodeListImpl images = (NodeListImpl)parsedDocument.getElementsByTagName("img"); for(int j = 0; j < images.getLength();j++){
                  HTMLImageElementImpl image = (HTMLImageElementImpl)images.item(j);
                  UINode uinode = image.getUINode();
                  BoundableRenderable br = (BoundableRenderable) uinode;
                  Point guiPoint = br.getGUIPoint(0, 0);
                  System.out.println("Source: "+image.getSrc()+"; GUI point: " + guiPoint);
              }        
          }

          private class LocalRenderableContainer implements RenderableContainer {
              public void invalidateLayoutUpTree() {
                  // nop
              }
             
              public Component add(Component component) {
                  //nop
                  return null;
              }
             
              public void remove(Component c) {
                  // nop
              }

              public Color getPaintedBackgroundColor() {
                  return Color.BLACK;
              }

              public Insets getInsets() {
                  return new Insets(0, 0, 0, 0);
              }

              public void repaint(int x, int y, int width, int height) {
              }

              public void relayout() {
                  // nop
              }

              public void updateAllWidgetBounds() {
                  // nop
              }

              public Point getGUIPoint(int clientX, int clientY) {
                  return new Point(clientX, clientY);
              }

              public void focus() {
                  //nop
              }

              public void addDelayedPair(DelayedPair pair) {
                  //nop
              }

              public RenderableContainer getParentContainer() {
                  return null;
              }   
          }
         
          private class LocalFrameContext implements FrameContext {
              public void expandSelection(RenderableSpot rpoint) {
              }

              public void resetSelection(RenderableSpot rpoint) {
              }

              public void delayedRelayout(NodeImpl node) {
              }
          }
      }

       
      • Note: In newer versions of Cobra, block.adjust() should be called after block.layout().

         
        • archnemesis
          archnemesis
          2008-11-30

          I have gone thru all the images and gotten their locations using:
          Point guiPoint = br.getGUIPoint(0, 0);

          I have also gotten their widths and heights, but the guiPoint returns extreme numbers such as 98787
          for the x and y positions.  How do I convert these to the java coordinate system?

           
    • stringout
      stringout
      2007-07-13

      Thanks for your help! now it works great.
      Nice project it being very usefull for me.
      bye, esteban

       
    • stringout
      stringout
      2007-07-16

      Hi again.
      I managed to get the Images, and Tables position.
      Now I need to get the position of A nodes and TEXT nodes.
      As I read in the previous post A and TEXT nodes doesnt have a UINODE associated with them so I cant get the position.

      1)Is it possible to associate a UiNode and the position with those elements?
      2)If so where should I start looking to implement it?

      Thanks
      esteban

       
    • That's trickier. Only boxes and Swing controls have UINodes. Inline elements like A and SPAN do not.

      It should be possible to get that information though. You need to traverse the renderer tree (whose root is the RBlock you created) instead. Every Renderable node (e.g. RWord) points to a DOM node (via modelNode). That's how you can find it.

       
    • qoowater
      qoowater
      2008-02-25

      Hello :

      I have read the whole discussion list, and tried the above example code out to get the image position successfully.
      But have no idea "how to traverse the renderer tree" as mentioned at the last reply. Could you please give more detail explanation about this? Or where can i find the information.

      Thanks in advance !

       
    • RBlock is a tree root. You can get its children by calling RBlock.getRenderables(). That's an iterator of Renderable objects. If the Renderable casts to RCollection, you can call getRenderables() on it again. So it's not that hard to write a recursive method that traverses the whole renderer tree, getting positions for all renderable nodes therein. Does that help?

      BTW, in 0.97.5 calling RBlock.layout() is not enough. There's RBlock.adjust() which completes some aspects of the layout process.

       
      • qoowater
        qoowater
        2008-02-26

        Hi:
        Many thanks for ur quick reply. The information is very useful, and I have tried it out.
        There is my traversal code:

        private void traversal (RCollection root)
            {
                Iterator children=root.getRenderables();
                while(children!=null && children.hasNext())
                {
                    Renderable node=(Renderable)children.next();
                    if (node instanceof BoundableRenderable)
                    {
                        BoundableRenderable box=(BoundableRenderable)node;
                        ModelNode modelNode=box.getModelNode();
                        String nodename="";
                        if(modelNode instanceof NodeImpl)
                        {
                            NodeImpl domnode=(NodeImpl)modelNode;
                            nodename=domnode.getNodeName();
                        }
                        int x=box.getX();
                        int y=box.getY();
                        int x2=x+box.getHeight();
                        int y2=y+box.getWidth();
                        System.out.println(nodename+"["+x+","+y+"]-["+x2+","+y2+"]");
                    }
                    if (node instanceof RCollection)
                    {
                        traversal((RCollection)node);
                    }
                   
                }
            }

        BTW, i found a problem that RCollection is not declared as public interface, therefore i try to modify the source code of Cobra and it work finally.

        Again, Thanks for ur contribution, it is very helpful for me.

         
    • FYI - There's a new HtmlBlockPanel method in 0.98 that can get your the rectangular bounds of a DOM node relative to the root block. That's what is used in Anchor support.

       
    • archnemesis
      archnemesis
      2008-11-30

      Ignore that last post.  I see now that the answer is right above my first post.

      block.layout(500, 500);
      block.adjust();

      does the trick.  Thanks!

       
  • rad
    rad
    2010-02-18

    I am using Cobra 0.89.4 to get absolute positions of text nodes from  this Web page:
    "<HTML><TITLE>Some Country Codes</TITLE>
    <BODY><B>Some Country Codes</B><P>
    <B>Congo</B> <I>242</I><BR>
    <B>Egypt</B> <I>20</I><BR>
    <B>Belize</B> <I>501</I><BR>
    <B>Spain</B> <I>34</I><BR>

    <HR><B>End</B></BODY></HTML>
    "
    I have drawn the boxes generated by my code and I remarked that "Some Country Codes" and "Congo 242" are in the same line, but using firefox they are rendered in separated lines. I don't understand that ? is it a bug ?
    thx for help.
    here my code:

    public class ElementLocation {
    private static final Logger logger = Logger.getLogger(ElementLocation.class.getName());

    UserAgentContext ucontext;
    HtmlRendererContext rcontext;
    final FrameContext frameContext = new LocalFrameContext();
    HtmlBlockPanel hb;

    /**
    * @param args
    */
    public static void main(String args) throws Exception {
    ElementLocation mt = new ElementLocation();
    mt.showElementPositions();
    }

    public static final String TEST_URL = "file:///C:/travail/cc0.htm";

    private void showElementPositions() throws Exception {
    URL url = new URL(TEST_URL);
    URLConnection connection = url.openConnection();
    connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible;) Cobra/0.96.1+");
    connection.setRequestProperty("Cookie", "");
    if(connection instanceof HttpURLConnection) {
    HttpURLConnection hc = (HttpURLConnection) connection;
    hc.setInstanceFollowRedirects(true);
    int responseCode = hc.getResponseCode();
    logger.info("process(): HTTP response code: " + responseCode);
    }
    InputStream in = connection.getInputStream();
    byte content;
    try {
    content = IORoutines.load(in, 8192);
    } finally {
    in.close();
    }
    //String source = new String(content, "ISO-8859-1");
    //long time1 = System.currentTimeMillis();
    logger.info("Content size: " + content.length + " bytes.");

    final RenderableContainer renderableContainer = new LocalRenderableContainer();
    ByteArrayInputStream bin = new ByteArrayInputStream(content);
    ucontext = new SimpleUserAgentContext();
    rcontext = new SimpleHtmlRendererContext(new HtmlPanel());
    DocumentBuilderImpl builder = new DocumentBuilderImpl(ucontext);
    Document document = builder.parse(new InputSourceImpl(bin, url.toExternalForm(), "ISO-8859-1"));
    final Document parsedDocument = document;

    hb = new HtmlBlockPanel(ucontext, rcontext, frameContext);
    RBlock block = new RBlock((NodeImpl) parsedDocument, 0, rcontext.getUserAgentContext(), rcontext, frameContext, renderableContainer);
    block.layout(800, 600,false);
    //block.adjust();

    Iterator chld = block.getRenderables();
    while(chld!=null && chld.hasNext()){
        Renderable node=(Renderable)chld.next();
        if(node instanceof RCollection)
            traversal((RCollection)node);
    }
    }
    private void traversal (RCollection root){
    int originx = root.getX();
    int originy = root.getY();
    Iterator children=root.getRenderables();
    while(children!=null && children.hasNext()){
       
        Renderable node=(Renderable)children.next();
        if (node instanceof BoundableRenderable){
        BoundableRenderable box=(BoundableRenderable)node;
        ModelNode modelNode=box.getModelNode();
        String nodename="";
            if(modelNode instanceof NodeImpl){
            NodeImpl domnode=(NodeImpl)modelNode;
            nodename=domnode.getNodeName();
            }
        int x= originx + box.getX();
        int y= originy + box.getY();
        int x2=x+box.getHeight();
        int y2=y+box.getWidth();
        System.out.println(nodename+"- width="+box.getWidth()+" Height="+box.getHeight());   
        }
        if (node instanceof RCollection){
        traversal((RCollection)node);
        }
    }
    }//end while
    }