Skip to content

Commit

Permalink
now without ffi
Browse files Browse the repository at this point in the history
  • Loading branch information
deirn committed Feb 6, 2024
1 parent 7d383b7 commit a6bc0cb
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 77 deletions.
6 changes: 3 additions & 3 deletions cli/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ENTRYPOINT=main.ts
DENO_PERMISSIONS=--allow-ffi --allow-net --allow-env --allow-read --allow-write
DENO_PERMISSIONS=--allow-net --allow-env --allow-read --allow-write

run: fontgen
cd ../scripts; \
Expand All @@ -17,14 +17,14 @@ build: fontgen check
deno run --allow-env --allow-read --allow-net --allow-write bundle.ts

install: build
deno install --unstable $(DENO_PERMISSIONS) -f -n fabric bundled.ts
deno install $(DENO_PERMISSIONS) -f -n fabric bundled.ts

install-stable: build
deno install $(DENO_PERMISSIONS) -f -n fabric bundled.ts

init:
rm -rf ./test
deno run --unstable $(DENO_PERMISSIONS) $(ENTRYPOINT) init ./test
deno run $(DENO_PERMISSIONS) $(ENTRYPOINT) init ./test

init-stable:
rm -rf ./test
Expand Down
101 changes: 56 additions & 45 deletions cli/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import * as utils from "../utils.ts";
import { ensureDir } from "https://deno.land/[email protected]/fs/ensure_dir.ts";
import fontData from "../font.ts";
import { decodeBase64 } from "https://deno.land/[email protected]/encoding/base64.ts";
import * as png from "https://deno.land/x/[email protected]/mod.ts";
import * as pureimage from "npm:[email protected]";
// @deno-types="npm:@types/opentype.js"
import * as _opentype from "npm:[email protected]";

const canGenerateIcon = Object.hasOwn(Deno, "dlopen") &&
Deno.permissions.querySync({ name: "ffi" }).state == "granted";
const canGenerateIcon = true;

const error = colors.bold.red;
const progress = colors.bold.yellow;
Expand Down Expand Up @@ -47,10 +50,6 @@ const optionArg = {
hidden: true,
};

interface CanvasFacade extends generator.CanvasFactory {
registerFont?(data: Uint8Array, alias?: string): void;
}

interface PromptedConfiguration extends generator.Configuration {
generateIcon: boolean;
}
Expand Down Expand Up @@ -83,12 +82,19 @@ export function initCommand() {
)
.arguments("[dir:file]")
.action(async (options, dir: string | undefined) => {
await generate(options, dir);
const fontFile = await Deno.makeTempFile();

try {
await generate(options, fontFile, dir);
} finally {
await Deno.remove(fontFile);
}
});
}

async function generate(
cli: CliOptions,
fontFile: string,
outputDirName: string | undefined,
) {
const outputDir = await getAndPrepareOutputDir(outputDirName);
Expand All @@ -103,7 +109,9 @@ async function generate(
? defaultOptions(path.basename(outputDir))
: promptUser(path.basename(outputDir), cli));

const canvas = await resolveCanvasFacade(config.generateIcon);
await Deno.writeFile(fontFile, decodeBase64(fontData));
const fontLoader = pureimage.registerFont(fontFile, generator.ICON_FONT);
await fontLoader.load();

const options: generator.Options = {
config,
Expand All @@ -112,30 +120,48 @@ async function generate(
await writeFile(outputDir, contentPath, content, options);
},
},
canvas,
canvas: {
create(width, height) {
const bitmap = pureimage.make(width, height);

return {
getContext: (id) => bitmap.getContext(id),
getPng: () => {
const p = png.encode(bitmap.data, bitmap.width, bitmap.height);
return p;
},
measureText(ctx: pureimage.Context, text) {
const font = fontLoader.font;
const fontSize = ctx._font.size!;

let advance = 0;
let ascent = 0;
let descent = 0;

const glyphs = font.stringToGlyphs(text);

for (const glyph of glyphs) {
const metrics = glyph.getMetrics();
advance += glyph.advanceWidth!;
ascent = Math.max(ascent, metrics.yMax);
descent = Math.min(descent, metrics.yMin);
}

return {
width: (advance / font.unitsPerEm) * fontSize,
ascent: Math.abs((ascent / font.unitsPerEm) * fontSize),
descent: Math.abs((descent / font.unitsPerEm) * fontSize),
};
},
};
},
},
};

console.log(progress("Generating mod template..."));

const fontFile = await registerFont(canvas);

try {
await generator.generateTemplate(options);
console.log(success("Done!"));
} finally {
if (fontFile) await Deno.remove(fontFile);
}
}

async function registerFont(canvas: CanvasFacade): Promise<string | null> {
if (!canvas.registerFont) return null;

// Using the font data directly doesn't seem to work...
const fontFile = await Deno.makeTempFile();
await Deno.writeFile(fontFile, decodeBase64(fontData));
canvas.registerFont(await Deno.readFile(fontFile), generator.ICON_FONT);

return fontFile;
await generator.generateTemplate(options);
console.log(success("Done!"));
}

