/***************************************************************************
 begin       : Sat Feb 20 2010
 copyright   : (C) 2010 by Martin Preuss
 email       : martin@libchipcard.de

 ***************************************************************************
 *          Please see toplevel file COPYING for license details           *
 ***************************************************************************/



#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#define DISABLE_DEBUGLOG


#include "htmlctx_p.h"
#include "o_box_l.h"
#include "g_box_l.h"

#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>



GWEN_INHERIT(GWEN_XML_CONTEXT, HTML_XMLCTX)




GWEN_XML_CONTEXT *HtmlCtx_new(uint32_t flags)
{
  GWEN_XML_CONTEXT *ctx;
  HTML_XMLCTX *xctx;
  HTML_GROUP *g;
  HTML_OBJECT *o;

  /* create base object */
  ctx=GWEN_XmlCtx_new(flags);
  assert(ctx);

  /* create and assign extension */
  GWEN_NEW_OBJECT(HTML_XMLCTX, xctx);
  assert(xctx);
  GWEN_INHERIT_SETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx, xctx,
                       HtmlCtx_FreeData);

  /* set virtual functions */
  GWEN_XmlCtx_SetStartTagFn(ctx, HtmlCtx_StartTag);
  GWEN_XmlCtx_SetEndTagFn(ctx, HtmlCtx_EndTag);
  GWEN_XmlCtx_SetAddDataFn(ctx, HtmlCtx_AddData);
  GWEN_XmlCtx_SetAddCommentFn(ctx, HtmlCtx_AddComment);
  GWEN_XmlCtx_SetAddAttrFn(ctx, HtmlCtx_AddAttr);

  xctx->objects=HtmlObject_Tree_new();
  xctx->mediaPaths=GWEN_StringList_new();

  xctx->resolutionX=75;
  xctx->resolutionY=75;

  /* create initial group */
  g=HtmlGroup_Box_new("HTML_ROOT", NULL, ctx);
  assert(g);
  o=HtmlObject_Box_new(ctx);
  HtmlGroup_SetObject(g, o);
  HtmlObject_Tree_Add(xctx->objects, o);
  HtmlCtx_SetCurrentGroup(ctx, g);

  /* return base object */
  return ctx;
}



GWENHYWFAR_CB
void HtmlCtx_FreeData(GWEN_UNUSED void *bp, void *p)
{
  HTML_XMLCTX *xctx;
  HTML_GROUP *g;

  xctx=(HTML_XMLCTX *)p;

  g=xctx->currentGroup;
  while (g) {
    HTML_GROUP *gParent;

    gParent=HtmlGroup_GetParent(g);
    HtmlGroup_free(g);
    g=gParent;
  }

  GWEN_DB_Group_free(xctx->dbCurrentAttribs);
  free(xctx->currentTagName);
  HtmlObject_Tree_free(xctx->objects);

  GWEN_StringList_free(xctx->mediaPaths);

  HtmlProps_free(xctx->standardProps);

  GWEN_FREE_OBJECT(xctx);
}



int HtmlCtx_GetResolutionX(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->resolutionX;
}



void HtmlCtx_SetResolutionX(GWEN_XML_CONTEXT *ctx, int i)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  xctx->resolutionX=i;
}



int HtmlCtx_GetResolutionY(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->resolutionY;
}



void HtmlCtx_SetResolutionY(GWEN_XML_CONTEXT *ctx, int i)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  xctx->resolutionY=i;
}



GWEN_STRINGLIST *HtmlCtx_GetMediaPaths(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->mediaPaths;
}



void HtmlCtx_AddMediaPath(GWEN_XML_CONTEXT *ctx, const char *s)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  assert(s && *s);
  GWEN_StringList_AppendString(xctx->mediaPaths, s, 0, 1);
}




HTML_GROUP *HtmlCtx_GetCurrentGroup(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->currentGroup;
}



void HtmlCtx_SetCurrentGroup(GWEN_XML_CONTEXT *ctx, HTML_GROUP *g)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  xctx->currentGroup=g;
}



const char *HtmlCtx_GetCurrentTagName(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->currentTagName;
}



void HtmlCtx_SetCurrentTagName(GWEN_XML_CONTEXT *ctx, const char *s)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  free(xctx->currentTagName);
  if (s)
    xctx->currentTagName=strdup(s);
  else
    xctx->currentTagName=NULL;
}



GWEN_DB_NODE *HtmlCtx_GetCurrentAttributes(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->dbCurrentAttribs;
}



HTML_OBJECT_TREE *HtmlCtx_GetObjects(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->objects;
}



HTML_OBJECT_TREE *HtmlCtx_TakeObjects(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;
  HTML_OBJECT_TREE *t;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  t=xctx->objects;
  xctx->objects=NULL;

  return t;
}



