Skip to content

Commit

Permalink
dev(pkg::core): make use of queryLocalFonts api
Browse files Browse the repository at this point in the history
  • Loading branch information
Myriad-Dreamin committed May 18, 2023
1 parent 2dac81b commit d49a301
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 105 deletions.
1 change: 1 addition & 0 deletions packages/compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ comemo = "0.2"
once_cell = "1.17.1"
siphasher = "0.3.10"
elsa = "1.8.0"
serde-wasm-bindgen = "^0.5"

# Everything to do with wasm
wasm-bindgen = { version = "^0.2" }
Expand Down
38 changes: 36 additions & 2 deletions packages/compiler/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::sync::{Arc, Mutex};

use js_sys::{JsString, Uint8Array};
use typst_ts_compiler::font::web::BrowserFontSearcher;
pub use typst_ts_compiler::*;
use typst_ts_core::DocumentExporter;
use typst_ts_core::{cache::FontInfoCache, DocumentExporter};
use wasm_bindgen::prelude::*;

use crate::utils::console_log;
Expand All @@ -27,6 +28,11 @@ impl TypstCompiler {
}
}

#[wasm_bindgen]
pub fn get_font_info(buffer: Uint8Array) -> JsValue {
serde_wasm_bindgen::to_value(&FontInfoCache::from_data(buffer.to_vec().as_slice())).unwrap()
}

#[wasm_bindgen]
impl TypstCompiler {
pub fn reset(&mut self) {
Expand Down Expand Up @@ -54,6 +60,26 @@ impl TypstCompiler {
}
}

pub fn modify_font_data(&mut self, idx: usize, buffer: Uint8Array) {
self.world
.font_resolver
.modify_font_data(idx, buffer.to_vec().into());
}

pub fn rebuild(&mut self) {
if self.world.font_resolver.partial_resolved() {
self.world.font_resolver.rebuild();
}
}

pub fn get_loaded_fonts(&mut self) -> Vec<JsString> {
self.world
.font_resolver
.loaded_fonts()
.map(|s| format!("<{}, {:?}>", s.0, s.1).into())
.collect()
}

pub fn get_ast(&mut self) -> Result<String, JsValue> {
let data = Arc::new(Mutex::new(vec![]));

Expand All @@ -64,9 +90,17 @@ impl TypstCompiler {
Ok(())
}));

// let artifact = Arc::new(Mutex::new(None));

