summaryrefslogtreecommitdiffstats
path: root/tkhtml1/generic/htmldraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkhtml1/generic/htmldraw.c')
-rw-r--r--tkhtml1/generic/htmldraw.c877
1 files changed, 877 insertions, 0 deletions
diff --git a/tkhtml1/generic/htmldraw.c b/tkhtml1/generic/htmldraw.c
new file mode 100644
index 0000000..42f2f80
--- /dev/null
+++ b/tkhtml1/generic/htmldraw.c
@@ -0,0 +1,877 @@
+/*
+** Routines used to render HTML onto the screen for the Tk HTML widget.
+**
+** Copyright (C) 1997-2000 D. Richard Hipp
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This library is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** Library General Public License for more details.
+**
+** You should have received a copy of the GNU Library General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@acm.org
+** http://www.hwaci.com/drh/
+*/
+#include <tk.h>
+#include <string.h>
+#include <stdlib.h>
+#include "htmldraw.h"
+/*
+#ifdef USE_TK_STUBS
+# include <tkIntXlibDecls.h>
+#endif
+*/
+#define USE_TK_DRAWCHARS 1
+
+/*
+** Allocate a new HtmlBlock structure.
+*/
+static HtmlBlock *AllocBlock(void){
+ HtmlBlock *pNew;
+
+ pNew = HtmlAlloc( sizeof(HtmlBlock) );
+ if( pNew ){
+ memset(pNew, 0, sizeof(*pNew));
+ pNew->base.type = Html_Block;
+ }
+ return pNew;
+}
+
+/*
+** Free an HtmlBlock structure. Assume that it is already unlinked
+** from the element list and the block list.
+*/
+static void FreeBlock(HtmlBlock *pBlock){
+ if( pBlock ){
+ if( pBlock->z ){
+ HtmlFree(pBlock->z);
+ }
+ HtmlFree(pBlock);
+ }
+}
+
+/*
+** Destroy the given Block after first unlinking it from the
+** element list. Note that this unlinks the block from the
+** element list only -- not from the block list.
+*/
+static void UnlinkAndFreeBlock(HtmlWidget *htmlPtr, HtmlBlock *pBlock){
+ if( pBlock->base.pNext ){
+ pBlock->base.pNext->base.pPrev = pBlock->base.pPrev;
+ TestPoint(0);
+ }else{
+ htmlPtr->pLast = pBlock->base.pPrev;
+ TestPoint(0);
+ }
+ if( pBlock->base.pPrev ){
+ pBlock->base.pPrev->base.pNext = pBlock->base.pNext;
+ TestPoint(0);
+ }else{
+ htmlPtr->pFirst = pBlock->base.pNext;
+ TestPoint(0);
+ }
+ pBlock->base.pPrev = pBlock->base.pNext = 0;
+ FreeBlock(pBlock);
+}
+
+/*
+** Append a block to the block list and insert the block into the
+** element list immediately prior to the element given.
+*/
+static void AppendBlock(
+ HtmlWidget *htmlPtr, /* The HTML widget */
+ HtmlElement *pToken, /* The token that comes after pBlock */
+ HtmlBlock *pBlock /* The block to be appended */
+){
+ pBlock->base.pPrev = pToken->base.pPrev;
+ pBlock->base.pNext = pToken;
+ pBlock->pPrev = htmlPtr->lastBlock;
+ pBlock->pNext = 0;
+ if( htmlPtr->lastBlock ){
+ htmlPtr->lastBlock->pNext = pBlock;
+ TestPoint(0);
+ }else{
+ htmlPtr->firstBlock = pBlock;
+ TestPoint(0);
+ }
+ htmlPtr->lastBlock = pBlock;
+ if( pToken->base.pPrev ){
+ pToken->base.pPrev->base.pNext = (HtmlElement*)pBlock;
+ TestPoint(0);
+ }else{
+ htmlPtr->pFirst = (HtmlElement*)pBlock;
+ TestPoint(0);
+ }
+ pToken->base.pPrev = (HtmlElement*)pBlock;
+}
+
+/*
+** Print an ordered list index into the given buffer. Use numbering
+** like this:
+**
+** A B C ... Y Z AA BB CC ... ZZ
+**
+** Revert to decimal for indices greater than 52.
+*/
+static void GetLetterIndex(char *zBuf, int index, int isUpper){
+ int seed;
+ if( index<1 || index>52 ){
+ sprintf(zBuf,"%d",index);
+ TestPoint(0);
+ return;
+ }
+ if( isUpper ){
+ seed = 'A';
+ TestPoint(0);
+ }else{
+ seed = 'a';
+ TestPoint(0);
+ }
+ index--;
+ if( index<26 ){
+ zBuf[0] = seed + index;
+ zBuf[1] = 0;
+ TestPoint(0);
+ }else{
+ index -= 26;
+ zBuf[0] = seed + index;
+ zBuf[1] = seed + index;
+ zBuf[2] = 0;
+ TestPoint(0);
+ }
+ strcat(zBuf,".");
+}
+
+/*
+** Print an ordered list index into the given buffer. Use roman
+** numerals. For indices greater than a few thousand, revert to
+** decimal.
+*/
+static void GetRomanIndex(char *zBuf, int index, int isUpper){
+ int i = 0;
+ int j;
+ static struct {
+ int value;
+ char *name;
+ } values[] = {
+ { 1000, "m" },
+ { 999, "im" },
+ { 990, "xm" },
+ { 900, "cm" },
+ { 500, "d" },
+ { 499, "id" },
+ { 490, "xd" },
+ { 400, "cd" },
+ { 100, "c" },
+ { 99, "ic" },
+ { 90, "xc" },
+ { 50, "l" },
+ { 49, "il" },
+ { 40, "xl" },
+ { 10, "x" },
+ { 9, "ix" },
+ { 5, "v" },
+ { 4, "iv" },
+ { 1, "i" },
+ };
+ if( index<1 || index>=5000 ){
+ sprintf(zBuf,"%d",index);
+ TestPoint(0);
+ return;
+ }
+ for(j=0; index>0 && j<sizeof(values)/sizeof(values[0]); j++){
+ int k;
+ while( index >= values[j].value ){
+ for(k=0; values[j].name[k]; k++){
+ zBuf[i++] = values[j].name[k];
+ TestPoint(0);
+ }
+ index -= values[j].value;
+ TestPoint(0);
+ }
+ }
+ zBuf[i] = 0;
+ if( isUpper ){
+ for(i=0; zBuf[i]; i++){
+ zBuf[i] += 'A' - 'a';
+ TestPoint(0);
+ }
+ }else{
+ TestPoint(0);
+ }
+ strcat(zBuf,".");
+}
+
+/* Draw the selection background for the given block
+*/
+static void DrawSelectionBackground(
+ HtmlWidget *htmlPtr, /* The HTML widget */
+ HtmlBlock *pBlock, /* The block whose background is drawn */
+ Drawable drawable, /* Draw the background on this drawable */
+ int x, int y /* Virtual coords of top-left of drawable */
+){
+ int xLeft, xRight; /* Left and right bounds of box to draw */
+ int yTop, yBottom; /* Top and bottom of box */
+ HtmlElement *p = 0; /* First element of the block */
+ Tk_Font font; /* Font */
+ GC gc; /* GC for drawing */
+ XRectangle xrec; /* Size of a filled rectangle to be drawn */
+
+ if( pBlock==0 || (pBlock->base.flags & HTML_Selected)==0 ){
+ TestPoint(0);
+ return;
+ }
+ xLeft = pBlock->left - x;
+ if( pBlock==htmlPtr->pSelStartBlock && htmlPtr->selStartIndex>0 ){
+ if( htmlPtr->selStartIndex >= pBlock->n ){ TestPoint(0); return; }
+ p = pBlock->base.pNext;
+ font = HtmlGetFont(htmlPtr, p->base.style.font);
+ if( font==0 ) return;
+ if( p->base.type==Html_Text ){
+ xLeft = p->text.x - x + Tk_TextWidth(font, pBlock->z,
+ htmlPtr->selStartIndex);
+ }
+ }
+ xRight = pBlock->right - x;
+ if( pBlock==htmlPtr->pSelEndBlock && htmlPtr->selEndIndex<pBlock->n ){
+ if( p==0 ){
+ p = pBlock->base.pNext;
+ font = HtmlGetFont(htmlPtr, p->base.style.font);
+ if( font==0 ) return;
+ }
+ if( p->base.type==Html_Text ){
+ xRight = p->text.x - x + Tk_TextWidth(font, pBlock->z,
+ htmlPtr->selEndIndex);
+ }
+ }
+ yTop = pBlock->top - y;
+ yBottom = pBlock->bottom - y;
+ gc = HtmlGetGC(htmlPtr, COLOR_Selection, FONT_Any);
+ xrec.x = xLeft;
+ xrec.y = yTop;
+ xrec.width = xRight - xLeft;
+ xrec.height = yBottom - yTop;
+ XFillRectangles(htmlPtr->display, drawable, gc, &xrec, 1);
+}
+
+/*
+** Draw a rectangle. The rectangle will have a 3-D appearance if
+** flat==0 and a flat appearance if flat==1.
+**
+** We don't use Tk_Draw3DRectangle() because it doesn't work well
+** when the background color is close to pure white or pure black.
+*/
+static void HtmlDrawRect(
+ HtmlWidget *htmlPtr, /* The HTML widget */
+ Drawable drawable, /* Draw it here */
+ HtmlElement *src, /* Element associated with drawing */
+ int x, int y, int w, int h, /* Coordinates of the rectangle */
+ int depth, /* Width of the relief, or the flat line */
+ int relief /* The relief. TK_RELIEF_FLAT omits 3d */
+){
+ if( depth>0 ){
+ int i;
+ GC gcLight, gcDark;
+ XRectangle xrec[1];
+ if( relief!=TK_RELIEF_FLAT ){
+ int iLight, iDark;
+ iLight = HtmlGetLightShadowColor(htmlPtr, src->base.style.bgcolor);
+ gcLight = HtmlGetGC(htmlPtr, iLight, FONT_Any);
+ iDark = HtmlGetDarkShadowColor(htmlPtr, src->base.style.bgcolor);
+ gcDark = HtmlGetGC(htmlPtr, iDark, FONT_Any);
+ if( relief==TK_RELIEF_SUNKEN ){
+ GC gcTemp = gcLight;
+ gcLight = gcDark;
+ gcDark = gcTemp;
+ }
+ }else{
+ gcLight = HtmlGetGC(htmlPtr, src->base.style.color, FONT_Any);
+ gcDark = gcLight;
+ }
+ xrec[0].x = x;
+ xrec[0].y = y;
+ xrec[0].width = depth;
+ xrec[0].height = h;
+ XFillRectangles(htmlPtr->display, drawable, gcLight, xrec, 1);
+ xrec[0].x = x+w-depth;
+ XFillRectangles(htmlPtr->display, drawable, gcDark, xrec, 1);
+ for(i=0; i<depth && i<h/2; i++){
+ XDrawLine(htmlPtr->display, drawable, gcLight, x+i, y+i, x+w-i-1, y+i);
+ XDrawLine(htmlPtr->display, drawable, gcDark, x+i, y+h-i-1,
+ x+w-i-1, y+h-i-1);
+ }
+ }
+ if( h>depth*2 && w>depth*2 ){
+ GC gcBg;
+ XRectangle xrec[1];
+ gcBg = HtmlGetGC(htmlPtr, src->base.style.bgcolor, FONT_Any);
+ xrec[0].x = x + depth;
+ xrec[0].y = y + depth;
+ xrec[0].width = w - depth*2;
+ xrec[0].height = h - depth*2;
+ XFillRectangles(htmlPtr->display, drawable, gcBg, xrec, 1);
+ }
+}
+
+/*
+** Display a single HtmlBlock. This is where all the drawing
+** happens.
+*/
+void HtmlBlockDraw(
+ HtmlWidget *htmlPtr, /* The main HTML widget */
+ HtmlBlock *pBlock, /* Block which needs to be drawn */
+ Drawable drawable, /* Draw the line on this */
+ int drawableLeft, /* Virtual coordinate of left edge of drawable */
+ int drawableTop, /* Virtual coordinate of top edge of drawable */
+ int drawableWidth, /* Width of the drawable */
+ int drawableHeight /* Height of the drawable */
+){
+ Tk_Font font; /* Font to use to render text */
+ GC gc; /* A graphics context */
+ HtmlElement *src; /* HtmlElement holding style information */
+ HtmlElement *pTable; /* The table (when drawing part of a table) */
+ int x, y; /* Where to draw */
+
+ if( pBlock==0 ){ TestPoint(0); return; }
+ src = pBlock->base.pNext;
+ while( src && (src->base.flags & HTML_Visible)==0 ){
+ src = src->base.pNext;
+ TestPoint(0);
+ }
+ if( src==0 ){ TestPoint(0); return; }
+ if( pBlock->n>0 ){
+ /* We must be dealing with plain old text */
+ if( src->base.type==Html_Text ){
+ x = src->text.x;
+ y = src->text.y;
+ TestPoint(0);
+ }else{
+ CANT_HAPPEN;
+ return;
+ }
+ if( pBlock->base.flags & HTML_Selected ){
+ HtmlLock(htmlPtr);
+ DrawSelectionBackground(htmlPtr, pBlock, drawable,
+ drawableLeft, drawableTop);
+ if( HtmlUnlock(htmlPtr) ) return;
+ }
+ gc = HtmlGetGC(htmlPtr, src->base.style.color, src->base.style.font);
+ font = HtmlGetFont(htmlPtr, src->base.style.font);
+ if( font==0 ) return;
+ Tk_DrawChars(htmlPtr->display,
+ drawable,
+ gc, font,
+ pBlock->z, pBlock->n,
+ x - drawableLeft, y - drawableTop);
+ if( src->base.style.flags & STY_Underline ){
+ Tk_UnderlineChars(htmlPtr->display, drawable, gc, font, pBlock->z,
+ x - drawableLeft, y-drawableTop, 0, pBlock->n);
+ }
+ if( src->base.style.flags & STY_StrikeThru ){
+ XRectangle xrec;
+ xrec.x = pBlock->left - drawableLeft;
+ xrec.y = (pBlock->top + pBlock->bottom)/2 - drawableTop;
+ xrec.width = pBlock->right - pBlock->left;
+ xrec.height = 1 + (pBlock->bottom - pBlock->top > 15);
+ XFillRectangles(htmlPtr->display, drawable, gc, &xrec, 1);
+ }
+ if( pBlock==htmlPtr->pInsBlock && htmlPtr->insStatus>0 ){
+ int x;
+ XRectangle xrec;
+ if( htmlPtr->insIndex < pBlock->n ){
+ x = src->text.x - drawableLeft;
+ x += Tk_TextWidth(font, pBlock->z, htmlPtr->insIndex);
+ }else{
+ x = pBlock->right - drawableLeft;
+ }
+ if( x>0 ){ TestPoint(0); x--; }
+ xrec.x = x;
+ xrec.y = pBlock->top - drawableTop;
+ xrec.width = 2;
+ xrec.height = pBlock->bottom - pBlock->top;
+ XFillRectangles(htmlPtr->display, drawable, gc, &xrec, 1);
+ }
+ }else{
+ /* We are dealing with a single HtmlElement which contains something
+ ** other than plain text. */
+ int top=0;
+ int btm=0;
+ int cntr;
+ int cnt, w;
+ char zBuf[30];
+ switch( src->base.type ){
+ case Html_LI:
+ x = src->li.x;
+ y = src->li.y;
+ cntr = (top+btm)/2;
+ switch( src->li.type ){
+ case LI_TYPE_Enum_1:
+ sprintf(zBuf,"%d.",src->li.cnt);
+ TestPoint(0);
+ break;
+ case LI_TYPE_Enum_A:
+ GetLetterIndex(zBuf,src->li.cnt,1);
+ TestPoint(0);
+ break;
+ case LI_TYPE_Enum_a:
+ GetLetterIndex(zBuf,src->li.cnt,0);
+ TestPoint(0);
+ break;
+ case LI_TYPE_Enum_I:
+ GetRomanIndex(zBuf,src->li.cnt,1);
+ TestPoint(0);
+ break;
+ case LI_TYPE_Enum_i:
+ GetRomanIndex(zBuf,src->li.cnt,0);
+ TestPoint(0);
+ break;
+ default:
+ zBuf[0] = 0;
+ TestPoint(0);
+ break;
+ }
+ gc = HtmlGetGC(htmlPtr, src->base.style.color, src->base.style.font);
+ switch( src->li.type ){
+ case LI_TYPE_Undefined:
+ case LI_TYPE_Bullet1:
+ XFillArc(htmlPtr->display,
+ drawable,
+ gc,
+ x - 7 - drawableLeft, y - 8 - drawableTop, 7, 7,
+ 0, 360*64);
+ TestPoint(0);
+ break;
+
+ case LI_TYPE_Bullet2:
+ XDrawArc(htmlPtr->display,
+ drawable,
+ gc,
+ x - 7 - drawableLeft, y - 8 - drawableTop, 7, 7,
+ 0, 360*64);
+ TestPoint(0);
+ break;
+
+ case LI_TYPE_Bullet3:
+ XDrawRectangle(htmlPtr->display,
+ drawable,
+ gc,
+ x - 7 - drawableLeft, y - 8 - drawableTop, 7, 7);
+ TestPoint(0);
+ break;
+
+ case LI_TYPE_Enum_1:
+ case LI_TYPE_Enum_A:
+ case LI_TYPE_Enum_a:
+ case LI_TYPE_Enum_I:
+ case LI_TYPE_Enum_i:
+ cnt = strlen(zBuf);
+ font = HtmlGetFont(htmlPtr, src->base.style.font);
+ if( font==0 ) return;
+ w = Tk_TextWidth(font, zBuf, cnt);
+ Tk_DrawChars(htmlPtr->display,
+ drawable,
+ gc, font,
+ zBuf, cnt,
+ x - w - drawableLeft, y - drawableTop);
+ TestPoint(0);
+ break;
+ }
+ break;
+ case Html_HR: {
+ int relief = htmlPtr->ruleRelief;
+ switch( relief ){
+ case TK_RELIEF_RAISED:
+ case TK_RELIEF_SUNKEN:
+ break;
+ default:
+ relief = TK_RELIEF_FLAT;
+ break;
+ }
+ HtmlDrawRect(htmlPtr, drawable, src,
+ src->hr.x - drawableLeft,
+ src->hr.y - drawableTop,
+ src->hr.w,
+ src->hr.h,
+ 1, relief);
+ break;
+ }
+ case Html_TABLE: {
+ int relief = htmlPtr->tableRelief;
+ switch( relief ){
+ case TK_RELIEF_RAISED:
+ case TK_RELIEF_SUNKEN:
+ break;
+ default:
+ relief = TK_RELIEF_FLAT;
+ break;
+ }
+ HtmlDrawRect(htmlPtr, drawable, src,
+ src->table.x - drawableLeft,
+ src->table.y - drawableTop,
+ src->table.w,
+ src->table.h,
+ src->table.borderWidth,
+ relief);
+ break;
+ }
+ case Html_TH:
+ case Html_TD: {
+ int depth, relief;
+ pTable = src->cell.pTable;
+ depth = pTable && pTable->table.borderWidth>0;
+ switch( htmlPtr->tableRelief ){
+ case TK_RELIEF_RAISED: relief = TK_RELIEF_SUNKEN; break;
+ case TK_RELIEF_SUNKEN: relief = TK_RELIEF_RAISED; break;
+ default: relief = TK_RELIEF_FLAT; break;
+ }
+ HtmlDrawRect(htmlPtr, drawable, src,
+ src->cell.x - drawableLeft,
+ src->cell.y - drawableTop,
+ src->cell.w,
+ src->cell.h,
+ depth,
+ relief);
+ break;
+ }
+ case Html_IMG:
+ if( src->image.pImage ){
+ HtmlDrawImage(src, drawable, drawableLeft, drawableTop,
+ drawableLeft + drawableWidth,
+ drawableTop + drawableHeight);
+ }else if( src->image.zAlt ){
+ gc = HtmlGetGC(htmlPtr, src->base.style.color, src->base.style.font);
+ font = HtmlGetFont(htmlPtr, src->base.style.font);
+ if( font==0 ) return;
+ Tk_DrawChars(htmlPtr->display,
+ drawable,
+ gc, font,
+ src->image.zAlt, strlen(src->image.zAlt),
+ src->image.x - drawableLeft,
+ src->image.y - drawableTop);
+ TestPoint(0);
+ }
+ break;
+ default:
+ TestPoint(0);
+ break;
+ }
+ }
+}
+
+/*
+** Draw all or part of an image.
+*/
+void HtmlDrawImage(
+ HtmlElement *pElem, /* The <IMG> to be drawn */
+ Drawable drawable, /* Draw it here */
+ int drawableLeft, /* left edge of the drawable */
+ int drawableTop, /* Virtual canvas coordinate for top of drawable */
+ int drawableRight, /* right edge of the drawable */
+ int drawableBottom /* bottom edge of the drawable */
+){
+ int imageTop; /* virtual canvas coordinate for top of image */
+ int x, y; /* where to place image on the drawable */
+ int imageX, imageY; /* \__ Subset of image that fits */
+ int imageW, imageH; /* / on the drawable */
+
+ imageTop = pElem->image.y - pElem->image.ascent;
+ y = imageTop - drawableTop;
+ if( imageTop + pElem->image.h > drawableBottom ){
+ imageH = drawableBottom - imageTop;
+ TestPoint(0);
+ }else{
+ imageH = pElem->image.h;
+ TestPoint(0);
+ }
+ if( y<0 ){
+ imageY = -y;
+ imageH += y;
+ y = 0;
+ TestPoint(0);
+ }else{
+ imageY = 0;
+ TestPoint(0);
+ }
+ x = pElem->image.x - drawableLeft;
+ if( pElem->image.x + pElem->image.w > drawableRight ){
+ imageW = drawableRight - pElem->image.x;
+ TestPoint(0);
+ }else{
+ imageW = pElem->image.w;
+ TestPoint(0);
+ }
+ if( x<0 ){
+ imageX = -x;
+ imageW += x;
+ x = 0;
+ TestPoint(0);
+ }else{
+ imageX = 0;
+ TestPoint(0);
+ }
+ Tk_RedrawImage(pElem->image.pImage->image, imageX, imageY, imageW, imageH,
+ drawable, x, y);
+ pElem->image.redrawNeeded = 0;
+}
+
+/*
+** Recompute the following fields of the given block structure:
+**
+** base.count The number of elements described by this
+** block structure.
+**
+** n The number of characters of text output
+** associated with this block. If the block
+** renders something other than text (ex: <IMG>)
+** then set n to 0.
+**
+** z Pointer to malloced memory containing the
+** text associated with this block. NULL if
+** n is 0.
+**
+** Return a pointer to the first HtmlElement not covered by the
+** block.
+*/
+static HtmlElement *FillOutBlock(HtmlWidget *htmlPtr, HtmlBlock *p){
+ HtmlElement *pElem;
+ int go;
+ int n;
+ int x, y;
+ int i;
+ HtmlStyle style;
+ int firstSelected; /* First selected character in this block */
+ int lastSelected; /* Last selected character in this block */
+ char zBuf[400];
+
+ /*
+ ** Reset n and z
+ */
+ if( p->n ){
+ p->n = 0;
+ }
+ if( p->z ){
+ HtmlFree(p->z);
+ }
+ firstSelected = 1000000;
+ lastSelected = -1;
+
+ /*
+ ** Skip over HtmlElements that aren't directly displayed.
+ */
+ pElem = p->base.pNext;
+ p->base.count = 0;
+ while( pElem && (pElem->base.flags & HTML_Visible)==0 ){
+ HtmlElement *pNext = pElem->pNext;
+ if( pElem->base.type==Html_Block ){
+ UnlinkAndFreeBlock(htmlPtr, &pElem->block);
+ TestPoint(0);
+ }else{
+ p->base.count++;
+ TestPoint(0);
+ }
+ pElem = pNext;
+ }
+ if( pElem==0 ){ TestPoint(0); return 0; }
+
+ /*
+ ** Handle "special" elements.
+ */
+ if( pElem->base.type!=Html_Text ){
+ switch( pElem->base.type ){
+ case Html_HR:
+ p->top = pElem->hr.y - pElem->hr.h;
+ p->bottom = pElem->hr.y;
+ p->left = pElem->hr.x;
+ p->right = pElem->hr.x + pElem->hr.w;
+ TestPoint(0);
+ break;
+ case Html_LI:
+ p->top = pElem->li.y - pElem->li.ascent;
+ p->bottom = pElem->li.y + pElem->li.descent;
+ p->left = pElem->li.x - 10;
+ p->right = pElem->li.x + 10;
+ TestPoint(0);
+ break;
+ case Html_TD:
+ case Html_TH:
+ p->top = pElem->cell.y;
+ p->bottom = pElem->cell.y + pElem->cell.h;
+ p->left = pElem->cell.x;
+ p->right = pElem->cell.x + pElem->cell.w;
+ TestPoint(0);
+ break;
+ case Html_TABLE:
+ p->top = pElem->table.y;
+ p->bottom = pElem->table.y + pElem->table.h;
+ p->left = pElem->table.x;
+ p->right = pElem->table.x + pElem->table.w;
+ TestPoint(0);
+ break;
+ case Html_IMG:
+ p->top = pElem->image.y - pElem->image.ascent;
+ p->bottom = pElem->image.y + pElem->image.descent;
+ p->left = pElem->image.x;
+ p->right = pElem->image.x + pElem->image.w;
+ TestPoint(0);
+ break;
+ }
+ p->base.count++;
+ TestPoint(0);
+ return pElem->pNext;
+ }
+
+ /*
+ ** If we get this far, we must be dealing with text.
+ */
+ n = 0;
+ x = pElem->text.x;
+ y = pElem->text.y;
+ p->top = y - pElem->text.ascent;
+ p->bottom = y + pElem->text.descent;
+ p->left = x;
+ style = pElem->base.style;
+ go = 1;
+ while( pElem ){
+ HtmlElement *pNext = pElem->pNext;
+ switch( pElem->base.type ){
+ case Html_Text:
+ if( pElem->base.style.flags & STY_Invisible ){
+ TestPoint(0);
+ break;
+ }
+ if( pElem->text.spaceWidth <=0 ){
+ CANT_HAPPEN;
+ break;
+ }
+ if( y != pElem->text.y
+ || style.font != pElem->base.style.font
+ || style.color != pElem->base.style.color
+ || (style.flags & STY_FontMask)
+ != (pElem->base.style.flags & STY_FontMask)
+ ){
+ go = 0;
+ TestPoint(0);
+ }else{
+ int sw = pElem->text.spaceWidth;
+ int nSpace = (pElem->text.x - x) / sw;
+ if( nSpace * sw + x != pElem->text.x ){
+ go = 0;
+ TestPoint(0);
+ }else if( n + nSpace + pElem->base.count >= sizeof(zBuf) ){
+ go = 0;
+ TestPoint(0);
+ }else{
+ for(i=0; i<nSpace; i++){
+ zBuf[n++] = ' ';
+ TestPoint(0);
+ }
+ strcpy(&zBuf[n], pElem->text.zText);
+ n += pElem->base.count;
+ x = pElem->text.x + pElem->text.w;
+ }
+ }
+ break;
+
+ case Html_Space:
+ if( pElem->base.style.font != style.font ){
+ pElem = pElem->pNext;
+ go = 0;
+ }else if( (style.flags & STY_Preformatted)!=0
+ && (pElem->base.flags & HTML_NewLine)!=0 ){
+ pElem = pElem->pNext;
+ go = 0;
+ }
+ break;
+
+ case Html_Block:
+ UnlinkAndFreeBlock(htmlPtr,&pElem->block);
+ break;
+
+ case Html_A:
+ case Html_EndA:
+ go = 0;
+ break;
+
+ default:
+ if( pElem->base.flags & HTML_Visible ) go = 0;
+ TestPoint(0);
+ break;
+ }
+ if( go==0 ) break;
+ p->base.count++;
+ pElem = pNext;
+ }
+ p->right = x;
+
+ while( n>0 && zBuf[n-1]==' ' ){ TestPoint(0); n--; }
+ p->z = HtmlAlloc( n );
+ strncpy(p->z, zBuf, n);
+ p->n = n;
+ return pElem;
+}
+
+/*
+** Scan ahead looking for a place to put a block. Return a pointer
+** to the element which should come immediately after the block.
+**
+** if pCnt!=0, then put the number of elements skipped in *pCnt.
+*/
+static HtmlElement *FindStartOfNextBlock(
+ HtmlWidget *htmlPtr, /* The HTML widget */
+ HtmlElement *p, /* First candid for the start of a block */
+ int *pCnt /* Write number of elements skipped here */
+){
+ int cnt = 0;
+
+ while( p && (p->base.flags & HTML_Visible)==0 ){
+ HtmlElement *pNext = p->pNext;
+ if( p->base.type==Html_Block ){
+ UnlinkAndFreeBlock(htmlPtr, &p->block);
+ }else{
+ cnt++;
+ }
+ p = pNext;
+ }
+ if( pCnt ){ *pCnt = cnt; }
+ return p;
+}
+
+
+/*
+** Add additional blocks to the block list in order to cover
+** all elements on the element list.
+**
+** If any old blocks are found on the element list, they must
+** be left over from a prior rendering. Unlink and delete them.
+*/
+void HtmlFormBlocks(HtmlWidget *htmlPtr){
+ HtmlElement *pElem;
+
+ if( htmlPtr->lastBlock ){
+ pElem = FillOutBlock(htmlPtr, htmlPtr->lastBlock);
+ }else{
+ pElem = htmlPtr->pFirst;
+ }
+ while( pElem ){
+ int cnt;
+ pElem = FindStartOfNextBlock(htmlPtr, pElem, &cnt);
+ if( pElem ){
+ HtmlBlock *pNew = AllocBlock();
+ if( htmlPtr->lastBlock ){
+ htmlPtr->lastBlock->base.count += cnt;
+ }
+ AppendBlock(htmlPtr, pElem, pNew);
+ pElem = FillOutBlock(htmlPtr, pNew);
+ }
+ }
+}