From 0e0b07ceb0056dd82cb2cf779f121ea1cf7e04b5 Mon Sep 17 00:00:00 2001 From: deirn Date: Tue, 20 Feb 2024 05:40:24 +0700 Subject: [PATCH] Mod icon generator (#56) * mod icon generator * cli: bundle the font * cli: only generate icon if --unstable --allow-ffi * cli: only upgrade with --unstable if it's already unstable * include font license file * remove workspace file * figured out vscode settings to enable deno * put the font face declaration to template page * now without ffi * bundle with esbuild * inline constant * make it actually configurable * Remove unstable note --------- Co-authored-by: modmuss50 --- .devcontainer/devcontainer.json | 3 +- .vscode/settings.json | 1 + Dockerfile | 9 +- Dockerfile.dev | 2 +- assets/fonts/ComicRelief-Regular.license.txt | 93 +++++++++++++++++++ assets/fonts/ComicRelief-Regular.woff2 | Bin 0 -> 37216 bytes cli/.gitignore | 1 + cli/Makefile | 31 +++++-- cli/bundle.ts | 24 +++-- cli/commands/init.ts | 60 ++++++++++-- cli/fontgen.ts | 13 +++ cli/main.ts | 6 +- scripts/src/lib/Template.svelte | 40 +++++++- scripts/src/lib/template/icon.ts | 76 +++++++++++++++ scripts/src/lib/template/modjson.ts | 17 ++-- scripts/src/lib/template/template.ts | 24 ++++- 16 files changed, 356 insertions(+), 44 deletions(-) create mode 100755 assets/fonts/ComicRelief-Regular.license.txt create mode 100644 assets/fonts/ComicRelief-Regular.woff2 create mode 100644 cli/fontgen.ts create mode 100644 scripts/src/lib/template/icon.ts diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 565e704c..8b8ec136 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,8 @@ "vscode": { "extensions": [ "svelte.svelte-vscode", - "shadowtime2000.eta-vscode" + "shadowtime2000.eta-vscode", + "denoland.vscode-deno" ] } } diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f7b9831..440c7a0c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { "editor.insertSpaces": false, + "deno.enablePaths": ["./cli"] } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f978501a..ff9fa4f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,13 @@ -FROM node:18-bullseye +FROM node:18-bookworm ENV DEBIAN_FRONTEND=noninteractive ENV PATH="${PATH}:$HOME/gems/bin" ENV GEM_HOME="$HOME/gems" RUN apt update && apt install -y ruby-full build-essential zlib1g-dev -RUN gem install jekyll bundler \ No newline at end of file +RUN gem install jekyll bundler + +ENV DENO_INSTALL="$HOME/.deno" +ENV DENO_INSTALL_ROOT="$HOME/.deno" +RUN curl -fsSL https://deno.land/x/install/install.sh | sh +ENV PATH="${PATH}:$HOME/.deno/bin" \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev index 4b5490fa..85cef180 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:18-bullseye AS BUILD +FROM node:18-bookworm AS BUILD ENV DEBIAN_FRONTEND=noninteractive ENV PATH="${PATH}:$HOME/gems/bin" diff --git a/assets/fonts/ComicRelief-Regular.license.txt b/assets/fonts/ComicRelief-Regular.license.txt new file mode 100755 index 00000000..6b1741cf --- /dev/null +++ b/assets/fonts/ComicRelief-Regular.license.txt @@ -0,0 +1,93 @@ +Copyright 2013 Jeff Davis (https://github.com/loudifier/Comic-Relief) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/ComicRelief-Regular.woff2 b/assets/fonts/ComicRelief-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..49fe77ac264b5ba2d33053a720a8bb60c1777c36 GIT binary patch literal 37216 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 bitmap.getContext(id), + getPng: () => png.encode(bitmap.data, bitmap.width, bitmap.height), + 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...")); + await generator.generateTemplate(options); console.log(success("Done!")); } @@ -203,7 +248,7 @@ async function promptUser( const advancedOptions = cliOptions ?? await Checkbox.prompt({ message: "Advanced options", - options: getAdancedOptions(minecraftVersion), + options: getAdvancedOptions(minecraftVersion), }); return { @@ -214,6 +259,7 @@ async function promptUser( useKotlin: advancedOptions.includes(KOTLIN_ADVANCED_OPTION), dataGeneration: advancedOptions.includes(DATAGEN_ADVANCED_OPTION), splitSources: advancedOptions.includes(SPLIT_ADVANCED_OPTION), + uniqueModIcon: advancedOptions.includes(ICON_ADVANCED_OPTION), }; } @@ -249,13 +295,15 @@ async function defaultOptions( useKotlin: false, dataGeneration: false, splitSources: generator.minecraftSupportsSplitSources(minecraftVersion), + uniqueModIcon: true, }; } -function getAdancedOptions(minecraftVersion: string): CheckboxValueOptions { - const options: CheckboxValueOptions = [ - { value: KOTLIN_ADVANCED_OPTION }, - ]; +function getAdvancedOptions(minecraftVersion: string): CheckboxValueOptions { + const options: CheckboxValueOptions = []; + + options.push({ value: ICON_ADVANCED_OPTION, checked: true }); + options.push({ value: KOTLIN_ADVANCED_OPTION }); if (generator.minecraftSupportsDataGen(minecraftVersion)) { options.push({ @@ -287,7 +335,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 { diff --git a/cli/fontgen.ts b/cli/fontgen.ts new file mode 100644 index 00000000..afaa8eeb --- /dev/null +++ b/cli/fontgen.ts @@ -0,0 +1,13 @@ +import { encodeBase64 } from "https://deno.land/std@0.203.0/encoding/base64.ts"; + +// @deno-types="npm:@types/wawoff2" +import * as wawoff2 from "npm:wawoff2@2.0.1"; + +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)};`, +); diff --git a/cli/main.ts b/cli/main.ts index 1a01ae0d..a717bea9 100755 --- a/cli/main.ts +++ b/cli/main.ts @@ -10,6 +10,10 @@ import { import { initCommand } from "./commands/init.ts"; import { upgradeCommand } from "./commands/upgrade.ts"; +// Replaced by esbuild. +declare let __VERSION__: string; +const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "dev"; + // Set the XML parser as we do not have DomParser here. generator.setXmlVersionParser((xml) => { const document = parseXml(xml) as any; @@ -19,7 +23,7 @@ generator.setXmlVersionParser((xml) => { if (import.meta.main) { const cmd = new Command() .name("Fabric CLI tools") - .version("%__VERSION__%") // Replaced by bundle.ts + .version(VERSION) .description("A set of command line tools to aid Fabric mod development") .action(() => { // Show the help in the default command with no args. diff --git a/scripts/src/lib/Template.svelte b/scripts/src/lib/Template.svelte index 28fb2b05..f38d25b8 100644 --- a/scripts/src/lib/Template.svelte +++ b/scripts/src/lib/Template.svelte @@ -2,9 +2,10 @@ import JSZip from "jszip"; import FileSaver from "file-saver"; import DownloadIcon from "./DownloadIcon.svelte"; - import { getTemplateGameVersions } from "./template/template"; + import { ICON_FONT, getTemplateGameVersions, type Configuration } 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"; @@ -49,7 +50,7 @@ loading = true; const generator = await import("./template/template"); - const config = { + const config: Configuration = { modid: customModId ?? modid, minecraftVersion, projectName, @@ -57,6 +58,7 @@ useKotlin, dataGeneration: dataGeneration && supportsDataGen, splitSources: splitSources && supportsSplitSources, + uniqueModIcon: true }; const zip = new JSZip(); @@ -70,6 +72,26 @@ }); }, }, + canvas: { + create(width, height) { + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + 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 + } + } + }; + }, + } }); FileSaver.saveAs( @@ -98,7 +120,12 @@ {#await versions} -

Loading data..

+

+ Loading data + + + ... +

{:then data}
@@ -111,7 +138,7 @@

Choose a name for your new mod. The mod ID will be {modid}. Use custom id

{/if} - + {#if modIdErrors != undefined} {#each modIdErrors as error} @@ -237,6 +264,11 @@ {/await}