From 7948fd17eea7422d19f60845bc0633fe084bbd67 Mon Sep 17 00:00:00 2001 From: Jerome St-Louis Date: Mon, 30 Sep 2013 03:40:50 -0400 Subject: [PATCH] extras/html: (~#997) Many fixes and improvements to HTML parser and renderer --- extras/html/HTMLView.ec | 339 ++++++++++++++++++++++++++++++++++++--------- extras/html/htmlParser.ec | 346 +++++++++++++++++++++++++++++++++++----------- extras/html/lines.ec | 24 +++- extras/html/tables.ec | 19 +-- 4 files changed, 573 insertions(+), 155 deletions(-) diff --git a/extras/html/HTMLView.ec b/extras/html/HTMLView.ec index 637afa5..5571895 100644 --- a/extras/html/HTMLView.ec +++ b/extras/html/HTMLView.ec @@ -4,6 +4,7 @@ import "htmlParser" enum BlockType { + HTML, TEXT = 1, IMAGE, BR, @@ -15,7 +16,9 @@ enum BlockType TD, ANCHOR, INPUT, - FORM + FORM, + TITLE, + HEAD }; define LEFT_MARGIN = 10; @@ -31,7 +34,7 @@ define CELL_SPACING = 0; class RenderFlags { bool lineW:1,width:1,render:1,minW:1; }; -enum InputType { text, submit, radio, hidden }; +enum InputType { text, submit, checkbox, radio, hidden }; class ImageEntry : struct { @@ -125,6 +128,7 @@ class ObjectThread : Thread { HTTPFile httpFile {}; file = httpFile; + incref file; //printf("Opening URL\n"); //((GuiApplication)__thisModule).PauseNetworkEvents(); @@ -150,6 +154,7 @@ class ObjectThread : Thread char extension[MAX_EXTENSION]; entry.bitmap = Bitmap { alphaBlend = true }; entry.bitmap.LoadFromFile(file, GetExtension(path, extension), null); + incref file; } } delete file; @@ -189,8 +194,9 @@ class ObjectThread : Thread browserWindow.Update(null); // ((GuiApplication)__thisModule).UpdateDisplay(); + display.Unlock(); } - display.Unlock(); + // TRIED MOVING THIS HERE BECAUSE OF printf("bug") in GuiApplication if(window.display.current) ((GuiApplication)__thisModule).UpdateDisplay(); } @@ -290,10 +296,54 @@ static void ComputeImageSize(Block block) ComputeImageSize(child); } +// Resources Cache +Window sharedWindow +{ + visible = false; + + bool OnLoadGraphics() + { + ImageEntry entry; + FontEntry fEntry; + for(entry = imageCache.first; entry; entry = entry.next) + { + entry.bitmap.MakeDD(displaySystem); + if(entry.bitmap && entry.bitmap.alphaBlend) + { + entry.bitmap.Convert(null, pixelFormat888, null); + // entry.bitmap.displaySystem = displaySystem; + } + } + for(fEntry = fontCache.first; fEntry; fEntry = fEntry.next) + { + if(!fEntry.font) + fEntry.font = displaySystem.LoadFont(fEntry.face, fEntry.size, fEntry.attribs); + } + return true; + } + + void OnUnloadGraphics() + { + ImageEntry entry; + FontEntry fontEntry; + while((entry = imageCache.first)) + { + imageCache.Remove(entry); + delete entry; + } + while((fontEntry = fontCache.first)) + { + displaySystem.UnloadFont(fontEntry.font); + fontCache.Remove(fontEntry); + delete fontEntry; + } + } +}; + class HTMLView : Window { HTMLFile html {}; - BitmapResource missing { "<:ecere>emblems/unreadable.png", window = this }; + BitmapResource missing { "<:ecere>emblems/unreadable.png", window = sharedWindow /*this*/ }; char * location; Block overLink, clickedLink; Block overBlock; @@ -423,11 +473,24 @@ class HTMLView : Window { case submit: block.window = Button { this, text = block.value, position = Point { 10, 50 }, id = (int64)block, NotifyClicked = ButtonClicked, isDefault = true }; + if(block.size) + block.window.size = { w = (int)(block.size * 8) }; + if(block.src) + { + ((Button)block.window).bitmap = { block.src }; + ((Button)block.window).bevel = false; + } eInstance_IncRef(block.window); block.window.Create(); block.window.cursor = ((GuiApplication)__thisModule).GetCursor(arrow); //if(!html.defaultButton) html.defaultButton = block.window; break; + case checkbox: + block.window = Button { this, isCheckbox = true, position = Point { 10, 100 }, id = (int64)block, NotifyClicked = ButtonClicked }; + eInstance_IncRef(block.window); + block.window.Create(); + block.window.cursor = ((GuiApplication)__thisModule).GetCursor(arrow); + break; case radio: block.window = Button { this, isRadio = true, position = Point { 10, 100 }, id = (int64)block, NotifyClicked = ButtonClicked }; eInstance_IncRef(block.window); @@ -435,7 +498,11 @@ class HTMLView : Window block.window.cursor = ((GuiApplication)__thisModule).GetCursor(arrow); break; case text: - block.window = EditBox { this, position = Point { 10, 20 }, id = (int64)block }; + block.window = EditBox { this, contents = block.value, size = { w = (int)(block.size * 8) }, position = Point { 10, 20 }, id = (int64)block }; + if(!block.size) + { + ((EditBox)block.window).size.w = block.parent.width; + } eInstance_IncRef(block.window); block.window.Create(); break; @@ -455,7 +522,7 @@ class HTMLView : Window if(block.src && (block.type == IMAGE || block.type == TD || block.type == TABLE)) { char path[MAX_LOCATION]; - ImageEntry entry; + ImageEntry entry = null; strcpy(path, location ? location : ""); if(location && path[strlen(path)-1] != '/') @@ -468,9 +535,12 @@ class HTMLView : Window else PathCat(path, block.src); - for(entry = imageCache.first; entry; entry = entry.next) - if(!strcmp(entry.src, path)) - break; + if(!strstr(path, "File://")) + { + for(entry = imageCache.first; entry; entry = entry.next) + if(!strcmp(entry.src, path)) + break; + } if(!entry) { @@ -524,6 +594,9 @@ class HTMLView : Window char extension[MAX_EXTENSION]; entry.bitmap = Bitmap { alphaBlend = true }; entry.bitmap.LoadFromFile(file, GetExtension(path, extension), null); + display.Lock(false); + entry.bitmap.MakeDD(displaySystem); + display.Unlock(); } delete file; for(bitmapPtr = entry.bitmapPtrs.first; bitmapPtr; bitmapPtr = bitmapPtr.next) @@ -534,6 +607,7 @@ class HTMLView : Window } } block.font = block.parent.font; + block.textColor = block.parent.textColor; } else if(block.type == FONT || block.type == ANCHOR) { @@ -550,50 +624,26 @@ class HTMLView : Window if(!entry) { display.Lock(false); - entry = FontEntry { /*font = displaySystem.LoadFont(block.face, block.size, block.attribs), */size = block.size, attribs = block.attribs, face = CopyString(block.face) }; + entry = FontEntry { font = displaySystem.LoadFont(block.face, block.size, block.attribs), size = block.size, attribs = block.attribs, face = CopyString(block.face) }; fontCache.Add(entry); display.Unlock(); } if(entry) - { - // ? block.font = entry; - } } else if(block.parent) - block.font = block.parent.font; - - for(child = block.subBlocks.first; child; child = child.next) - LoadGraphics(child, previous); - } - - bool OnLoadGraphics() - { - ImageEntry entry; - FontEntry fEntry; - for(entry = imageCache.first; entry; entry = entry.next) { - entry.bitmap.MakeDD(displaySystem); - if(entry.bitmap && entry.bitmap.alphaBlend) - { - entry.bitmap.Convert(null, pixelFormat888, null); - // entry.bitmap.displaySystem = displaySystem; - } + block.font = block.parent.font; + block.textColor = block.parent.textColor; } - for(fEntry = fontCache.first; fEntry; fEntry = fEntry.next) - fEntry.font = browserWindow.displaySystem.LoadFont(fEntry.face, fEntry.size, fEntry.attribs); - return true; - } - - void OnUnloadGraphics() - { - FontEntry entry; - for(entry = fontCache.first; entry; entry = entry.next) + else { - browserWindow.displaySystem.UnloadFont(entry.font); - entry.font = null; + block.textColor = black; } + + for(child = block.subBlocks.first; child; child = child.next) + LoadGraphics(child, previous); } void NormalizeSelection(Block * startBlock, int * startSel, Block * endBlock, int * endSel) @@ -646,8 +696,11 @@ class HTMLView : Window Surface surface = display.GetSurface(0,0,null); if(surface) { + Font font; if(html.defaultFont.font) surface.TextFont(html.defaultFont.font.font); + + font = surface.font; for(;block;) { Block nextBlock; @@ -656,7 +709,6 @@ class HTMLView : Window int left, right; int x, maxW; int thisLineCentered = centered; - Font font = surface.GetFont(); bool changeLine; left = LEFT_MARGIN; @@ -682,12 +734,13 @@ class HTMLView : Window maxW = right - left; newH = ComputeLine(surface, block, textPos, &nextBlock, &nextTextPos, ¢ered, &w, maxW, maxH - y, RenderFlags {}, y, &leftObjects, &rightObjects, &changeLine, true, 0 /*y*/, LEFT_MARGIN); + if(thisLineCentered) x = Max(left,(left + right - w) / 2); else x = left; - surface.TextFont(font); + surface.font = font; PositionLine(this, surface, x - scroll.x, y - scroll.y, maxW, newH, block, textPos, nextBlock, nextTextPos, @@ -707,13 +760,15 @@ class HTMLView : Window void Open(char * location, char * firstReferer) { - HTTPFile f {}; + HTTPFile f { /*reuseConnection = false*/ }; char referer[MAX_LOCATION] = ""; char relocation[MAX_LOCATION]; bool opened = false; strcpy(relocation, location); + // PrintLn("\n\n\nOpening new location: ", location, "\n"); + if(strstr(location, "http://") != location) { if(!FileExists(location)) @@ -781,9 +836,153 @@ class HTMLView : Window if(opened) { + bool isHTTP = eClass_IsDerived(f._class, class(HTTPFile)); void * previous = null; - html.Parse(f); + bool isImage = false; + bool isPlain = isHTTP ? false : true; + + // Handle known types + if(isHTTP && f.contentType) + { + if(strstr(f.contentType, "image/") == f.contentType) + isImage = true; + else if(strstr(f.contentType, "text/") == f.contentType && strnicmp(f.contentType + 5, "html", 4)) + isPlain = true; + } + else + { + const String imageExt[] = { "jpg", "jpeg", "bmp", "pcx", "png", "gif" }; + const String htmlExt[] = { "html", "htm", "php" }; + const String textExt[] = { "c", "h", "ec", "eh", "epj", "cpp", "cxx", "cc", "hpp", "hxx", "hh", "m", "java", "cs", "py", "Makefile", "mk", "cf" }; + char ext[MAX_EXTENSION]; + int i; + GetExtension(fileName, ext); + for(i = 0; i < sizeof(imageExt) / sizeof(imageExt[0]); i++) + if(!strcmpi(ext, imageExt[i])) + { + isImage = true; + break; + } + + for(i = 0; i < sizeof(htmlExt) / sizeof(htmlExt[0]); i++) + if(!strcmpi(ext, htmlExt[i])) + { + isPlain = false; + break; + } + + for(i = 0; i < sizeof(textExt) / sizeof(textExt[0]); i++) + if(!strcmpi(ext, textExt[i])) + { + isPlain = true; + break; + } + } + + if(isImage) + { + Block subBlock; + html.body = HTMLFile::AddBlock(html.block, BODY); + + subBlock = HTMLFile::AddBlock(html.body, IMAGE); + subBlock.valign = bottom; + subBlock.halign = middle; + subBlock.src = CopyString(fileName); + html.defaultFont.type = FONT; + html.defaultFont.face = CopyString("Times New Roman"); + } + else if(isPlain) + { + uint size; + TempFile tmp { }; + Block subBlock; + char * text; + int len; + String cd = eClass_IsDerived(f._class, class(HTTPFile)) ? f.contentDisposition : null; + + String tmpPath = PrintString("File://", (uintptr)tmp); + f.CopyTo(tmpPath); + size = tmp.GetSize(); + tmp.Seek(0, start); + + html.defaultFont.type = FONT; + html.defaultFont.face = CopyString("Courier New"); + + if(cd) + { + char fn[MAX_LOCATION]; + while(GetKeyWordEx(&cd, fn, sizeof(fn), true, false)) + { + if(!strcmp(fn, "filename") && GetKeyWordEx(&cd, fn, sizeof(fn), true, true)) + { + html.titleBlock = HTMLFile::AddBlock(html.block, TITLE); + subBlock = HTMLFile::AddBlock(html.titleBlock, TEXT); + subBlock.text = CopyString(fn); + subBlock.textLen = strlen(fn); + } + } + } + + html.body = HTMLFile::AddBlock(html.block, BODY); + html.block = html.body; + + text = new char[size + 1]; + len = tmp.Read(text, 1, size); + text[len] = 0; + + { + int c; + char ch; + int start = 0; + Block textBlock = HTMLFile::AddBlock(html.block, TEXT); + + for(c = 0; ; c++) + { + ch = text[c]; + if(ch == '\n' || ch == '\r' || !ch) + { + int len = c - start; + textBlock.text = renew textBlock.text char[textBlock.textLen + 1 + len]; + memmove(textBlock.text + len, textBlock.text, textBlock.textLen + 1); + memcpy(textBlock.text, text + start, len); + textBlock.textLen += len; + if(!ch) break; + { + Block block { type = BR, parent = textBlock.parent }; + Block newBlock { type = TEXT, parent = textBlock.parent }; + + textBlock.parent.subBlocks.Insert(textBlock, block); + textBlock.parent.subBlocks.Insert(block, newBlock); + + newBlock.textLen = 0; + newBlock.text = new char[1]; + newBlock.text[0] = 0; + + textBlock = newBlock; + } + if(ch == '\r' && text[c+1] == '\n') c++; + start = c + 1; + } + } + + html.block = html.block.parent; + delete text; + } + delete tmp; + delete tmpPath; + + } + else + { + html.Parse(f); + if(html.baseHRef) + { + delete this.location; + this.location = CopyString(html.baseHRef); + } + } LoadGraphics(html.defaultFont, &previous); + html.block.font = html.defaultFont.font; LoadGraphics(html.block, &previous); CreateForms(html.block); @@ -804,7 +1003,10 @@ class HTMLView : Window */ } + ((GuiApplication)__thisModule.application).Unlock(); + // PrintLn("At position ", f.Tell(), " for ", fileName); delete f; + ((GuiApplication)__thisModule.application).Lock(); NotifyPageOpened(master); } @@ -836,6 +1038,7 @@ class HTMLView : Window void * previous = null; html.Parse(f); LoadGraphics(html.defaultFont, &previous); + html.block.font = html.defaultFont.font; LoadGraphics(html.block, &previous); CreateForms(html.block); @@ -964,6 +1167,7 @@ class HTMLView : Window int maxH = clientSize.h - BOTTOM_MARGIN; OldList leftObjects { }; OldList rightObjects { }; + Font font; AlignedObject object, nextObject; int h = 0; @@ -986,7 +1190,6 @@ class HTMLView : Window int left, right; int x, maxW; int thisLineCentered = centered; - Font font = surface.GetFont(); bool changeLine; left = LEFT_MARGIN; @@ -1011,7 +1214,9 @@ class HTMLView : Window right = Max(left, right); maxW = right - left; + font = surface.font; newH = ComputeLine(surface, block, textPos, &nextBlock, &nextTextPos, ¢ered, &w, maxW, maxH - y, RenderFlags {}, y, &leftObjects, &rightObjects, &changeLine, false, 0, 0); + surface.font = font; if(thisLineCentered) x = Max(left,(left + right - w) / 2); else @@ -1110,7 +1315,7 @@ class HTMLView : Window int left, right; int x, maxW; int thisLineCentered = centered; - Font font = surface.GetFont(); + Font font = surface.font; bool changeLine; left = LEFT_MARGIN; @@ -1141,7 +1346,7 @@ class HTMLView : Window else x = left; - surface.TextFont(font); + surface.font = font; if(PickLine(this, surface, x - scroll.x, y - scroll.y, maxW, newH, block, textPos, nextBlock, nextTextPos, @@ -1161,6 +1366,17 @@ class HTMLView : Window } delete surface; } + + for(object = leftObjects.last; object; object = nextObject) + { + nextObject = object.prev; + leftObjects.Delete(object); + } + for(object = rightObjects.last; object; object = nextObject) + { + nextObject = object.prev; + rightObjects.Delete(object); + } return result; } @@ -1184,7 +1400,7 @@ class HTMLView : Window } if(linkBlock) { - if(strstr(linkBlock.href, "edit://") == linkBlock.href) + if(linkBlock.href && strstr(linkBlock.href, "edit://") == linkBlock.href) cursor = ((GuiApplication)__thisModule).GetCursor(iBeam); else cursor = ((GuiApplication)__thisModule).GetCursor(hand); @@ -1353,21 +1569,7 @@ class HTMLView : Window { delete this.location; - { - ImageEntry entry; - FontEntry fontEntry; - while((entry = imageCache.first)) - { - imageCache.Remove(entry); - delete entry; - } - while((fontEntry = fontCache.first)) - { - fontCache.Remove(fontEntry); - delete fontEntry; - } - html.block.ClearEntries(); - } + html.block.ClearEntries(); } property char * location @@ -1382,4 +1584,13 @@ class HTMLView : Window } get { return location; } } + + property String title + { + get + { + String title = html.title; + return title ? title : location; + } + } } diff --git a/extras/html/htmlParser.ec b/extras/html/htmlParser.ec index 438339b..d1b64f7 100644 --- a/extras/html/htmlParser.ec +++ b/extras/html/htmlParser.ec @@ -1,7 +1,7 @@ import "HTMLView" -#define MAX_TAG_LEN 204800 -#define MAX_SYMBOL_LEN 1000 +#define MAX_TAG_LEN 256 +#define MAX_SYMBOL_LEN 256 #define WORD_NONE 0 #define WORD_NORMAL 1 @@ -101,12 +101,40 @@ class Block : struct } }; -static bool GetKeyWordEx(char ** input, char * keyWord, int maxSize, bool treatEqual) +String ParseURL(String input) +{ + int c; + char ch; + int len = strlen(input); + String output = new char[len+1]; + len = 0; + for(c = 0; (ch = input[c]); c++) + { + if(ch == '%' && isalnum(input[c+1]) && isalnum(input[c+2])) + { + char hex[3] = { input[c+1], input[c+2], 0 }; + char * end; + int v = (int)strtoul(hex, &end, 16); + if(v && end == hex + 2) + { + output[len++] = (char)v; + c += 2; + continue; + } + } + output[len++] = ch; + } + output[len++] = 0; + return renew output char[len]; +} + +/*static */bool GetKeyWordEx(char ** input, char * keyWord, int maxSize, bool treatEqual, bool acceptSingleQuote) { char * string = *input; char ch; int c = 0; bool quoted = false, start = true, wasQuoted = false; + char quoteChar = 0; for(; (ch = *string); string++) { @@ -121,8 +149,10 @@ static bool GetKeyWordEx(char ** input, char * keyWord, int maxSize, bool treatE { if(!quoted && ((ch == ',' || (treatEqual && ch == '=')) || ch == '>') ) break; - else if(ch == '\"' /*|| ch == '\''*/) + else if((ch == '\"' || (acceptSingleQuote && ch == '\'')) && (!quoteChar || quoteChar == ch)) { + if(!wasQuoted) + quoteChar = ch; quoted ^= true; wasQuoted = true; start = false; @@ -143,7 +173,7 @@ static bool GetKeyWordEx(char ** input, char * keyWord, int maxSize, bool treatE static bool GetKeyWord(char ** input, char * keyWord, int maxSize) { - return GetKeyWordEx(input, keyWord, maxSize, true); + return GetKeyWordEx(input, keyWord, maxSize, true, false); } static char * GetString(char * string, char * what, int count) @@ -164,30 +194,54 @@ static char * GetString(char * string, char * what, int count) return string + sc; } -static Block AddBlock(Block parent, BlockType type) +#include + +String EncodeString(String input, int * lenPtr) { - Block block = Block { parent = parent, type = type }; - parent.subBlocks.Add(block); - return block; + if(UTF8Validate(input)) + { + return CopyString(input); + } + else + { + int len = strlen(input); + String s = new char[len*4+1]; + len = ISO8859_1toUTF8(input, s, len*4); + if(lenPtr) *lenPtr = len; + return renew s char[len+1]; + } } -#include - class HTMLFile { Block block {}; Block defaultFont { }; Block body; + Block titleBlock; ColorAlpha background { 255, white }; + String baseHRef; //Button defaultButton; + Block ::AddBlock(Block parent, BlockType type) + { + Block block = Block { parent = parent, type = type }; + parent.subBlocks.Add(block); + return block; + } + + ~HTMLFile() + { + delete baseHRef; + } + bool Parse(File f) { + bool result = true; bool insideTag = false; char tag[MAX_TAG_LEN]; char symbol[MAX_SYMBOL_LEN]; - int tagLen; + int tagLen = 0; Block block = this.block, subBlock; char * text; int textLen = 0; @@ -198,6 +252,7 @@ class HTMLFile byte lastCh = ' '; bool code = false; bool quoted = false; + bool lastBR = true; Block fontBlock = defaultFont; fontBlock.type = FONT; @@ -212,13 +267,14 @@ class HTMLFile fontBlock.textColor = black; fontBlock.size = 10; - fontBlock.font = FontEntry { size = fontBlock.size, attribs = fontBlock.attribs, face = CopyString(fontBlock.face) }; - fontCache.Add(fontBlock.font); + /*fontBlock.font = FontEntry { size = fontBlock.size, attribs = fontBlock.attribs, face = CopyString(fontBlock.face) }; + fontCache.Add(fontBlock.font);*/ background = white; text = new char[32768*4]; + block.font = fontBlock.font; body = block; // Parse entire file @@ -227,7 +283,9 @@ class HTMLFile byte ch = 0; f.Getc(&ch); +#ifdef _DEBUG //fwrite(&ch, 1, 1, stdout); +#endif if(commented) { if((ch == '-' && tagLen < 2) || (ch == '>' && tagLen == 2)) @@ -246,8 +304,10 @@ class HTMLFile { if(ch == '\"') quoted ^= true; - if(ch == '<' && !quoted) + if(ch == '<' && !quoted && !insideScript && !insideStyle) + { insideTag++; + } /*else */if(ch == '>' && !quoted) { insideTag--; @@ -283,6 +343,7 @@ class HTMLFile } else if(!strcmpi(keyWord, "img")) { + lastBR = false; subBlock = AddBlock(block, IMAGE); subBlock.valign = bottom; subBlock.halign = middle; @@ -291,7 +352,7 @@ class HTMLFile GetKeyWord(&string, keyWord, sizeof(keyWord)); if(!strcmpi(keyWord, "src")) { - GetKeyWordEx(&string, keyWord, sizeof(keyWord), false); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, false); delete subBlock.src; subBlock.src = keyWord[0] ? CopyString(keyWord) : null; } @@ -345,6 +406,11 @@ class HTMLFile } } } + else if(!strcmpi(keyWord, "title")) + { + block = AddBlock(block, TITLE); + titleBlock = block; + } else if(!strcmpi(keyWord, "body")) { block = AddBlock(block, BODY); @@ -357,7 +423,7 @@ class HTMLFile if(!strcmpi(keyWord, "bgcolor")) { GetKeyWord(&string, keyWord, sizeof(keyWord)); - background = strtol((keyWord[0] == '#') ? (keyWord+1) : keyWord, null, 16); + background = !strcmpi(keyWord, "#fff") ? white : strtol((keyWord[0] == '#') ? (keyWord+1) : keyWord, null, 16); if(keyWord[0] != '#' || strlen(keyWord) <= 7) background |= 0xFF000000; } @@ -369,10 +435,32 @@ class HTMLFile } } } - else if(!strcmpi(keyWord, "br")) + else if(!strcmpi(keyWord, "br") || (!lastBR && (!strcmpi(keyWord, "div") || !strcmpi(keyWord, "li")))) { - subBlock = AddBlock(block, BR); - lastCh = ' '; + if(!lastBR || (lastCh && lastCh != ' ')) + { + subBlock = AddBlock(block, BR); + lastCh = ' '; + lastBR = true; + } + } + else if(!strcmpi(keyWord, "/ul")) + { + lastBR = false; + } + else if(!strcmpi(keyWord, "/ul")) + { + lastBR = false; + } + else if(!strcmpi(keyWord, "/div")) + { + if(!lastBR) + { + subBlock = AddBlock(block, BR); + lastBR = true; + } + else + lastBR = false; } else if(!strcmpi(keyWord, "code")) { @@ -389,6 +477,17 @@ class HTMLFile || !strcmpi(keyWord, "strong") || !strcmpi(keyWord, "em") || !strcmpi(keyWord, "h1") || !strcmpi(keyWord, "h2") || !strcmpi(keyWord, "h3")) { + if((!strcmpi(keyWord, "h1") || !strcmpi(keyWord, "h2") || !strcmpi(keyWord, "h3"))) + { + if(!lastBR || (lastCh && lastCh != ' ')) + { + if(!lastBR) + subBlock = AddBlock(block, BR); + subBlock = AddBlock(block, BR); + lastBR = true; + } + lastCh = ' '; + } subBlock = AddBlock(block, FONT); subBlock.attribs = fontBlock.attribs; if(!strcmpi(keyWord, "font")) @@ -479,17 +578,23 @@ class HTMLFile !strcmpi(keyWord, "/h2") || !strcmpi(keyWord, "/h3")) { - if(block.type == FONT) + /*while(block.type != FONT && block.parent && block.parent.type != BODY) + block = block.parent;*/ + if(block.type == FONT || block.type == ANCHOR) { fontBlock = block.prevFont; block = block.parent; } + if(!lastBR && (!strcmpi(keyWord, "/h1") || !strcmpi(keyWord, "/h2") || !strcmpi(keyWord, "/h3"))) + { + subBlock = AddBlock(block, BR); + lastBR = true; + } } else if(!strcmpi(keyWord, "a")) { int textDecoration = 0; - subBlock = AddBlock(block, ANCHOR); - subBlock.attribs = fontBlock.attribs; + Block anchor { type = ANCHOR, parent = block }; for(;string[0];) { @@ -497,15 +602,15 @@ class HTMLFile if(!strcmpi(keyWord, "name")) { - GetKeyWordEx(&string, keyWord, sizeof(keyWord), false); - delete subBlock.anchor; - subBlock.anchor = CopyString(keyWord); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, false); + delete anchor.anchor; + anchor.anchor = CopyString(keyWord); } else if(!strcmpi(keyWord, "href")) { - GetKeyWordEx(&string, keyWord, sizeof(keyWord), false); - delete subBlock.href; - subBlock.href = CopyString(keyWord); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, true); + delete anchor.href; + anchor.href = CopyString(keyWord); if(!textDecoration) textDecoration = 1; } @@ -513,22 +618,46 @@ class HTMLFile { //for(;string[0];) { - GetKeyWordEx(&string, keyWord, sizeof(keyWord), false); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, false); if(strstr(keyWord, "text-decoration:") && strstr(keyWord, "none;")) textDecoration = 2; } } } - subBlock.attribs |= FONT_BOLD; - if(textDecoration == 1) subBlock.attribs |= FONT_UNDERLINE; + + if(anchor.href && (/*lastBR || */isalnum(lastCh))) + { + subBlock = AddBlock(block, TEXT); + subBlock.text = CopyString(" "); + subBlock.textLen = 2; + subBlock.prevFont = fontBlock; + } + subBlock = anchor; + block.subBlocks.Add(subBlock); + + subBlock.attribs = fontBlock.attribs | FONT_BOLD; delete subBlock.face; subBlock.face = CopyString(fontBlock.face); subBlock.size = fontBlock.size; subBlock.textColor = Color { 85,85,255 }; subBlock.prevFont = fontBlock; + if(textDecoration == 1) subBlock.attribs |= FONT_UNDERLINE; fontBlock = subBlock; block = subBlock; + + lastCh = 0; } + /*else if(!strcmpi(keyWord, "/span")) + { + if(isalnum(lastCh)) + { + subBlock = AddBlock(block, TEXT); + subBlock.text = CopyString(" "); + subBlock.textLen = 2; + subBlock.prevFont = block.parent.prevFont; + } + lastCh = 0; + }*/ else if(!strcmpi(keyWord, "/a")) { if(block.type == ANCHOR) @@ -555,7 +684,7 @@ class HTMLFile if(insideStyle) insideStyle--; } - else if(!strcmpi(keyWord, "input")) + else if(!strcmpi(keyWord, "input") || !strcmpi(keyWord, "button")) { subBlock = AddBlock(block, INPUT); for(;string[0];) @@ -564,12 +693,12 @@ class HTMLFile if(!strcmpi(keyWord, "type")) { - GetKeyWord(&string, keyWord, sizeof(keyWord)); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), true, true); if(!strcmpi(keyWord, "text")) { subBlock.inputType = InputType::text; } - else if(!strcmpi(keyWord, "submit")) + else if(!strcmpi(keyWord, "submit") || !strcmpi(keyWord, "image")) { subBlock.inputType = submit; } @@ -577,6 +706,10 @@ class HTMLFile { subBlock.inputType = radio; } + else if(!strcmpi(keyWord, "checkbox")) + { + subBlock.inputType = checkbox; + } else if(!strcmpi(keyWord, "hidden")) { subBlock.inputType = hidden; @@ -586,15 +719,20 @@ class HTMLFile } else if(!strcmpi(keyWord, "size")) { - int size; GetKeyWord(&string, keyWord, sizeof(keyWord)); - size = atoi(keyWord); + subBlock.size = atoi(keyWord); + } + else if(!strcmpi(keyWord, "maxlength")) + { + int maxlength; + GetKeyWord(&string, keyWord, sizeof(keyWord)); + maxlength = atoi(keyWord); } else if(!strcmpi(keyWord, "value")) { - GetKeyWordEx(&string, keyWord, sizeof(keyWord), false); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, true); delete subBlock.value; - subBlock.value = CopyString(keyWord); + subBlock.value = EncodeString(keyWord, null); } else if(!strcmpi(keyWord, "name")) { @@ -602,6 +740,12 @@ class HTMLFile delete subBlock.name; subBlock.name = CopyString(keyWord); } + else if(!strcmpi(keyWord, "src")) + { + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, false); + delete subBlock.src; + subBlock.src = keyWord[0] ? CopyString(keyWord) : null; + } } } else if(!strcmpi(keyWord, "form")) @@ -613,7 +757,7 @@ class HTMLFile if(!strcmpi(keyWord, "action")) { - GetKeyWordEx(&string, keyWord, sizeof(keyWord), false); + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, false); delete subBlock.action; subBlock.action = CopyString(keyWord); } @@ -639,6 +783,20 @@ class HTMLFile block = block.parent; } } + else if(!strcmpi(keyWord, "base")) + { + while(string[0]) + { + GetKeyWord(&string, keyWord, sizeof(keyWord)); + + if(!strcmpi(keyWord, "href")) + { + GetKeyWordEx(&string, keyWord, sizeof(keyWord), false, true); + delete baseHRef; + baseHRef = ParseURL(keyWord); + } + } + } else if(!strcmpi(keyWord, "table")) { lastCh = ' '; @@ -678,7 +836,7 @@ class HTMLFile else if(!strcmpi(keyWord, "bgcolor")) { GetKeyWord(&string, keyWord, sizeof(keyWord)); - subBlock.bgColor = 0xFF000000 | strtol((keyWord[0] == '#') ? (keyWord+1) : keyWord, null, 16); + subBlock.bgColor = !strcmpi(keyWord, "#fff") ? white : (0xFF000000 | strtol((keyWord[0] == '#') ? (keyWord+1) : keyWord, null, 16)); } else if(!strcmpi(keyWord, "align")) { @@ -705,17 +863,17 @@ class HTMLFile if(block.type == TD) { block = block.parent; - lastCh = ' '; + lastCh = 0;//' '; } if(block.type == TR) { block = block.parent; - lastCh = ' '; + lastCh = 0;//' '; } if(block.type == TABLE) { block = block.parent; - lastCh = ' '; + lastCh = 0;//' '; } } else if(!strcmpi(keyWord, "tr")) @@ -843,7 +1001,7 @@ class HTMLFile else if(!strcmpi(keyWord, "bgcolor")) { GetKeyWord(&string, keyWord, sizeof(keyWord)); - subBlock.bgColor = 0xFF000000 | strtol((keyWord[0] == '#') ? (keyWord+1) : keyWord, null, 16); + subBlock.bgColor = !strcmpi(keyWord, "#fff") ? white : (0xFF000000 |strtol((keyWord[0] == '#') ? (keyWord+1) : keyWord, null, 16)); } else if(!strcmpi(keyWord, "valign")) { @@ -886,7 +1044,7 @@ class HTMLFile if(block.type == TD) { block = block.parent; - lastCh = ' '; + lastCh = 0;//' '; } } else if(!strcmpi(keyWord, "/html")) @@ -895,14 +1053,27 @@ class HTMLFile } else { - tag[tagLen++] = ch; - tag[tagLen] = '\0'; + if(tagLen < MAX_TAG_LEN-1) + { + tag[tagLen++] = ch; + tag[tagLen] = '\0'; + } } } else { - tag[tagLen++] = ch; - tag[tagLen] = '\0'; + if((insideScript || insideStyle) && !tagLen && ch != '/') + { + insideTag = false; + } + else + { + if(tagLen < MAX_TAG_LEN-1) + { + tag[tagLen++] = ch; + tag[tagLen] = '\0'; + } + } } if(!strcmp(tag, "!--")) { @@ -916,31 +1087,34 @@ class HTMLFile { if(ch == '<') { - if(textLen) + if(!insideScript && !insideStyle) { - if(block.type == TABLE) + if(textLen) { - subBlock = AddBlock(block, TR); - block = subBlock; - } - if(block.type == TR) - { - subBlock = AddBlock(block, TD); - subBlock.span = subBlock.rowSpan = 1; - subBlock.valign = block.valign; - subBlock.halign = block.halign; - block = subBlock; - } - - subBlock = AddBlock(block, TEXT); - delete subBlock.text; - subBlock.text = CopyString(text); - subBlock.textLen = textLen; - + if(block.type == TABLE) + { + subBlock = AddBlock(block, TR); + block = subBlock; + } + if(block.type == TR) + { + subBlock = AddBlock(block, TD); + subBlock.span = subBlock.rowSpan = 1; + subBlock.valign = block.valign; + subBlock.halign = block.halign; + block = subBlock; + } - textLen = 0; - text[0] = '\0'; + subBlock = AddBlock(block, TEXT); + delete subBlock.text; + subBlock.text = EncodeString(text, &textLen); + subBlock.textLen = textLen; + if(block.type != TITLE) + lastBR = false; + textLen = 0; + text[0] = '\0'; + } } insideTag = true; @@ -954,7 +1128,9 @@ class HTMLFile { unichar unicode = 0; char utf8[5]; - if(!strcmpi(symbol, "nbsp")) + if(symbol[0] == '#' && symbol[1] == 'x') + unicode = strtol(symbol+2, null, 16); + else if(!strcmpi(symbol, "nbsp")) unicode = ' '; else if(!strcmpi(symbol, "copy")) unicode ='©'; @@ -971,7 +1147,7 @@ class HTMLFile else if(!strcmpi(symbol, "acirc")) unicode = 'â'; else if(!strcmpi(symbol, "ocirc")) - unicode = 'ô'; + unicode = 'ô'; if(unicode) { int len = UTF32toUTF8Len(&unicode, 1, utf8, 5); @@ -995,8 +1171,11 @@ class HTMLFile } else { - symbol[symbolLen++] = ch; - symbol[symbolLen] = '\0'; + if(symbolLen < MAX_SYMBOL_LEN-1) + { + symbol[symbolLen++] = ch; + symbol[symbolLen] = '\0'; + } } } else @@ -1026,7 +1205,7 @@ class HTMLFile subBlock = AddBlock(block, TEXT); delete subBlock.text; - subBlock.text = CopyString(text); + subBlock.text = EncodeString(text, &textLen); subBlock.textLen = textLen; textLen = 0; text[0] = '\0'; @@ -1040,6 +1219,19 @@ class HTMLFile byte ch = 0; f.Getc(&ch); }*/ - return true; + return result; + } + + property String title + { + get + { + if(titleBlock && titleBlock.subBlocks.first && ((Block)titleBlock.subBlocks.first).type == TEXT) + { + Block t = titleBlock.subBlocks.first; + return t.text; + } + return null; + } } } diff --git a/extras/html/lines.ec b/extras/html/lines.ec index 7d09e8f..b8a9917 100644 --- a/extras/html/lines.ec +++ b/extras/html/lines.ec @@ -31,26 +31,35 @@ Block GetNextBlock(Block block) { if(block.type == FONT || block.type == ANCHOR) { - surface.TextFont(block.prevFont.font.font); + surface.TextFont(block.parent.font.font); if(flags.render) - surface.SetForeground(block.prevFont.textColor); + surface.SetForeground(block.parent.textColor); } block = block.next; break; } + //if(block) + { + if(block.type == FONT || block.type == ANCHOR) + { + surface.TextFont(block.parent.font.font); + if(flags.render) + surface.SetForeground(block.parent.textColor); + } + } block = block.parent; // Getting out of a block if(block) { - if(block.type == FONT || block.type == ANCHOR) + /*if(block.type == FONT || block.type == ANCHOR) { surface.TextFont(block.prevFont.font.font); if(flags.render) surface.SetForeground(block.prevFont.textColor); } - else if(block.type == CENTER) + else */if(block.type == CENTER) { if(centered) (*centered)--; @@ -106,7 +115,7 @@ int ComputeLine(Surface surface, Block startBlock, int startTextPos, Block * nex if(block.window) { width += block.window.size.w; - height = Max(height, block.window.size.h); + height = Max(height, Max(26, block.window.size.h)); } break; } @@ -161,7 +170,6 @@ int ComputeLine(Surface surface, Block startBlock, int startTextPos, Block * nex surface.TextExtent(" ", 1, null, &th); height = Max(height, th); - for(; textPos