HTML_OBJECT *HtmlCtx_GetRootObject(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return HtmlObject_Tree_GetFirst(xctx->objects);
}



int HtmlCtx_SanitizeData(GWEN_UNUSED GWEN_XML_CONTEXT *ctx,
                         const char *data,
                         GWEN_BUFFER *buf)
{
  if (data && *data) {
    const uint8_t *p;
    uint8_t *dst;
    uint8_t *src;
    unsigned int size;
    unsigned int i;
    int lastWasBlank;
    uint8_t *lastBlankPos;
    uint32_t bStart=0;

    if (GWEN_Text_UnescapeXmlToBuffer(data, buf)) {
      DBG_INFO(GWEN_LOGDOMAIN, "here");
      return GWEN_ERROR_BAD_DATA;
    }
    if (GWEN_Buffer_GetUsedBytes(buf)==0) {
      DBG_INFO(GWEN_LOGDOMAIN, "Empty data.");
      return 0;
    }

    dst=(uint8_t *)GWEN_Buffer_GetStart(buf);
    src=dst;

    /* skip leading blanks */
    while (*src && (*src<33 || *src==127))
      src++;

    p=src;
    bStart=src-((uint8_t *)GWEN_Buffer_GetStart(buf));
    size=GWEN_Buffer_GetUsedBytes(buf)-bStart;
    lastWasBlank=0;
    lastBlankPos=0;

    for (i=0; i<size; i++) {
      uint8_t c;

      c=*p;
      if (c<32 || c==127)
        c=32;

      /* remember next loop whether this char was a blank */
      if (c==32) {
        if (!lastWasBlank) {
          /* store only one blank */
          lastWasBlank=1;
          lastBlankPos=dst;
          *(dst++)=c;
        }
      }
      else {
        lastWasBlank=0;
        lastBlankPos=0;
        *(dst++)=c;
      }
      p++;
    }

    /* remove trailing blanks */
    if (lastBlankPos!=0)
      dst=lastBlankPos;

    size=dst-(uint8_t *)GWEN_Buffer_GetStart(buf);
    GWEN_Buffer_Crop(buf, 0, size);
  }

  return 0;
}



HTML_PROPS *HtmlCtx_GetStandardProps(const GWEN_XML_CONTEXT *ctx)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  return xctx->standardProps;
}



void HtmlCtx_SetStandardProps(GWEN_XML_CONTEXT *ctx, HTML_PROPS *pr)
{
  HTML_XMLCTX *xctx;
  HTML_OBJECT *o;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  HtmlProps_Attach(pr);
  HtmlProps_free(xctx->standardProps);
  xctx->standardProps=pr;

  if (pr) {
    if (xctx->currentGroup && HtmlGroup_GetParent(xctx->currentGroup)==NULL)
      HtmlGroup_SetProperties(xctx->currentGroup, pr);
    o=HtmlObject_Tree_GetFirst(xctx->objects);
    if (o && HtmlObject_GetProperties(o)==NULL)
      HtmlObject_SetProperties(o, pr);
  }
}



int HtmlCtx_StartTag(GWEN_XML_CONTEXT *ctx, const char *tagName)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  DBG_DEBUG(GWEN_LOGDOMAIN, "Starting tag [%s]", tagName);

  /* store for later, do nothing more here */
  HtmlCtx_SetCurrentTagName(ctx, tagName);
  GWEN_DB_Group_free(xctx->dbCurrentAttribs);
  xctx->dbCurrentAttribs=GWEN_DB_Group_new("attribs");

  return 0;
}