// compile and export document
typst::compile(&self.world)
.and_then(|output| ast_exporter.export(&self.world, &output))
.and_then(|output| {
// let mut artifact = artifact.lock().unwrap();
// artifact = Some(Artifact::from(&output));
// drop(artifact);

ast_exporter.export(&self.world, &output)
})
.unwrap();
let converted = ansi_to_html::convert_escaped(
String::from_utf8(data.lock().unwrap().clone())
Expand Down
63 changes: 25 additions & 38 deletions packages/typst.ts/examples/compiler.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="origin-trial" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Typst.ts</title>
<script type="module" src="/dist/main.js"></script>
Expand All @@ -26,6 +26,23 @@
const terminalContent = document.getElementById('terminal-content');
terminalContent.innerHTML = 'Compiling...';

const runCompile = async () => {
const data = await fetch('http://localhost:20810/skyzh-cv/main.typ').then(res =>
res.text(),
);

const begin = performance.now();
compilerPlugin.addSource('main.typ', data, true);
const ast = await compilerPlugin.getAst();
const end = performance.now();
const rounded = Math.round((end - begin) * 1000) / 1000;

const compileInfo = `---
<span style="color:#c0caf5">Compiled in <span style="color:#7dcfff">${rounded}</span>ms</span>`;

terminalContent.innerHTML = [compileInfo, ast].join('\n');
};

let compilerPlugin = window.TypstCompileModule.createTypstCompiler();
compilerPlugin
.init({
Expand All @@ -45,44 +62,11 @@
getModule: () =>
'/node_modules/@myriaddreamin/typst-ts-web-compiler/typst_ts_web_compiler_bg.wasm',
})
.then(async () => {
const data = await fetch('http://localhost:20810/skyzh-cv/main.typ').then(res =>
res.text(),
);

const begin = performance.now();
compilerPlugin.addSource('main.typ', data, true);
const ast = await compilerPlugin.getAst();
const end = performance.now();
const rounded = Math.round((end - begin) * 1000) / 1000;

const compileInfo = `---
<span style="color:#c0caf5">Compiled in <span style="color:#7dcfff">${rounded}</span>ms</span>`;

terminalContent.innerHTML = [compileInfo, ast].join('\n');
});

let rendererPlugin = window.TypstRenderModule.createTypstRenderer(pdfjsLib);
rendererPlugin
.init({
beforeBuild: [
window.TypstRenderModule.preloadRemoteFonts([
'http://localhost:20811/fonts/LinLibertine_R.ttf',
'http://localhost:20811/fonts/LinLibertine_RB.ttf',
'http://localhost:20811/fonts/LinLibertine_RBI.ttf',
'http://localhost:20811/fonts/LinLibertine_RI.ttf',
'http://localhost:20811/fonts/NewCMMath-Book.otf',
'http://localhost:20811/fonts/NewCMMath-Regular.otf',
]),
window.TypstRenderModule.preloadSystemFonts({
byFamily: ['Segoe UI Symbol'],
}),
],
getModule: () =>
'/node_modules/@myriaddreamin/typst-ts-renderer/typst_ts_renderer_bg.wasm',
})
.then(() => {
console.log('renderer init');
document.getElementById('compile-button').addEventListener('click', () => {
runCompile();
});
return runCompile();
});
});
</script>
Expand Down Expand Up @@ -149,6 +133,9 @@
<div class="clear"></div>
</div>
<div class="content">
<div>
<button id="compile-button">Compile</button>
</div>
<div class="terminal">
<pre id="terminal-content">hello world</pre>
</div>
Expand Down
3 changes: 3 additions & 0 deletions packages/typst.ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@
"esbuild-plugin-wasm": "^1.0.0",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"idb": "^7.1.1"
}
}
24 changes: 18 additions & 6 deletions packages/typst.ts/src/compiler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @ts-ignore
import typstInit, * as typst from '../../compiler/pkg/typst_ts_web_compiler';
import { buildComponent } from './init';
import { buildComponent, globalFontPromises } from './init';

import type { InitOptions, BeforeBuildMark } from './options.init';
import { LazyWasmModule } from './wasm';
Expand Down Expand Up @@ -47,10 +47,22 @@ class TypstCompilerDriver {
this.compiler = await buildComponent(options, gCompilerModule, typst.TypstCompilerBuilder, {});
}

