forked from BrowserJam/jam001
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The original repository lives at: https://github.com/TomasHubelbauer/leveret
- Loading branch information
1 parent
72b4600
commit eacf2bb
Showing
27 changed files
with
3,955 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# NPM | ||
node_modules | ||
|
||
# Bun | ||
bun.lockb | ||
|
||
# Caches | ||
fetchCached.ts.*.html | ||
|
||
# Outputs | ||
index |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { file, write } from 'bun'; | ||
|
||
export default async function fetchCached(url: string) { | ||
const protocol = new URL(url).protocol; | ||
if (protocol !== 'http:' && protocol !== 'https:') { | ||
throw new Error('Only HTTP(S) URLs are supported'); | ||
} | ||
|
||
const name = url.slice((protocol + '//').length).replace(/[^a-z0-9]+/gi, '-'); | ||
const path = `${import.meta.file}.${name}.html`; | ||
|
||
const cache = file(path); | ||
if (await cache.exists()) { | ||
return await cache.text(); | ||
} | ||
|
||
const response = await fetch(url); | ||
const text = await response.text(); | ||
write(path, text); | ||
return text; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { expect, test } from 'bun:test'; | ||
import fitText from './fitText'; | ||
import { createCanvas } from '@napi-rs/canvas'; | ||
|
||
test('empty', () => { | ||
const canvas = createCanvas(640, 480); | ||
const context = canvas.getContext('2d'); | ||
expect(fitText('', 100, context)).toEqual({ text: '', width: 0 }); | ||
}); | ||
|
||
test('fit', () => { | ||
const canvas = createCanvas(640, 480); | ||
const context = canvas.getContext('2d'); | ||
expect(fitText('short', 100, context)).toEqual({ text: 'short', width: 22 }); | ||
}); | ||
|
||
test('one break', () => { | ||
const canvas = createCanvas(640, 480); | ||
const context = canvas.getContext('2d'); | ||
|
||
let text = 'short but long enough to break'; | ||
|
||
const slice = fitText(text, 100, context); | ||
expect(slice).toEqual({ | ||
text: 'short but long enough ', | ||
width: 99, | ||
}); | ||
|
||
const rest = text.slice(slice.text.length); | ||
expect(fitText(rest, 100, context)).toEqual({ text: 'to break', width: 36 }); | ||
}); | ||
|
||
test('two breaks', () => { | ||
const canvas = createCanvas(640, 480); | ||
const context = canvas.getContext('2d'); | ||
|
||
let text = 'short but long enough to break and then break again'; | ||
|
||
const slice1 = fitText(text, 100, context); | ||
expect(slice1).toEqual({ | ||
text: 'short but long enough ', | ||
width: 99, | ||
}); | ||
|
||
text = text.slice(slice1.text.length); | ||
|
||
const slice2 = fitText(text, 100, context); | ||
expect(slice2).toEqual({ | ||
text: 'to break and then brea', | ||
width: 100, | ||
}); | ||
|
||
const rest = text.slice(slice2.text.length); | ||
expect(fitText(rest, 100, context)).toEqual({ text: 'k again', width: 32 }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { SKRSContext2D } from '@napi-rs/canvas'; | ||
|
||
export default function fitText(text: string, limit: number, context: SKRSContext2D) { | ||
if (limit < 0) { | ||
throw new Error('limit must be greater than or equal to 0'); | ||
} | ||
|
||
let low = 0; | ||
let high = text.length; | ||
let result = text; | ||
let finalWidth = 0; | ||
|
||
while (low <= high) { | ||
const mid = ~~((low + high) / 2); | ||
const candidate = text.slice(0, mid); | ||
const width = ~~context.measureText(candidate).width; | ||
|
||
if (width <= limit) { | ||
result = candidate; | ||
finalWidth = width; | ||
low = mid + 1; | ||
} else { | ||
high = mid - 1; | ||
} | ||
} | ||
|
||
return { | ||
text: result, | ||
width: finalWidth, | ||
}; | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { write } from 'bun'; | ||
import { createCanvas } from '@napi-rs/canvas'; | ||
import parse from './parse'; | ||
import layout from './layout'; | ||
import render from './render'; | ||
import fetchCached from './fetchCached'; | ||
|
||
// TODO: Accept a custom URL from the CLI | ||
// Download the 1st WWW website page and parse it | ||
// Use `view-source:http://info.cern.ch/hypertext/WWW/TheProject.html` to debug | ||
const url = 'http://info.cern.ch/hypertext/WWW/TheProject.html'; | ||
|
||
// Set up the native, Skia-based `canvas` and its 2D rendering `context` | ||
const canvas = createCanvas(640, 480); | ||
const context = canvas.getContext('2d'); | ||
|
||
context.save(); | ||
context.fillStyle = 'white'; | ||
context.fillRect(0, 0, canvas.width, canvas.height); | ||
context.restore(); | ||
|
||
const html = await fetchCached(url); | ||
let document = parse(html); | ||
if (document.errors.length > 0) { | ||
let html = `<DIV color=red>Leveret failed to parse ${url}!</DIV>`; | ||
for (const error of document.errors) { | ||
html += `<DIV>${error}</DIV>`; | ||
} | ||
|
||
document = parse(html); | ||
} | ||
|
||
const { node, getSuperNode, errors, infos } = document; | ||
if (errors.length > 0) { | ||
throw new Error(`Failed to parse ${url}:\n${errors.join('\n')}`); | ||
} | ||
|
||
for (const info of infos) { | ||
console.log(info); | ||
} | ||
|
||
render(layout(node, getSuperNode, context), context); | ||
await write('index.png', await canvas.encode('png')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ElementNode } from './parse'; | ||
|
||
const BLOCKS = ['HTML', 'BODY', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'DL', 'DD', 'DT', 'UL', 'LI', 'MENU', 'DIV', 'HEADER']; | ||
|
||
export default function isBlock(node: ElementNode) { | ||
return BLOCKS.includes(node.tag); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { LayoutNode } from './layout'; | ||
|
||
const NON_VISUALS = ['HEADER']; | ||
|
||
export default function isNonVisual(node: LayoutNode) { | ||
return (node.type === 'element' && NON_VISUALS.includes(node.tag)) || node.layout === 'removed'; | ||
} |
Oops, something went wrong.