int HtmlCtx_EndTag(GWEN_XML_CONTEXT *ctx, int closing)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (closing) {
    /* just ignore empty tags which are closed immediately */
    DBG_DEBUG(GWEN_LOGDOMAIN, "Closing empty tag [%s]",
              (xctx->currentTagName)?xctx->currentTagName:"<noname>");
    return 0;
  }

  if (xctx->currentTagName==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No tag name, malformed HTML data");
    return GWEN_ERROR_BAD_DATA;
  }

  DBG_DEBUG(GWEN_LOGDOMAIN, "Completed tag [%s]", xctx->currentTagName);

  if (xctx->currentGroup) {
    if (*(xctx->currentTagName)=='/') {
      int rv;

      /* it is a closing tag, call EndTagFn */
      DBG_DEBUG(GWEN_LOGDOMAIN,
                "Calling %s->EndTag(%s)",
                HtmlGroup_GetGroupName(xctx->currentGroup),
                xctx->currentTagName);
      rv=HtmlGroup_EndTag(xctx->currentGroup, xctx->currentTagName+1);
      if (rv<0) {
        if (rv!=GWEN_ERROR_NOT_IMPLEMENTED) {
          DBG_INFO(GWEN_LOGDOMAIN,
                   "Error in EndTag(%s) for [%s]",
                   HtmlGroup_GetGroupName(xctx->currentGroup),
                   xctx->currentTagName);
          return rv;
        }
      }
      else if (rv==1) {
        HTML_GROUP *g;
        HTML_GROUP *gParent;

        /* pop current group from stack */
        g=xctx->currentGroup;
        gParent=HtmlGroup_GetParent(g);
        xctx->currentGroup=gParent;
        if (gParent) {
          DBG_DEBUG(GWEN_LOGDOMAIN,
                    "Calling %s->EndSubGroup(%s)",
                    HtmlGroup_GetGroupName(gParent),
                    HtmlGroup_GetGroupName(g));
          HtmlGroup_EndSubGroup(gParent, g);
        }
        HtmlGroup_free(g);
        GWEN_XmlCtx_DecDepth(ctx);
      }
    }
    else {
      int rv;

      /* it is an opening tag, call StartTagFn */
      DBG_DEBUG(GWEN_LOGDOMAIN,
                "Calling %s->StartTag(%s)",
                HtmlGroup_GetGroupName(xctx->currentGroup),
                xctx->currentTagName);
      rv=HtmlGroup_StartTag(xctx->currentGroup, xctx->currentTagName);
      if (rv<0) {
        if (rv!=GWEN_ERROR_NOT_IMPLEMENTED) {
          DBG_INFO(GWEN_LOGDOMAIN,
                   "Error in StartTag(%s) for [%s]",
                   HtmlGroup_GetGroupName(xctx->currentGroup),
                   xctx->currentTagName);
          return rv;
        }
      }
    }
  }

  return 0;
}






int HtmlCtx_AddData(GWEN_XML_CONTEXT *ctx, const char *data)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->currentGroup) {
    int rv;

    DBG_DEBUG(GWEN_LOGDOMAIN,
              "Calling %s->AddData()",
              HtmlGroup_GetGroupName(xctx->currentGroup));
    rv=HtmlGroup_AddData(xctx->currentGroup, data);
    if (rv<0) {
      if (rv!=GWEN_ERROR_NOT_IMPLEMENTED) {
        DBG_INFO(GWEN_LOGDOMAIN,
                 "Error in AddData(%s)",
                 HtmlGroup_GetGroupName(xctx->currentGroup));
        return rv;
      }
    }
  }

  return 0;
}



int HtmlCtx_AddComment(GWEN_XML_CONTEXT *ctx, GWEN_UNUSED const char *data)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  /* ignore comments */
  return 0;
}



int HtmlCtx_AddAttr(GWEN_XML_CONTEXT *ctx,
                    const char *attrName,
                    const char *attrData)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->currentGroup) {
    assert(xctx->dbCurrentAttribs);
    GWEN_DB_SetCharValue(xctx->dbCurrentAttribs,
                         GWEN_DB_FLAGS_DEFAULT,
                         attrName, attrData);
  }

  return 0;
}



int HtmlCtx_GetTextWidth(GWEN_XML_CONTEXT *ctx,
                         HTML_FONT *fnt,
                         const char *s)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->getTextWidthFn)
    return xctx->getTextWidthFn(ctx, fnt, s);
  else
    return -1;
}



int HtmlCtx_GetTextHeight(GWEN_XML_CONTEXT *ctx,
                          HTML_FONT *fnt,
                          const char *s)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->getTextHeightFn)
    return xctx->getTextHeightFn(ctx, fnt, s);
  else
    return -1;
}



uint32_t HtmlCtx_GetColorFromName(const GWEN_XML_CONTEXT *ctx,
                                  const char *name)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->getColorFromNameFn)
    return xctx->getColorFromNameFn(ctx, name);
  else
    return 0xffffffff;
}



HTML_FONT *HtmlCtx_GetFont(GWEN_XML_CONTEXT *ctx,
                           const char *fontName,
                           int fontSize,
                           uint32_t fontFlags)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->getFontFn)
    return xctx->getFontFn(ctx, fontName, fontSize, fontFlags);
  else
    return NULL;
}



HTML_IMAGE *HtmlCtx_GetImage(GWEN_XML_CONTEXT *ctx, const char *imageName)
{
  HTML_XMLCTX *xctx;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  if (xctx->getImageFn)
    return xctx->getImageFn(ctx, imageName);
  else
    return NULL;
}