async loadFont(builder: typst.TypstCompilerBuilder, fontPath: string): Promise<void> {
const response = await fetch(fontPath);
const fontBuffer = new Uint8Array(await response.arrayBuffer());
await builder.add_raw_font(fontBuffer);
async runSyncCodeUntilStable<T>(execute: () => T): Promise<T> {
do {
console.log(this.compiler.get_loaded_fonts());
const result = await execute();
console.log(this.compiler.get_loaded_fonts());
if (globalFontPromises.length > 0) {
const promises = globalFontPromises.splice(0, globalFontPromises.length);
const callbacks = await Promise.all(promises);
for (const callback of callbacks) {
this.compiler.modify_font_data(callback.idx, new Uint8Array(callback.buffer));
}
this.compiler.rebuild();
continue;
}
return result;
} while (true);
}

async reset(): Promise<void> {
Expand All @@ -62,7 +74,7 @@ class TypstCompilerDriver {
}

async getAst(): Promise<string> {
return this.compiler.get_ast();
return this.runSyncCodeUntilStable(() => this.compiler.get_ast());
}

async compile(options: any): Promise<void> {
Expand Down
118 changes: 94 additions & 24 deletions packages/typst.ts/src/init.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { get_font_info } from '../../compiler/pkg/typst_ts_web_compiler';
import { BeforeBuildMark, InitOptions } from './options.init';
import { LazyWasmModule } from './wasm';
import * as idb from 'idb';

/** @internal */
export interface TypstCommonBuilder<T> {
Expand All @@ -23,42 +25,110 @@ interface InitContext<T> {
hooks: ComponentBuildHooks;
}

/** @internal */
export const globalFontPromises: Promise<{ buffer: ArrayBuffer; idx: number }>[] = [];

async function addPartialFonts<T>({ builder, hooks }: InitContext<T>): Promise<void> {
const t = performance.now();

if ('queryLocalFonts' in window) {
const fonts: any[] = await (window as any).queryLocalFonts();
console.log('local fonts count:', fonts.length);
await builder.add_web_fonts(fonts);

const db = await idb.openDB('typst-ts-store', 1, {
upgrade(db) {
db.createObjectStore('font-information', {
keyPath: 'postscriptName',
});
},
});

const informations = await Promise.all(
fonts.map(async font => {
const postscriptName = font.postscriptName;

return (await db.get('font-information', postscriptName))?.info;
}),
);

await builder.add_web_fonts(
fonts.map((font, font_idx) => {
let gettingBuffer = false;
let readyBuffer: ArrayBuffer | undefined = undefined;
const fullName = font.fullName;
const postscriptName = font.postscriptName;

const prev = informations[font_idx];
if (prev) {
console.log('prev', postscriptName, prev);
}
return {
family: font.family,
style: font.style,
fullName: fullName,
postscriptName: postscriptName,
ref: font,
info: informations[font_idx],
blob: (idx: number) => {
console.log(this, font, idx);
if (readyBuffer) {
return readyBuffer;
}
if (gettingBuffer) {
return;
}
gettingBuffer = true;
globalFontPromises.push(
(async () => {
const blob: Blob = await font.blob();
const buffer = await blob.arrayBuffer();
readyBuffer = buffer;
const realFontInfo = get_font_info(new Uint8Array(buffer));
console.log(realFontInfo);

db.put('font-information', {
fullName,
postscriptName,
info: realFontInfo,
});

return { buffer, idx };
})(),
);
},
};
}),
);
}

const t2 = performance.now();
console.log('addPartialFonts time used:', t2 - t);
}

/** @internal */
async function buildComponentInternal<T>(
options: Partial<InitOptions> | undefined,
gModule: LazyWasmModule,
builder: TypstCommonBuilder<T>,
hooks: ComponentBuildHooks,
): Promise<T> {
/// init typst wasm module
if (options?.getModule) {
await gModule.init(options.getModule());
class ComponentBuilder<T> {
async loadFont(builder: TypstCommonBuilder<T>, fontPath: string): Promise<void> {
const response = await fetch(fontPath);
const fontBuffer = new Uint8Array(await response.arrayBuffer());
await builder.add_raw_font(fontBuffer);
}

/// build typst component
const buildCtx: InitContext<T> = { ref: this, builder, hooks };
async build(
options: Partial<InitOptions> | undefined,
builder: TypstCommonBuilder<T>,
hooks: ComponentBuildHooks,
): Promise<T> {
/// build typst component
const buildCtx: InitContext<T> = { ref: this, builder, hooks };

for (const fn of options?.beforeBuild ?? []) {
await fn(undefined as unknown as BeforeBuildMark, buildCtx);
}
addPartialFonts(buildCtx);
for (const fn of options?.beforeBuild ?? []) {
await fn(undefined as unknown as BeforeBuildMark, buildCtx);
}
await addPartialFonts(buildCtx);

const component = await builder.build();
const component = await builder.build();

return component;
return component;
}
}

/** @internal */
Expand All @@ -68,10 +138,10 @@ export async function buildComponent<T>(
Builder: { new (): TypstCommonBuilder<T> },
hooks: ComponentBuildHooks,
): Promise<T> {
const builder = new Builder();
try {
return buildComponentInternal(options, gModule, builder, hooks);
} finally {
builder.free();
/// init typst wasm module
if (options?.getModule) {
await gModule.init(options.getModule());
}

return await new ComponentBuilder<T>().build(options, new Builder(), hooks);
}
6 changes: 0 additions & 6 deletions packages/typst.ts/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,6 @@ class TypstRendererDriver {

constructor(private pdf: typeof pdfjsModule) {}

async loadFont(builder: typst.TypstRendererBuilder, fontPath: string): Promise<void> {
const response = await fetch(fontPath);
const fontBuffer = new Uint8Array(await response.arrayBuffer());
await builder.add_raw_font(fontBuffer);
}

async init(options?: Partial<InitOptions>): Promise<void> {
this.renderer = await buildComponent(options, gRendererModule, typst.TypstRendererBuilder, {});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/typst.ts/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"strictNullChecks": true,
"declaration": true,
"stripInternal": true,
"lib": ["ES5", "ES6", "ES7"]
"lib": ["ES5", "ES6", "ES7", "ES2018"]
},
"include": ["src/**/*.ts"]
}
Loading

0 comments on commit d49a301

Please sign in to comment.