diff --git a/browser/graphics.py b/browser/graphics.py index 6fe1205..f3daf6c 100644 --- a/browser/graphics.py +++ b/browser/graphics.py @@ -1,4 +1,4 @@ -from browser.parser import HTMLParser +from browser.parser import HTMLParser, style from browser.request import request from browser.constants import VSTEP, HEIGHT, WIDTH, SCROLL_STEP from browser.layout import DocumentLayout @@ -9,7 +9,9 @@ class Browser: def __init__(self): self.window = tkinter.Tk() - self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT) + self.canvas = tkinter.Canvas( + self.window, width=WIDTH, height=HEIGHT, bg="white" + ) self.canvas.pack() self.scroll = 0 @@ -30,7 +32,6 @@ def scrolldown(self, e): self.scroll = min(self.scroll + SCROLL_STEP, max_y) self.draw() - # Draws the to-be-displayed text; calculates the position # of each character by subtracting the current scroll # (i.e. how far the user is down the page) @@ -44,12 +45,13 @@ def draw(self): cmd.execute(self.scroll, self.canvas) - # Renders the contents of the url to the canvas def load(self, url): headers, body = request(url) - self.nodes = HTMLParser(body).parse() - self.document = DocumentLayout(self.nodes) + self.node = HTMLParser(body).parse() + style(self.node) + + self.document = DocumentLayout(self.node) self.document.layout() # The display_list consists of commands like DrawText and DrawRect diff --git a/browser/layout.py b/browser/layout.py index 950f0a2..e83b66e 100644 --- a/browser/layout.py +++ b/browser/layout.py @@ -49,9 +49,11 @@ def __init__(self, x1, y1, x2, y2, color): # Draws rectangle to the given canvas def execute(self, scroll, canvas: tkinter.Canvas): canvas.create_rectangle( - self.left, self.top - scroll, - self.right, self.bottom - scroll, - width=0, # border width + self.left, + self.top - scroll, + self.right, + self.bottom - scroll, + width=0, # border width fill=self.color, ) @@ -106,14 +108,20 @@ def __init__(self, node: Element, parent: Element, previous: "BlockLayout"): self.display_list = [] def paint(self, display_list): - if isinstance(self.node, Element) and self.node.tag == "pre": + bgcolor = self.node.style.get("background-color", "transparent") + if bgcolor != "transparent": x2, y2 = self.x + self.width, self.y + self.height - display_list.append(DrawRect(self.x, self.y, x2, y2, "gray")) + display_list.append(DrawRect(self.x, self.y, x2, y2, bgcolor)) + + # if isinstance(self.node, Element) and self.node.tag == "pre": + # x2, y2 = self.x + self.width, self.y + self.height + # display_list.append(DrawRect(self.x, self.y, x2, y2, "gray")) for child in self.children: child.paint(display_list) for x, y, word, font in self.display_list: + print(word) display_list.append(DrawText(x, y, word, font)) """ diff --git a/browser/main.py b/browser/main.py index 3427bbf..af9913e 100644 --- a/browser/main.py +++ b/browser/main.py @@ -9,7 +9,7 @@ browser.load(sys.argv[1]) else: browser.load("file:///home/vever/cs/browser/browser/tests/index.html") - browser.load("https://browser.engineering/layout.html") + # browser.load("https://browser.engineering/layout.html") # browser.load("https://www.w3.org/Style/CSS/Test/CSS1/current/test5526c.htm") diff --git a/browser/parser.py b/browser/parser.py index df05d7c..a89fd42 100644 --- a/browser/parser.py +++ b/browser/parser.py @@ -8,7 +8,6 @@ def __init__(self, text, parent): def __repr__(self): return self.text - class Element: def __init__(self, tag, attributes, parent): self.tag = tag @@ -74,24 +73,26 @@ def parse(self): return self.finish() - ''' + """ Returns the type of tag and key-value attributes - ''' + """ + def get_attributes(self, text): parts = text.split() if len(parts) == 1: return parts[0], {} - print(parts) tag = parts[0] attributes = {} for part in parts[1:]: if "=" in part: key, value = part.split("=", 1) - if len(value) > 2 and value[0] in ["'", "\""]: + if len(value) > 2 and value[0] in ["'", '"']: value = value[1:-1] + attributes[key.lower()] = value else: attributes[part.lower()] = "" + return tag, attributes """ @@ -146,27 +147,92 @@ def finish(self): return self.unfinished.pop() - def print_tree(node, indent=0): - print(" " * indent, node) + print(" " * indent, node, node.style, getattr(node, "attributes", "")) for child in node.children: print_tree(child, indent + 2) -# k = HTMLParser( -# """ -# -# -# -# -# -# Document -# -# -# Hi! this is a test -# bold text -# -# """ -# ) - -# print_tree(k.parse()) +# A parser class containing parsing functions to advance through the text +class CSSParser: + # Initialize the CSS parser with the text being parse and the position set to 0 + def __init__(self, s): + self.s = s + self.i = 0 + + # Advance through the current whitespace characters + def whitespace(self): + while self.i < len(self.s) and self.s[self.i].isspace(): + self.i += 1 + + # Advance through the current word + def word(self): + start = self.i + while self.i < len(self.s): + if self.s[self.i].isalnum() or self.s[self.i] in "#-.%": + self.i += 1 + else: + break + + if start == self.i: + raise Exception("Error when parsing word") + + return self.s[start : self.i] + + # Advance through the given literal + def literal(self, literal): + if not (self.i < len(self.s) and self.s[self.i] == literal): + raise Exception(f"Error when parsing literal {literal}") + self.i += 1 + + # Advance through the current property-value pair + def pair(self): + prop = self.word() + self.whitespace() + self.literal(":") + self.whitespace() + value = self.word() + return prop.lower(), value + + # Advance the index unconditionally until you reach one of the chars + def ignore_until(self, chars): + while self.i < len(self.s): + if self.s[self.i] in chars: + return self.s[self.i] + else: + self.i += 1 + + # Advance through the current "style" attribute + def body(self): + pairs = {} + while self.i < len(self.s): + try: + prop, value = self.pair() + pairs[prop] = value + self.whitespace() + self.literal(";") + self.whitespace() + except Exception as e: + print("Error parsing body:", e) # DEBUG + + # Skip to the next set of properties if encounter parsing error + why = self.ignore_until([";"]) + if why == ";": + self.literal(";") + self.whitespace() + else: + break + return pairs + +# Recursively add the style property-value attributes to the given node and its children +def style(node): + node.style = {} + + if isinstance(node, Element) and "style" in node.attributes: + pairs = CSSParser(node.attributes["style"]).body() + for property, value in pairs.items(): + node.style[property] = value + + + for child in node.children: + style(child) diff --git a/tests/index.html b/tests/index.html index 10f1d87..2ebbe54 100644 --- a/tests/index.html +++ b/tests/index.html @@ -13,14 +13,9 @@

title

bold text italic text -

- Some text in a paragraph +

+ GREEN TEXT

-
- some text in a div - more text in dev -
-

this is a big text!