HTMLCTX_GET_TEXT_WIDTH_FN HtmlCtx_SetGetTextWidthFn(GWEN_XML_CONTEXT *ctx,
                                                    HTMLCTX_GET_TEXT_WIDTH_FN fn)
{
  HTML_XMLCTX *xctx;
  HTMLCTX_GET_TEXT_WIDTH_FN of;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  of=xctx->getTextWidthFn;
  xctx->getTextWidthFn=fn;

  return of;
}



HTMLCTX_GET_TEXT_HEIGHT_FN HtmlCtx_SetGetTextHeightFn(GWEN_XML_CONTEXT *ctx,
                                                      HTMLCTX_GET_TEXT_HEIGHT_FN fn)
{
  HTML_XMLCTX *xctx;
  HTMLCTX_GET_TEXT_HEIGHT_FN of;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  of=xctx->getTextHeightFn;
  xctx->getTextHeightFn=fn;

  return of;
}



HTMLCTX_GET_COLOR_FROM_NAME_FN HtmlCtx_SetGetColorFromNameFn(GWEN_XML_CONTEXT *ctx,
                                                             HTMLCTX_GET_COLOR_FROM_NAME_FN fn)
{
  HTML_XMLCTX *xctx;
  HTMLCTX_GET_COLOR_FROM_NAME_FN of;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  of=xctx->getColorFromNameFn;
  xctx->getColorFromNameFn=fn;

  return of;
}



HTMLCTX_GET_FONT_FN HtmlCtx_SetGetFontFn(GWEN_XML_CONTEXT *ctx, HTMLCTX_GET_FONT_FN fn)
{
  HTML_XMLCTX *xctx;
  HTMLCTX_GET_FONT_FN of;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  of=xctx->getFontFn;
  xctx->getFontFn=fn;

  return of;
}



HTMLCTX_GET_IMAGE_FN HtmlCtx_SetGetImageFn(GWEN_XML_CONTEXT *ctx, HTMLCTX_GET_IMAGE_FN fn)
{
  HTML_XMLCTX *xctx;
  HTMLCTX_GET_IMAGE_FN of;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  of=xctx->getImageFn;
  xctx->getImageFn=fn;

  return of;
}



int HtmlCtx_Layout(GWEN_XML_CONTEXT *ctx, int width, int height)
{
  HTML_XMLCTX *xctx;
  HTML_OBJECT *o;
  int rv;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  o=HtmlObject_Tree_GetFirst(xctx->objects);
  if (o==NULL) {
    DBG_ERROR(GWEN_LOGDOMAIN, "No objects");
    return 0;
  }

  HtmlObject_SetWidth(o, width);
  HtmlObject_SetHeight(o, height);
  rv=HtmlObject_Layout(o);
  if (rv<0) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  return 0;
}



void HtmlCtx_SetText(GWEN_XML_CONTEXT *ctx, const char *s)
{
  HTML_XMLCTX *xctx;
  int rv;
  HTML_GROUP *g;
  HTML_OBJECT *o;

  assert(ctx);
  xctx=GWEN_INHERIT_GETDATA(GWEN_XML_CONTEXT, HTML_XMLCTX, ctx);
  assert(xctx);

  /* reset */
  HtmlObject_Tree_Clear(xctx->objects);

  /* create initial group */
  g=HtmlGroup_Box_new("HTML_ROOT", NULL, ctx);
  assert(g);

  HtmlGroup_SetProperties(g, xctx->standardProps);
  o=HtmlObject_Box_new(ctx);
  HtmlObject_SetProperties(o, xctx->standardProps);
  HtmlGroup_SetObject(g, o);
  HtmlObject_Tree_Add(xctx->objects, o);
  HtmlCtx_SetCurrentGroup(ctx, g);

  if (s && *s) {
    rv=GWEN_XMLContext_ReadFromString(ctx, s);
    if (rv<0) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    }
  }

#if 0
  fprintf(stderr, "============ Objects:\n");
  fprintf(stderr, "String: [%s]\n", s);
  o=HtmlObject_Tree_GetFirst(xctx->objects);
  while (o) {
    const char *xx;

    xx=HtmlObject_GetText(o);
    fprintf(stderr, "Object(%d): [%s]\n",
            HtmlObject_GetObjectType(o),
            xx?xx:"(NULL)");
    o=HtmlObject_Tree_GetBelow(o);
  }
#endif
}



int HtmlCtx_GetWidth(const GWEN_XML_CONTEXT *ctx)
{
  HTML_OBJECT *o;

  o=HtmlCtx_GetRootObject(ctx);
  if (o)
    return HtmlObject_GetWidth(o);
  else
    return -1;
}



int HtmlCtx_GetHeight(const GWEN_XML_CONTEXT *ctx)
{
  HTML_OBJECT *o;

  o=HtmlCtx_GetRootObject(ctx);
  if (o)
    return HtmlObject_GetHeight(o);
  else
    return -1;
}


