Skip to content

Commit

Permalink
Add support for font styles, weight, size
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinL10 committed Apr 27, 2023
1 parent 70558e7 commit 601374f
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 38 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ ch.2
- Emoji support
- Allow browser resizing
- Zoom for font size
- newlines



ch.3
-
44 changes: 8 additions & 36 deletions browser/graphics.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from browser.lexer import lex, Tag
from browser.lexer import lex, Tag, Text
from browser.request import request
from browser.constants import HSTEP, VSTEP, HEIGHT, WIDTH, SCROLL_STEP
from browser.layout import Layout
import tkinter
import tkinter.font

Expand All @@ -18,13 +19,6 @@ def __init__(self):
# Linux mouse wheel bindings
self.window.bind("<Button-4>", self.scrollup)
self.window.bind("<Button-5>", self.scrolldown)

self.font = tkinter.font.Font(
family="Times",
size=16,
weight="bold",
slant="italic",
)

def scrollup(self, e):
self.scroll = max(0, self.scroll - SCROLL_STEP)
Expand All @@ -34,49 +28,27 @@ def scrolldown(self, e):
self.scroll += SCROLL_STEP
self.draw()

# Returns the coordinates (relative to start of the PAGE)
# for each character of the text
def layout(self, tokens):
display_list = []
cursor_x, cursor_y = HSTEP, VSTEP

for token in tokens:
if isinstance(token, Tag):
continue

for word in token.text.split():
w = self.font.measure(word)
cursor_x += w

if cursor_x >= WIDTH - HSTEP:
cursor_y += self.font.metrics("linespace") * 1.25
cursor_x = HSTEP

display_list.append((cursor_x, cursor_y, word))
cursor_x += w + self.font.measure(" ")

return display_list

# 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)
def draw(self):
self.canvas.delete("all")
for x, y, c in self.display_list:
for x, y, c, font in self.display_list:
if y > self.scroll + HEIGHT:
continue
if y + VSTEP < self.scroll:
continue
self.canvas.create_text(x, y - self.scroll, text=c, anchor='nw')
self.canvas.create_text(x, y - self.scroll, text=c, anchor='nw', font=font)

# Renders the contents of the url to the canvas
def load(self, url):
headers, body = request(url)
text = lex(body)

self.display_list = self.layout(text)
tokens = lex(body)
self.display_list = Layout(tokens).display_list
self.draw()


Browser().load("https://example.org")
# Browser().load("https://example.org")
Browser().load("file://./tests/index.html")
tkinter.mainloop()
96 changes: 96 additions & 0 deletions browser/layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from browser.constants import HSTEP, VSTEP, WIDTH
from browser.lexer import Tag, Text
import tkinter.font

# Represents the layout of the web page (including font, position, size, etc.)
class Layout:
def __init__(self, tokens):
self.display_list = []
self.cursor_x = HSTEP
self.cursor_y = VSTEP
self.weight = "normal"
self.style = "roman"
self.size = 16

# Text is aligned along the top by default (instead of the bottom), so
# we use a two-pass approach: first compute the x-coordinates of the
# elements along the current line, then adjust the y-coordinates so that
# all elements are aligned along the bottom
self.line = []

for token in tokens:
self.add_token(token)

# Flushy any remaining layout elements
self.flush()


# add the token to the layout
def add_token(self, token):
print(token)
if isinstance(token, Text):
self.add_text(token)
elif token.tag == "i":
self.style = "italic"
elif token.tag == "/i":
self.style = "roman"
elif token.tag == "b":
self.weight = "bold"
elif token.tag == "/b":
self.weight = "normal"
elif token.tag == "small":
self.size -= 2
elif token.tag == "/small":
self.size += 2
elif token.tag == "big":
self.size += 4
elif token.tag == "/big":
self.size -= 4
elif token.tag == "br":
self.flush()
elif token.tag == "/p":
self.flush()
self.cursor_y += VSTEP


# adds the text to the layout
def add_text(self, token):
font = tkinter.font.Font(
family="Times",
size=self.size,
weight=self.weight,
slant=self.style,
)

for word in token.text.split():
w = font.measure(word)
self.cursor_x += w

if self.cursor_x >= WIDTH - HSTEP:
self.flush()


self.line.append((self.cursor_x, word, font))
self.cursor_x += w + font.measure(" ")

# Flushes the current display line:
# - Aligns the word along the bottom of the line
# - Add all of the words in the current line to the display_list
# - Updates cursor_x and cursor_y
def flush(self):
if not self.line:
return

metrics = [font.metrics() for x, word, font in self.line]
max_ascent = max([metric["ascent"] for metric in metrics])
max_descent = max([metric["descent"] for metric in metrics])
baseline = self.cursor_y + max_ascent

for x, word, font in self.line:
y = baseline - font.metrics("ascent")
self.display_list.append((x, y, word, font))

self.cursor_y = baseline + 1.25 * max_descent
self.cursor_x = HSTEP

self.line = []
1 change: 0 additions & 1 deletion browser/lexer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


class Text:
def __init__(self, text):
self.text = text
Expand Down
16 changes: 15 additions & 1 deletion tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
<title>Document</title>
</head>
<body>
Hi! this is a test document
Hi! this is a test

<b>bold text</b>

<i>italic text</i>

<big>this is a big paragraph!</big>
<b>bold again !</b>

<small>
this is a small paragraph!
</small>
<big>
big big big
</big>
</body>
</html>

0 comments on commit 601374f

Please sign in to comment.