async function getAndPrepareOutputDir(
Expand All @@ -154,21 +180,6 @@ async function getAndPrepareOutputDir(
return outputDir;
}

async function resolveCanvasFacade(enabled: boolean): Promise<CanvasFacade> {
if (!enabled) {
return {
create: () => null,
};
}

const canvas = await import("https://deno.land/x/[email protected]/mod.ts");

return {
create: canvas.createCanvas,
registerFont: canvas.Fonts.register,
};
}

async function promptUser(
startingName: string,
cli: CliOptions,
Expand Down Expand Up @@ -343,7 +354,7 @@ async function writeFile(
};

// is there a cleaner way to do this?
if (content instanceof ArrayBuffer) {
if (content instanceof ArrayBuffer || content instanceof Uint8Array) {
const data = new Uint8Array(content);
await Deno.writeFile(output, data, writeOptions);
} else {
Expand Down Expand Up @@ -388,7 +399,7 @@ async function requestPermissions(outputDir: string) {
{
name: "net",
host: "maven.fabricmc.net",
}
},
];

for (const permission of permissions) {
Expand Down
4 changes: 0 additions & 4 deletions cli/commands/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,8 @@ class UpdateProvider extends Provider {
override async upgrade(
{}: UpgradeOptions,
): Promise<void> {
// Enable the unstable flag only if the current installation is unstable already
const unstable = Object.hasOwn(Deno, "dlopen") ? ["--unstable"] : [];

const args = [
"install",
...unstable,
"--force",
"--reload",
"--quiet",
Expand Down
13 changes: 10 additions & 3 deletions cli/fontgen.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { encodeBase64 } from "https://deno.land/[email protected]/encoding/base64.ts";

const font = await Deno.readFile("../assets/fonts/ComicRelief-Regular.woff2");
const base64 = encodeBase64(font)
// @deno-types="npm:@types/wawoff2"
import * as wawoff2 from "npm:[email protected]";

Deno.writeTextFileSync("./font.ts", `export default ${JSON.stringify(base64)};`);
const woff2 = await Deno.readFile("../assets/fonts/ComicRelief-Regular.woff2");
const woff = await wawoff2.decompress(woff2);
const base64 = encodeBase64(woff);

Deno.writeTextFileSync(
"./font.ts",
`export default ${JSON.stringify(base64)};`,
);
17 changes: 15 additions & 2 deletions scripts/src/lib/Template.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { ICON_FONT, getTemplateGameVersions } from "./template/template";
import { minecraftSupportsDataGen, minecraftSupportsSplitSources, computeCustomModIdErrors, sharedModIdChecks, formatPackageName, nameToModId} from "./template/minecraft";
import { computePackageNameErrors } from "./template/java"
import { decode64 } from "./template/utils";
let minecraftVersion: string;
let projectName = "Template Mod";
Expand Down Expand Up @@ -75,7 +76,19 @@
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
return canvas;
return {
getContext: (id) => canvas.getContext(id),
getPng: () => decode64(canvas.toDataURL().split(";base64,")[1]),
measureText(ctx: CanvasRenderingContext2D, text) {
const metrics = ctx.measureText(text);
return {
width: metrics.width,
ascent: metrics.actualBoundingBoxAscent,
descent: metrics.actualBoundingBoxDescent
}
}
};
},
}
});
Expand Down Expand Up @@ -124,7 +137,7 @@
<p>Choose a name for your new mod. The mod ID will be <code>{modid}</code>. <a href={""} on:click|preventDefault={useCustomModId}>Use custom id</a></p>
{/if}

<input id="project-name" bind:value={projectName} on:keyup={doFormatProjectName} />
<input id="project-name" bind:value={projectName} on:blur={doFormatProjectName} />

{#if modIdErrors != undefined}
{#each modIdErrors as error}
Expand Down
20 changes: 10 additions & 10 deletions scripts/src/lib/template/icon.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { ICON_FONT, type CanvasFactory, type CanvasLike } from "./template";
import { ICON_FONT, type CanvasAdaptorFactory, type CanvasAdaptor } from "./template";
import { decode64 } from "./utils";

const DEFAULT_ICON = "iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAAD///+l2Z/dAAABeklEQVRIx9XTsW1cMQwGYAoKIlfWbaAVUroKbxSPcBtIQRaTN9EILFkI+l3ovXuSLvYZCK4wK+IrWBD/T1iGHg41fBUcygwWKY7QLGiCuoLaNoOY+1BnKFfQDUhnyGeZIZ3lNEO+LFBef01gyutlAlvOEzRb6MIDVCskDAD01MEJCTeKjWgHI1wp1A3Ui9Wg5HSHoFaDkN1BgroOzwN4ORkJuQNXX738NsKFAULpEI2wdIhAdRLTCM1JzBIHsMJZokSAkK/ApQMAbMADhCycDzASsoQObwDwR31Sn154AFJHpwPqT6NENACZOgIambYBNrCYAOSQflAcIEDCmPXEDyrQ42GP9wBuBXuAMqBENxBneCGegVNYIPsFygrqFqh2gXYDZoG+j5BuIC6QVyh8D444aPgXBKi/B9XtUD+ANkH9CsAuNz4D/wH8/Rx4gPZIsP8DDlAeHrRBXUFXkCugQ/YLUJhBiCeg3o8JsICdAKm38OhtGio2zveBd37Jm8IEWUmfAAAAAElFTkSuQmCC";

export function generateModIcon(name: string, factory: CanvasFactory): ArrayBufferLike {
export function generateModIcon(name: string, factory: CanvasAdaptorFactory): ArrayBufferLike {
const canvas = factory.create(128, 128);

if (canvas != null && drawModIcon(canvas, name)) {
return decode64(canvas.toDataURL().split(";base64,")[1]);
return canvas.getPng();
} else {
return decode64(DEFAULT_ICON);
}
}

export function drawModIcon(canvas: CanvasLike, name: string): boolean {
export function drawModIcon(canvas: CanvasAdaptor, name: string): boolean {
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
if (ctx == null) return false;

ctx.rect(0, 0, 128, 128);
ctx.fillStyle = "#ffffff";
ctx.fill();
ctx.fillRect(0, 0, 128, 128);

const words = name.split(/\s+/);

Expand All @@ -44,8 +43,8 @@ export function drawModIcon(canvas: CanvasLike, name: string): boolean {
for (let i = 0; i < words.length; i++) {
const word = words[i];
ctx.font = `${fontSize}px ${ICON_FONT}`;
const metrics = ctx.measureText(word);
lineHeight[i] = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
const metrics = canvas.measureText(ctx, word);
lineHeight[i] = metrics.ascent + metrics.descent;
totalHeight += lineHeight[i];
}

Expand All @@ -65,10 +64,11 @@ export function drawModIcon(canvas: CanvasLike, name: string): boolean {
ctx.font = `${fontSize}px ${ICON_FONT}`;
ctx.fillStyle = "#000000";
ctx.textAlign = "center";
const metrics = ctx.measureText(word);
ctx.fillText(word, 64, textY + offset + metrics.actualBoundingBoxAscent);
const metrics = canvas.measureText(ctx, word);
ctx.fillText(word, 64, textY + offset + metrics.ascent);
}

console.log(fontSize)
return true;
}

Expand Down
8 changes: 4 additions & 4 deletions scripts/src/lib/template/modjson.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CanvasFactory, ComputedConfiguration, TemplateWriter } from "./template";
import type { CanvasAdaptorFactory, ComputedConfiguration, TemplateWriter } from "./template";
import { generateClientMixin, generateMixin } from "./mixin";
import { generateEntrypoint } from "./modentrypoint";
import { getJavaVersion } from "./java"
Expand All @@ -9,13 +9,13 @@ function usesNewModid(fabricVersion: string) : boolean {
return Number(fabricVersion.split(".")[1]) >= 59;
}

export async function addModJson(writer: TemplateWriter, canvas: CanvasFactory, config: ComputedConfiguration) {
var mixins = [
export async function addModJson(writer: TemplateWriter, canvas: CanvasAdaptorFactory, config: ComputedConfiguration) {
const mixins = [
...await generateMixin(writer, config),
...(config.splitSources ? await generateClientMixin(writer, config) : [])
];

var fabricModJson : any = {
const fabricModJson : any = {
"schemaVersion": 1,
"id": config.modid,
"version": "${version}",
Expand Down
19 changes: 13 additions & 6 deletions scripts/src/lib/template/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface Options {
* this might be directly to the filesystem or to a zip file.
*/
writer: TemplateWriter;
canvas: CanvasFactory;
canvas: CanvasAdaptorFactory;
}

export interface Configuration {
Expand Down Expand Up @@ -55,13 +55,20 @@ export interface TemplateWriter {
write(path: string, content: string | ArrayBufferLike, options?: FileOptions): Promise<void>
}

export interface CanvasFactory {
create(width: number, height: number): CanvasLike | null
export interface CanvasAdaptorFactory {
create(width: number, height: number): CanvasAdaptor | null
}

export interface CanvasLike {
getContext(contextId: "2d"): unknown,
toDataURL(): string
export interface CanvasAdaptor {
getContext(contextId: "2d"): unknown
getPng(): ArrayBufferLike
measureText(ctx: unknown, text: string): TextMetricsAdaptor
}

export interface TextMetricsAdaptor {
width: number
ascent: number
descent: number
}

export async function generateTemplate(options: Options) {
Expand Down

0 comments on commit a6bc0cb

Please sign in to comment.