├── .gitattributes ├── .github └── workflows │ ├── build-ci.yml │ └── build-release.yml ├── .gitignore ├── CHANGES.md ├── LICENCE ├── README.md ├── build.gradle ├── docs └── generator.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java ├── mnm │ └── hdfontgen │ │ ├── FontGenerator.java │ │ ├── GeneratorWindow.java │ │ ├── IOUtils.java │ │ ├── Log.java │ │ ├── Main.java │ │ ├── SwingMain.java │ │ ├── TerminalMain.java │ │ └── pack │ │ ├── FontPack.java │ │ ├── HDFont.java │ │ ├── PackFormat.java │ │ ├── PackGenerator.java │ │ ├── PackJson.java │ │ ├── PackSettings.java │ │ ├── ResourcePath.java │ │ ├── TextureSize.java │ │ ├── ZipPath.java │ │ ├── generator │ │ ├── AbstractFontProviderPackGenerator.java │ │ ├── AbstractPackGenerator.java │ │ ├── FontProviderFontGenerator.java │ │ └── LegacyFontGenerator.java │ │ ├── provider │ │ ├── BitmapFontProvider.java │ │ ├── FontProvider.java │ │ ├── FontProvidersJson.java │ │ ├── LegacyUnicodeFontProvider.java │ │ ├── StandardFontProviders.java │ │ └── TrueTypeFontProvider.java │ │ └── resource │ │ ├── AbstractBitmapResource.java │ │ ├── AbstractJsonResource.java │ │ ├── BitmapFontResource.java │ │ ├── FileResource.java │ │ ├── GlyphSizesResource.java │ │ └── Resource.java └── module-info.java └── resources └── unifont-7.0.06.ttf /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bat text eol=crlf 2 | -------------------------------------------------------------------------------- /.github/workflows/build-ci.yml: -------------------------------------------------------------------------------- 1 | name: Build CI 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build-ci: 9 | name: Build CI 10 | runs-on: ubuntu-latest 11 | steps: 12 | - id: checkout-code 13 | name: Checkout code 14 | uses: actions/checkout@v2 15 | - id: setup-jdk 16 | name: Setup JDK 17 | uses: actions/setup-java@v1 18 | with: 19 | java-version: 17 20 | - id: check 21 | name: Build distribution 22 | run: | 23 | gradle -v 24 | gradle check -------------------------------------------------------------------------------- /.github/workflows/build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build Distributions 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | create-draft: 10 | name: Create Draft Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - id: draft-release 14 | name: Draft Release 15 | uses: softprops/action-gh-release@v1 16 | with: 17 | draft: true 18 | 19 | 20 | build-distributions: 21 | name: Build Distributions 22 | runs-on: ubuntu-latest 23 | steps: 24 | - id: checkout-code 25 | name: Checkout code 26 | uses: actions/checkout@v2 27 | with: 28 | fetch-depth: 0 29 | - id: setup-jdk 30 | name: Setup JDK 31 | uses: actions/setup-java@v1 32 | with: 33 | java-version: 17 34 | - id: build 35 | name: Build distribution 36 | run: | 37 | gradle -v 38 | gradle build 39 | - id: upload-distributions 40 | name: Upload Distributions 41 | uses: softprops/action-gh-release@v1 42 | with: 43 | files: ./build/distributions/* 44 | draft: true 45 | 46 | build-packages: 47 | name: Build System Packages on ${{ matrix.os }} 48 | runs-on: ${{ matrix.os }} 49 | strategy: 50 | matrix: 51 | os: 52 | - windows-latest 53 | - ubuntu-latest 54 | - macos-latest 55 | steps: 56 | - id: checkout-code 57 | name: Checkout code 58 | uses: actions/checkout@v2 59 | - name: Fetch Tags 60 | run: git fetch --unshallow --tags 61 | - id: setup-jdk 62 | name: Setup JDK 63 | uses: actions/setup-java@v1 64 | with: 65 | java-version: 17 66 | - id: jpackage 67 | name: Run jpackage 68 | run: | 69 | gradle -v 70 | gradle jpackageDist 71 | - id: upload-distributions 72 | name: Upload Distributions 73 | uses: softprops/action-gh-release@v1 74 | with: 75 | files: ./build/distributions/* 76 | draft: true 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | .settings/ 4 | .gradle/ 5 | .project 6 | .classpath 7 | .idea/ 8 | 9 | *.zip 10 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | ## Unreleased 4 | 5 | - 6 | 7 | ## 1.3.1 8 | 9 | - Implement generation of glyph_sizes.bin when using legacy unicode bitmaps 10 | 11 | ## 1.3.0 12 | 13 | - Complete rewrite to support modern Minecraft versions 14 | - Legacy support via `pack_format: 1-3` (MC 1.6.1-1.12.2) 15 | - Font providers via `pack_format: 4` (MC 1.13+) 16 | - TrueType fonts via `pack-format: 5` (MC 1.15+) 17 | - Named providers via `pack_format: 6` (MC 1.16+) 18 | - Update Java to 17 19 | - Java is now bundled, so it does not need to be installed separately 20 | - Build package using `./gradlew jpackageDist`, zip will be in 21 | `build/distributions` 22 | - Use gson to generate json files 23 | 24 | ## 1.2.0 25 | 26 | - Use [GNU Unifont](https://unifoundry.com/unifont/index.html) as fallback font, same as Minecraft 27 | - Decrease font size for larger fonts -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HD Font Generator 2 | 3 | This tool creates HD font resource packs using installed fonts on the system. If 4 | it's supported and installed in your computer, you can use it. 5 | 6 | To use, select the font and texture size from the dropdown boxes and click 7 | Create. A zip named using the font name and the size will be created in the 8 | directory it was started from. Add this file as a resource pack. 9 | 10 | For command line usage, just give it a font name with spaces replaced with 11 | underscores, then the texture. A nonexistent font will use the JRE default 12 | (dialog.plain). 13 | 14 | ![The generator window](docs/generator.png) 15 | 16 | Note: Every font is different, so some may look weird and require tweaking after 17 | generation, but most fonts should be fine. 18 | 19 | ## Download 20 | 21 | The generator can be downloaded from the [Release](https://github.com/killjoy1221/HD-Font-Generator/releases/latest) 22 | page. Pick the file for your operating system. 23 | 24 | Extract the zip or tar file to run the executable. 25 | 26 | ## Options 27 | 28 | Different options can be customized before generation. These include: 29 | 30 | - **Font**: The font family to use, pre-populated from system fonts. 31 | - **Texture Size**: The size of the texture. Limited to one of x32, x64, x128, 32 | x256, x512. Higher resolution textures will take longer to generate 33 | - **Pack Format**: The minecraft version range to generate the font for. 34 | Supports formats 1 through 7. 35 | - **Unicode**: (legacy), Generates the full extended unicode, which consists of 36 | 256 pages, each with 256 characters. Fallback font uses the 37 | [gnu uniform font](http://unifoundry.com/unifont/). 38 | - **Parallel**: Uses multiple threads to generate font bitmaps in parallel. 39 | 40 | ## Development 41 | 42 | Note: The project uses JDK 17. 43 | 44 | ### Gradle Tasks 45 | 46 | The following Gradle tasks can be used to run or build the project. 47 | 48 | - `./gradlew run` - Runs the project with gradle 49 | - `./gradlew build` - Build the project and package dependencies with a launch script. This is useful if you have the JDK installed already, though you have to extract it manually. 50 | - `./gradlew jPackageDist` - Create a platform-specific archive which bundles the JRE. The unarchived build files are located in `build/jpackage`. 51 | 52 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | id "org.ajoberstar.reckon" version "0.16.1" 4 | id 'org.beryx.jlink' version '2.25.0' 5 | } 6 | 7 | java { 8 | toolchain { 9 | languageVersion = JavaLanguageVersion.of(17) 10 | } 11 | } 12 | 13 | application { 14 | // Define the main class for the application. 15 | mainModule = 'hdfontgen' 16 | mainClass = 'mnm.hdfontgen.Main' 17 | } 18 | 19 | // append the java version to the dist archives 20 | [distTar, distZip]*.configure{ 21 | archiveClassifier = "java-${java.toolchain.languageVersion.get()}" 22 | } 23 | distTar.compression = "GZIP" 24 | 25 | reckon { 26 | stages('build', 'final') 27 | 28 | defaultInferredScope = "patch" 29 | scopeCalc = calcScopeFromProp() | calcScopeFromCommitMessages() 30 | stageCalc = calcStageFromProp() 31 | } 32 | 33 | jlink { 34 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] 35 | launcher { 36 | name = 'hdfontgen' 37 | noConsole = true 38 | } 39 | 40 | jpackage { 41 | appVersion = reckon.version.get().normal.toString() 42 | skipInstaller = true 43 | } 44 | } 45 | def platform = System.properties["os.name"].split(" ")[0].toLowerCase() 46 | [Zip, Tar].each{type -> 47 | task("jpackage${type.simpleName}", type: type, dependsOn: "jpackage") { 48 | destinationDirectory = new File(buildDir, "distributions") 49 | archiveClassifier = platform 50 | 51 | from jlink.jpackageData.get().imageOutputDir 52 | 53 | if (type === Tar) { 54 | compression = "GZIP" 55 | } 56 | } 57 | } 58 | 59 | 60 | task jpackageDist { 61 | dependsOn platform == "linux" ? jpackageTar : jpackageZip 62 | } 63 | 64 | repositories { 65 | mavenCentral() 66 | } 67 | 68 | dependencies { 69 | implementation 'com.google.code.gson:gson:2.9.0' 70 | } 71 | -------------------------------------------------------------------------------- /docs/generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmess1221/HD-Font-Generator/df4e42f21962d5cdf97617e76e0d954e61bae583/docs/generator.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmess1221/HD-Font-Generator/df4e42f21962d5cdf97617e76e0d954e61bae583/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'hdfontgen' 2 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/FontGenerator.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | import java.io.IOException; 4 | import java.io.UncheckedIOException; 5 | 6 | import mnm.hdfontgen.pack.PackGenerator; 7 | import mnm.hdfontgen.pack.PackSettings; 8 | 9 | public class FontGenerator { 10 | 11 | public static void generate(PackSettings settings, boolean parallel) throws UncheckedIOException, IOException { 12 | PackGenerator generator = settings.createGenerator(); 13 | var pack = generator.generate(); 14 | var filename = String.format("%s.zip", settings.description); 15 | Log.log("Rendering pages"); 16 | pack.writeTo(filename, parallel); 17 | Log.log("Generated font at %s", filename); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/GeneratorWindow.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | import java.awt.*; 4 | import java.io.IOException; 5 | import java.io.UncheckedIOException; 6 | 7 | import javax.swing.*; 8 | import javax.swing.border.BevelBorder; 9 | import javax.swing.border.TitledBorder; 10 | 11 | import mnm.hdfontgen.pack.PackFormat; 12 | import mnm.hdfontgen.pack.PackSettings; 13 | import mnm.hdfontgen.pack.TextureSize; 14 | import mnm.hdfontgen.pack.provider.FontProvidersJson; 15 | 16 | /* 17 | * Mostly generated using Window Builder Pro 18 | */ 19 | public class GeneratorWindow { 20 | 21 | public static GeneratorWindow instance; 22 | private Thread thread; 23 | 24 | JFrame frmHdFontGenerator; 25 | private JComboBox choiceFont; 26 | private JComboBox comboBox; 27 | private JComboBox choiceFormat; 28 | private JLabel lblDisplay; 29 | private JLabel lblStatus; 30 | private JCheckBox checkboxUnicode; 31 | private JButton btnCreate; 32 | private JCheckBox checkboxParallel; 33 | 34 | public GeneratorWindow() { 35 | initialize(); 36 | } 37 | 38 | private static JComboBox createComboBox(JPanel panel, String labelString, T[] items, int y) { 39 | 40 | var label = new JLabel(labelString); 41 | var gbc_label = new GridBagConstraints(); 42 | gbc_label.insets = new Insets(0, 0, 5, 5); 43 | gbc_label.gridx = 1; 44 | gbc_label.gridy = y; 45 | panel.add(label, gbc_label); 46 | 47 | JComboBox comboBox = new JComboBox<>(); 48 | comboBox.setModel(new DefaultComboBoxModel<>(items)); 49 | var gbc_comboBox = new GridBagConstraints(); 50 | gbc_comboBox.insets = new Insets(0, 0, 5, 5); 51 | gbc_comboBox.fill = GridBagConstraints.HORIZONTAL; 52 | gbc_comboBox.gridx = 2; 53 | gbc_comboBox.gridy = y; 54 | panel.add(comboBox, gbc_comboBox); 55 | 56 | return comboBox; 57 | } 58 | 59 | private static JCheckBox createCheckbox(JPanel panel, String labelString, int y) { 60 | 61 | var checkbox = new JCheckBox(labelString); 62 | var gbc_checkbox = new GridBagConstraints(); 63 | gbc_checkbox.gridwidth = 2; 64 | gbc_checkbox.insets = new Insets(0, 0, 5, 5); 65 | gbc_checkbox.gridx = 1; 66 | gbc_checkbox.gridy = y; 67 | panel.add(checkbox, gbc_checkbox); 68 | return checkbox; 69 | } 70 | 71 | private void initialize() { 72 | try { 73 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 74 | } catch (ReflectiveOperationException | UnsupportedLookAndFeelException e) { 75 | e.printStackTrace(); 76 | } 77 | frmHdFontGenerator = new JFrame(); 78 | frmHdFontGenerator.setTitle("HD Font Generator"); 79 | frmHdFontGenerator.setBounds(100, 100, 230, 322); 80 | frmHdFontGenerator.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 81 | frmHdFontGenerator.getContentPane().setLayout(new BorderLayout(0, 0)); 82 | 83 | var panel = new JPanel(); 84 | panel.setBorder(new TitledBorder(null, "Font settings", TitledBorder.LEADING, TitledBorder.TOP, null, null)); 85 | frmHdFontGenerator.getContentPane().add(panel, BorderLayout.CENTER); 86 | var gbl_panel = new GridBagLayout(); 87 | gbl_panel.columnWidths = new int[]{25, 117, 81, 25}; 88 | gbl_panel.rowHeights = new int[]{85, 0, 0, 0, 0, 0, 32, 0}; 89 | gbl_panel.columnWeights = new double[]{0.0, 1.0, 1.0, 0.0}; 90 | gbl_panel.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, Double.MIN_VALUE}; 91 | panel.setLayout(gbl_panel); 92 | 93 | lblDisplay = new JLabel("AaBbCcDdEe"); 94 | var gbc_lblDisplay = new GridBagConstraints(); 95 | gbc_lblDisplay.fill = GridBagConstraints.VERTICAL; 96 | gbc_lblDisplay.gridwidth = 4; 97 | gbc_lblDisplay.insets = new Insets(0, 0, 5, 0); 98 | gbc_lblDisplay.gridx = 0; 99 | gbc_lblDisplay.gridy = 0; 100 | panel.add(lblDisplay, gbc_lblDisplay); 101 | 102 | var y = 0; 103 | 104 | var lblFont = new JLabel("Font:"); 105 | var gbc_lblFont = new GridBagConstraints(); 106 | gbc_lblFont.anchor = GridBagConstraints.WEST; 107 | gbc_lblFont.insets = new Insets(0, 0, 5, 5); 108 | gbc_lblFont.gridx = 1; 109 | gbc_lblFont.gridy = ++y; 110 | panel.add(lblFont, gbc_lblFont); 111 | 112 | choiceFont = new JComboBox<>(getInstalledFonts()); 113 | choiceFont.addItemListener(e -> onSetFont()); 114 | onSetFont(); 115 | var gbc_choice = new GridBagConstraints(); 116 | gbc_choice.fill = GridBagConstraints.BOTH; 117 | gbc_choice.gridwidth = 2; 118 | gbc_choice.insets = new Insets(0, 0, 5, 5); 119 | gbc_choice.gridx = 1; 120 | gbc_choice.gridy = ++y; 121 | panel.add(choiceFont, gbc_choice); 122 | 123 | comboBox = createComboBox(panel, "Texture Size:", TextureSize.values(), ++y); 124 | choiceFormat = createComboBox(panel, "Pack Format:", PackFormat.values(), ++y); 125 | choiceFormat.setSelectedItem(PackFormat.LATEST); 126 | 127 | checkboxUnicode = createCheckbox(panel, "Unicode (Legacy)", ++y); 128 | checkboxParallel = createCheckbox(panel, "Parallel Bitmaps", ++y); 129 | 130 | btnCreate = new JButton("Create"); 131 | btnCreate.addActionListener(e -> this.startThread()); 132 | var gbc_btnCreate = new GridBagConstraints(); 133 | gbc_btnCreate.insets = new Insets(0, 0, 0, 5); 134 | gbc_btnCreate.gridwidth = 2; 135 | gbc_btnCreate.gridx = 1; 136 | gbc_btnCreate.gridy = ++y; 137 | panel.add(btnCreate, gbc_btnCreate); 138 | 139 | lblStatus = new JLabel("Started"); 140 | lblStatus.setBorder(new BevelBorder(BevelBorder.LOWERED)); 141 | frmHdFontGenerator.getContentPane().add(lblStatus, BorderLayout.SOUTH); 142 | 143 | frmHdFontGenerator.pack(); 144 | 145 | Log.addLogger(lblStatus::setText); 146 | } 147 | 148 | private String[] getInstalledFonts() { 149 | return GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); 150 | } 151 | 152 | private void onSetFont() { 153 | var f = getChoiceFont(); 154 | lblDisplay.setFont(f.deriveFont(Font.PLAIN, 24)); 155 | } 156 | 157 | private Font getChoiceFont() { 158 | return Font.decode((String) choiceFont.getSelectedItem()); 159 | } 160 | 161 | private TextureSize getTextureSize() { 162 | return (TextureSize) comboBox.getSelectedItem(); 163 | } 164 | 165 | private PackFormat getPackFormat() { 166 | return (PackFormat) choiceFormat.getSelectedItem(); 167 | } 168 | 169 | private void startThread() { 170 | if (thread == null || !thread.isAlive()) { 171 | thread = new Thread(this::run); 172 | thread.start(); 173 | } 174 | } 175 | 176 | private void run() { 177 | lblStatus.setText("Working..."); 178 | btnCreate.setEnabled(false); 179 | 180 | var settings = new PackSettings.Builder(getPackFormat()) 181 | .bitmap(FontProvidersJson.DEFAULT_NAME, b -> b 182 | .withFont(getChoiceFont()) 183 | .withSize(getTextureSize()) 184 | .withUnicode(checkboxUnicode.isSelected()) 185 | ).build(); 186 | 187 | var parallel = checkboxParallel.isSelected(); 188 | 189 | try { 190 | FontGenerator.generate(settings, parallel); 191 | } catch (UncheckedIOException | IOException e) { 192 | e.printStackTrace(); 193 | lblStatus.setText("An error has occurred.!"); 194 | // TODO create log file so users can check the log 195 | JOptionPane.showMessageDialog(this.frmHdFontGenerator, "An error has occurred.\nCheck the log for details.", "Error!", JOptionPane.ERROR_MESSAGE); 196 | } finally { 197 | btnCreate.setEnabled(true); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/IOUtils.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | import java.io.IOException; 4 | import java.io.UncheckedIOException; 5 | import java.util.function.Consumer; 6 | 7 | public class IOUtils { 8 | 9 | public static Consumer consume(IOConsumer consumer) { 10 | return obj -> { 11 | try { 12 | consumer.accept(obj); 13 | } catch (IOException e) { 14 | throw new UncheckedIOException(e); 15 | } 16 | }; 17 | } 18 | 19 | public interface IOConsumer { 20 | void accept(T obj) throws IOException; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/Log.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Log { 7 | private static final List loggers = new ArrayList<>(); 8 | 9 | public static void addLogger(ILogger logger) { 10 | loggers.add(logger); 11 | } 12 | 13 | public static void log(String msg, Object... args) { 14 | String formattedMessage = String.format(msg, args); 15 | loggers.forEach(log -> log.log(formattedMessage)); 16 | } 17 | 18 | public interface ILogger { 19 | void log(String msg); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/Main.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | public class Main { 4 | 5 | public static void main(String[] args) { 6 | if (args.length == 0) { 7 | // open the gui 8 | SwingMain.main(); 9 | } else { 10 | TerminalMain.main(args); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/SwingMain.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | import java.awt.*; 4 | import java.io.PrintWriter; 5 | import java.io.StringWriter; 6 | 7 | import javax.swing.*; 8 | 9 | public class SwingMain implements Runnable { 10 | 11 | @Override 12 | public void run() { 13 | try { 14 | var window = GeneratorWindow.instance = new GeneratorWindow(); 15 | window.frmHdFontGenerator.setVisible(true); 16 | } catch (HeadlessException e) { 17 | e.printStackTrace(); 18 | } catch (Exception e) { 19 | e.printStackTrace(); 20 | 21 | var sw = new StringWriter(); 22 | var pw = new PrintWriter(sw); 23 | e.printStackTrace(pw); 24 | JOptionPane.showMessageDialog(null, sw.toString(), "Error!", JOptionPane.ERROR_MESSAGE); 25 | } 26 | } 27 | 28 | public static void main(String[] args) { 29 | main(); 30 | } 31 | 32 | public static void main() { 33 | EventQueue.invokeLater(new SwingMain()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/TerminalMain.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen; 2 | 3 | import java.io.IOException; 4 | import java.io.UncheckedIOException; 5 | import java.nio.file.Paths; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.Objects; 10 | import java.util.Optional; 11 | import java.util.function.Function; 12 | import java.util.stream.Collectors; 13 | 14 | import mnm.hdfontgen.pack.PackFormat; 15 | import mnm.hdfontgen.pack.PackSettings; 16 | import mnm.hdfontgen.pack.TextureSize; 17 | import mnm.hdfontgen.pack.provider.FontProvidersJson; 18 | 19 | public class TerminalMain { 20 | 21 | private static boolean quiet; 22 | 23 | static { 24 | Log.addLogger(msg -> { 25 | if (!quiet) { 26 | System.out.println(msg); 27 | } 28 | }); 29 | } 30 | 31 | public static void main(String[] args) throws SystemExit { 32 | try { 33 | var options = parseOptions(args); 34 | if (options.containsKey("help")) { 35 | throw printUsage(); 36 | } 37 | if (options.containsKey("quiet")) { 38 | quiet = true; 39 | } 40 | 41 | var settings = parseSettings(options); 42 | 43 | FontGenerator.generate(settings, options.containsKey("parallel")); 44 | } catch (IOException | UncheckedIOException e) { 45 | throw new RuntimeException(e); 46 | } catch (SystemExit e) { 47 | e.exit(); 48 | } 49 | } 50 | 51 | private static PackSettings parseSettings(Map options) throws SystemExit { 52 | var format = parsePackFormat(requireOption(options, "format")); 53 | var builder = new PackSettings.Builder(format); 54 | if (options.containsKey("description")) { 55 | builder = builder.withDescription(options.get("description")); 56 | } 57 | var type = requireOption(options, "type"); 58 | return (switch (type) { 59 | case "bitmap" -> builder.bitmap(FontProvidersJson.DEFAULT_NAME, b -> b 60 | .withFont(requireOption(options, "font")) 61 | .withSize(parseTextureSize(options.getOrDefault("size", TextureSize.x32.name()))) 62 | .withUnicode(options.containsKey("unicode")) 63 | ); 64 | case "truetype" -> builder.trueType(FontProvidersJson.DEFAULT_NAME, b -> b 65 | .withFont(Paths.get(requireOption(options, "font"))) 66 | .withOversample(Float.parseFloat(options.getOrDefault("oversample", "1"))) 67 | ); 68 | default -> throw printUnsupportedOption("type", type, "bitmap", "truetype"); 69 | }).build(); 70 | // return builder.build(); 71 | } 72 | 73 | private static String requireOption(Map options, String key) throws SystemExit { 74 | var value = options.get(key); 75 | if (value == null) { 76 | throw printMissingOption(key); 77 | } 78 | return value; 79 | } 80 | 81 | /** 82 | * Parses long options and returns them as a map. 83 | */ 84 | private static Map parseOptions(String[] args) { 85 | var map = new HashMap(); 86 | for (var i = 0; i < args.length; i++) { 87 | var key = args[i]; 88 | if (!key.startsWith("--")) { 89 | throw new IllegalArgumentException("unexpected non-option argument " + key); 90 | } 91 | key = key.substring(2); 92 | String value; 93 | if (key.contains("=")) { 94 | int index = key.indexOf("="); 95 | value = key.substring(index + 1); 96 | key = key.substring(0, index); 97 | } else { 98 | if (i + 1 == args.length || args[i + 1].startsWith("--")) { 99 | value = ""; 100 | } else { 101 | value = args[i + 1]; 102 | } 103 | i++; 104 | } 105 | map.put(key, value); 106 | } 107 | return map; 108 | } 109 | 110 | private static PackFormat parsePackFormat(String format) throws SystemExit { 111 | return parseEnum(PackFormat.class, format, PackFormat::getFormat) 112 | .orElseThrow(() -> printFormats(format)); 113 | } 114 | 115 | private static TextureSize parseTextureSize(String size) throws SystemExit { 116 | return parseEnum(TextureSize.class, size, TextureSize::getTextureSize) 117 | .orElseThrow(() -> printSizes(size)); 118 | } 119 | 120 | private static > Optional parseEnum(Class enumClass, String value, Function func) { 121 | for (var en : enumClass.getEnumConstants()) { 122 | if (Objects.toString(func.apply(en)).equals(value) || en.name().equals(value)) { 123 | return Optional.of(en); 124 | } 125 | } 126 | return Optional.empty(); 127 | } 128 | 129 | private static SystemExit printMissingOption(String option) { 130 | return new SystemExit(2, 131 | "Option --" + option + " is missing and is required." 132 | ); 133 | } 134 | 135 | private static SystemExit printUnsupportedOption(String option, String value, String... values) { 136 | var supported = String.join(", ", values); 137 | return new SystemExit(2, 138 | value + " is not a supported " + option + ".", 139 | "Supported values are: " + supported 140 | ); 141 | } 142 | 143 | private static SystemExit printFormats(String format) { 144 | var supported = Arrays.stream(PackFormat.values()) 145 | .map(PackFormat::getFormat) 146 | .map(Objects::toString) 147 | .toArray(String[]::new); 148 | return printUnsupportedOption("pack format", format, supported); 149 | } 150 | 151 | private static SystemExit printSizes(String size) { 152 | var supported = Arrays.stream(TextureSize.values()) 153 | .map(TextureSize::getTextureSize) 154 | .map(Objects::toString) 155 | .toArray(String[]::new); 156 | return printUnsupportedOption("texture size", size, supported); 157 | } 158 | 159 | private static SystemExit printUsage() { 160 | return new SystemExit(0, 161 | "Command Line Usage", 162 | "Options:", 163 | " --type The type of font to generate (bitmap, truetype)", 164 | " --format The pack format to use", 165 | " --description The description to use for the pack", 166 | " --quiet Disable log output", 167 | " --help Displays this help text and exits", 168 | "Bitmap Options:", 169 | " --font The name of the font registered in the system to use", 170 | " --size The size of font to use " + Arrays.stream(TextureSize.values()).map(Object::toString).collect(Collectors.joining(", ", "(", ")")), 171 | " --unicode Generates unicode pages", 172 | "TrueType Options:", 173 | " --font The file path to a ttf or otf file", 174 | " --oversample A decimal number specifying the resolution multiplier of the font" 175 | ); 176 | } 177 | 178 | private static class SystemExit extends RuntimeException { 179 | private final int code; 180 | 181 | SystemExit(int code, String message) { 182 | super(message); 183 | this.code = code; 184 | } 185 | 186 | SystemExit(int code, String... message) { 187 | this(code, String.join("\n", message)); 188 | } 189 | 190 | void exit() { 191 | System.out.println(this.getMessage()); 192 | System.exit(this.code); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/FontPack.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | import java.io.IOException; 4 | import java.io.UncheckedIOException; 5 | import java.net.URI; 6 | import java.nio.file.FileSystem; 7 | import java.nio.file.FileSystems; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.stream.Collectors; 17 | 18 | import mnm.hdfontgen.IOUtils; 19 | import mnm.hdfontgen.Log; 20 | import mnm.hdfontgen.pack.resource.Resource; 21 | 22 | public class FontPack { 23 | 24 | private final List resources = new ArrayList<>(); 25 | 26 | public void addResource(Resource resource) { 27 | this.resources.add(resource); 28 | } 29 | 30 | public void addResources(Collection resources) { 31 | this.resources.addAll(resources); 32 | } 33 | 34 | public void writeTo(FileSystem zipFs, boolean parallel) throws UncheckedIOException { 35 | // write all the pages 36 | this.resources.stream() 37 | .collect(Collectors.groupingBy(Resource::order)) 38 | .entrySet().stream() 39 | .sorted(Map.Entry.comparingByKey()) 40 | .map(Map.Entry::getValue) 41 | .forEach(resources -> { 42 | 43 | var stream = resources.stream(); 44 | if (parallel) { 45 | stream = stream.parallel(); 46 | } 47 | stream.forEach(IOUtils.consume(page -> { 48 | Path pagePath = zipFs.getPath(page.getPath().getFileLocation()); 49 | Log.log("Writing %s", pagePath.getFileName()); 50 | if (pagePath.getParent() != null) { 51 | Files.createDirectories(pagePath.getParent()); 52 | } 53 | page.writeTo(pagePath); 54 | })); 55 | }); 56 | } 57 | 58 | public void writeTo(String filename, boolean parallel) throws IOException, UncheckedIOException { 59 | var file = Paths.get(filename); 60 | if (Files.exists(file)) { 61 | Files.delete(file); 62 | } 63 | var uri = URI.create("jar:" + file.toUri()); 64 | var env = new HashMap(); 65 | env.put("create", "true"); 66 | 67 | try (var fs = FileSystems.newFileSystem(uri, env)) { 68 | this.writeTo(fs, parallel); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/HDFont.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | import java.awt.*; 4 | import java.awt.font.FontRenderContext; 5 | import java.awt.geom.Rectangle2D; 6 | import java.awt.image.BufferedImage; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.util.Objects; 10 | 11 | public class HDFont { 12 | 13 | private static final Font fallbackFont = loadDefaultFont(); 14 | 15 | private final Font font; 16 | private final TextureSize size; 17 | 18 | public HDFont(Font font, TextureSize size) { 19 | this.font = font; 20 | this.size = size; 21 | } 22 | 23 | public record RenderResult(BufferedImage image, byte[] sizes) { 24 | } 25 | 26 | public RenderResult render(String[] chars) { 27 | var sizes = new ByteArrayOutputStream(); 28 | int size = this.size.getTextureSize() / 2; 29 | int yOffset = size - size / 4; 30 | var image = new BufferedImage(size * chars[0].length(), size * chars.length, BufferedImage.TYPE_INT_ARGB); 31 | var graphics = (Graphics2D) image.getGraphics(); 32 | for (int y = 0; y < chars.length; y++) { 33 | for (int x = 0; x < chars[y].length(); x++) { 34 | char charToRender = chars[y].charAt(x); 35 | if (charToRender == '\0' && x != 0 && y != 0) { 36 | continue; 37 | } 38 | int xPos = x * size; 39 | int yPos = y * size; 40 | var font = this.getFontForChar(charToRender); 41 | graphics.setClip(xPos, yPos, size, size); 42 | graphics.setFont(font); 43 | graphics.drawChars(new char[]{charToRender}, 0, 1, xPos, yPos + yOffset); 44 | 45 | var wdth = getCharBounds(font, charToRender).getWidth(); 46 | var data = (int) Math.ceil(wdth / size * 0xf); 47 | sizes.write(data); 48 | } 49 | } 50 | graphics.dispose(); 51 | return new RenderResult(image, sizes.toByteArray()); 52 | } 53 | 54 | private Font getFontForChar(char ch) { 55 | final int size = this.size.getTextureSize() / 2; 56 | var f = (font.canDisplay(ch) ? font : fallbackFont).deriveFont(Font.PLAIN, size); 57 | f = f.deriveFont(Font.PLAIN, size); 58 | 59 | // decrease font size for large fonts. 60 | int s = size; 61 | 62 | Rectangle2D bounds; 63 | while ((bounds = getCharBounds(f, ch)) != null && (bounds.getHeight() > size || bounds.getWidth() > size)) { 64 | f = f.deriveFont(Font.PLAIN, --s); 65 | } 66 | return f; 67 | } 68 | 69 | private static Rectangle2D getCharBounds(Font f, char c) { 70 | var context = new FontRenderContext(f.getTransform(), false, false); 71 | return f.getStringBounds(new char[]{c}, 0, 1, context); 72 | } 73 | 74 | private static Font loadDefaultFont() { 75 | try (var in = HDFont.class.getResourceAsStream("/unifont-7.0.06.ttf")) { 76 | return Font.createFont(Font.TRUETYPE_FONT, Objects.requireNonNull(in)); 77 | } catch (FontFormatException | IOException e) { 78 | throw new RuntimeException("Unable to read Unifont fallback font", e); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/PackFormat.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | /** 4 | * The format version of a resource pack. 5 | */ 6 | public enum PackFormat { 7 | // legacy formats 8 | V1(1, "1.6.1", "1.8.9"), 9 | V2(2, "1.9", "1.10.2"), 10 | V3(3, "1.11", "1.12.2"), 11 | // supports font providers 12 | V4(4, "1.13", "1.14.4"), 13 | // supports truetype fonts 14 | V5(5, "1.15", "1.16.1"), 15 | // supports named fonts 16 | V6(6, "1.16.2", "1.16.5"), 17 | V7(7, "1.17", null), 18 | ; 19 | 20 | public static final PackFormat LATEST = V7; 21 | 22 | private final int format; 23 | private final String minVersion; 24 | private final String maxVersion; 25 | 26 | PackFormat(int format, String minVersion, String maxVersion) { 27 | this.format = format; 28 | this.minVersion = minVersion; 29 | this.maxVersion = maxVersion; 30 | } 31 | 32 | /** 33 | * Gets the format number of this PackFormat. 34 | * 35 | * @return The format number 36 | */ 37 | public int getFormat() { 38 | return format; 39 | } 40 | 41 | /** 42 | * Gets the version range supported by this PackFormat. If the latest 43 | * version as of release 44 | * 45 | * @return The string representation of the version range 46 | */ 47 | public String getVersionRange() { 48 | if (maxVersion == null) { 49 | return String.format("%s+", minVersion); 50 | } 51 | return String.format("%s-%s", minVersion, maxVersion); 52 | } 53 | 54 | /** 55 | * Returns the user friendly version range supported by this format. 56 | * 57 | * @return The user friendly version range 58 | */ 59 | @Override 60 | public String toString() { 61 | if (maxVersion == null) { 62 | return String.format("MC %s+", minVersion); 63 | } 64 | return String.format("MC %s - %s", minVersion, maxVersion); 65 | } 66 | 67 | /** 68 | * Returns true if this format supports font providers. Font providers can 69 | * be used to customize how a font is rendered, the location of the font 70 | * resources, and the characters supported by it. 71 | * 72 | * @return true if font provders are supported 73 | */ 74 | public boolean supportsFontProviders() { 75 | return this.format >= V4.format; 76 | } 77 | 78 | /** 79 | * Returns true if this format supports true type fonts. 80 | * 81 | * @return true if true type fonts are supported 82 | */ 83 | public boolean supportsTrueTypeFonts() { 84 | return this.format >= V5.format; 85 | } 86 | 87 | /** 88 | * Returns true if this format supports named fonts. Named fonts can be 89 | * specified in the {@code font} property of a text component. 90 | * 91 | * @return true if named fonts are supported 92 | */ 93 | public boolean supportsNamedFonts() { 94 | return this.format >= V6.format; 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/PackGenerator.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | public interface PackGenerator { 4 | FontPack generate(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/PackJson.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import mnm.hdfontgen.pack.resource.AbstractJsonResource; 6 | 7 | public class PackJson extends AbstractJsonResource { 8 | 9 | final PackSection pack; 10 | 11 | public PackJson(int packFormat, String description) { 12 | pack = new PackSection(packFormat, description); 13 | } 14 | 15 | @Override 16 | public ZipPath getPath() { 17 | return () -> "pack.mcmeta"; 18 | } 19 | 20 | record PackSection(@SerializedName("pack_format") int packFormat, String description) { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/PackSettings.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | import java.awt.*; 4 | import java.nio.file.Path; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Objects; 10 | import java.util.function.Predicate; 11 | import java.util.function.UnaryOperator; 12 | 13 | import mnm.hdfontgen.pack.generator.FontProviderFontGenerator; 14 | import mnm.hdfontgen.pack.generator.LegacyFontGenerator; 15 | import mnm.hdfontgen.pack.provider.FontProvider; 16 | import mnm.hdfontgen.pack.provider.FontProvidersJson; 17 | import mnm.hdfontgen.pack.provider.StandardFontProviders; 18 | 19 | public class PackSettings { 20 | public final PackFormat format; 21 | public final String description; 22 | public final Map fonts; 23 | 24 | private PackSettings(PackFormat format, String description, Map fonts) { 25 | this.format = format; 26 | this.description = description; 27 | this.fonts = Map.copyOf(fonts); 28 | } 29 | 30 | public PackJson getPackJson() { 31 | return new PackJson(format.getFormat(), description); 32 | } 33 | 34 | public PackGenerator createGenerator() { 35 | if (!format.supportsFontProviders()) { 36 | return new LegacyFontGenerator(this); 37 | } 38 | return new FontProviderFontGenerator(this); 39 | } 40 | 41 | public abstract static class FontTexture { 42 | public final ResourcePath name; 43 | 44 | protected FontTexture(ResourcePath name) { 45 | this.name = name; 46 | } 47 | 48 | public abstract List getProviders(PackFormat format); 49 | } 50 | 51 | public static class Bitmap extends FontTexture { 52 | public final Font font; 53 | public final TextureSize size; 54 | public final boolean unicode; 55 | 56 | private Bitmap(ResourcePath name, Font font, TextureSize size, boolean unicode) { 57 | super(name); 58 | this.font = font; 59 | this.size = size; 60 | this.unicode = unicode; 61 | } 62 | 63 | public HDFont getFont() { 64 | return new HDFont(font, size); 65 | } 66 | 67 | @Override 68 | public List getProviders(PackFormat format) { 69 | List providers = new ArrayList<>(4); 70 | providers.add(StandardFontProviders.ascii(this)); 71 | if (format.supportsFontProviders()) { 72 | providers.add(StandardFontProviders.nonLatinEuropean(this)); 73 | providers.add(StandardFontProviders.accented(this)); 74 | } 75 | if (this.unicode) { 76 | providers.add(StandardFontProviders.unicodePages(this)); 77 | } 78 | return providers; 79 | } 80 | } 81 | 82 | public static class TrueType extends FontTexture { 83 | public final Path font; 84 | public final float oversample; 85 | 86 | private TrueType(ResourcePath name, Path font, float oversample) { 87 | super(name); 88 | this.font = font; 89 | this.oversample = oversample; 90 | } 91 | 92 | @Override 93 | public List getProviders(PackFormat format) { 94 | return List.of( 95 | StandardFontProviders.trueType(this) 96 | ); 97 | } 98 | } 99 | 100 | public static class Builder implements BuilderBase { 101 | private final PackFormat format; 102 | private String description; 103 | private final Map fonts = new HashMap<>(); 104 | 105 | public Builder(PackFormat format) { 106 | this.format = Objects.requireNonNull(format); 107 | } 108 | 109 | public Builder withDescription(String desc) { 110 | this.description = Objects.requireNonNull(desc); 111 | return this; 112 | } 113 | 114 | public Builder bitmap(ResourcePath name, UnaryOperator func) { 115 | return addFontProvider(name, new BitmapBuilder(name), func); 116 | } 117 | 118 | public Builder trueType(ResourcePath name, UnaryOperator func) { 119 | if (!this.format.supportsTrueTypeFonts()) { 120 | throw new UnsupportedOperationException("Pack format " + this.format + " does not support true type fonts."); 121 | } 122 | return addFontProvider(name, new TrueTypeBuilder(name), func); 123 | } 124 | 125 | protected > 126 | Builder addFontProvider(ResourcePath name, B builder, UnaryOperator func) { 127 | // make sure this name hasn't been registered already 128 | if (this.fonts.containsKey(name)) { 129 | throw new UnsupportedOperationException("Provider named " + name + " was already created."); 130 | } 131 | // named fonts are only supported in V6 and up 132 | if (!this.format.supportsNamedFonts() && !FontProvidersJson.DEFAULT_NAME.equals(name)) { 133 | throw new UnsupportedOperationException("Pack format " + this.format + " does not support named fonts. Only " + FontProvidersJson.DEFAULT_NAME + " is supported."); 134 | } 135 | 136 | this.fonts.put(name, func.apply(builder).build()); 137 | return this; 138 | } 139 | 140 | @Override 141 | public PackSettings build() { 142 | checkNotNull(this.description, "description"); 143 | check(this.fonts, Predicate.not(Map::isEmpty), "fonts is empty"); 144 | return new PackSettings(format, description, fonts); 145 | } 146 | 147 | 148 | public class BitmapBuilder implements BuilderBase { 149 | 150 | private final ResourcePath name; 151 | private Font font; 152 | private TextureSize size; 153 | private boolean unicode; 154 | 155 | BitmapBuilder(ResourcePath name) { 156 | this.name = name; 157 | } 158 | 159 | private String makeDescription() { 160 | var fontName = font.getFontName(); 161 | var withUnicode = unicode ? " with unicode" : ""; 162 | var versions = format.getVersionRange(); 163 | 164 | return String.format("%s %s%s for Minecraft %s", fontName, size, withUnicode, versions); 165 | } 166 | 167 | public BitmapBuilder withFont(String font) { 168 | return withFont(Font.decode(font)); 169 | } 170 | 171 | public BitmapBuilder withFont(Font font) { 172 | this.font = Objects.requireNonNull(font); 173 | return this; 174 | } 175 | 176 | public BitmapBuilder withSize(TextureSize size) { 177 | this.size = Objects.requireNonNull(size); 178 | return this; 179 | } 180 | 181 | public BitmapBuilder withUnicode(boolean unicode) { 182 | this.unicode = unicode; 183 | return this; 184 | } 185 | 186 | @Override 187 | public Bitmap build() { 188 | checkNotNull(font, "font"); 189 | checkNotNull(size, "size"); 190 | if (description == null) { 191 | description = makeDescription(); 192 | } 193 | return new Bitmap(name, font, size, unicode); 194 | } 195 | } 196 | 197 | public class TrueTypeBuilder implements BuilderBase { 198 | private final ResourcePath name; 199 | private Path font; 200 | private float oversample = 1f; 201 | 202 | TrueTypeBuilder(ResourcePath name) { 203 | this.name = name; 204 | } 205 | 206 | private String makeDescription() { 207 | var fontName = font.getFileName().toString(); 208 | var versions = format.getVersionRange(); 209 | 210 | return String.format("%s x%.01f for Minecraft %s", fontName, oversample, versions); 211 | } 212 | 213 | public TrueTypeBuilder withFont(Path fontFile) { 214 | this.font = Objects.requireNonNull(fontFile); 215 | return this; 216 | } 217 | 218 | public TrueTypeBuilder withOversample(float oversample) { 219 | this.oversample = oversample; 220 | return this; 221 | } 222 | 223 | @Override 224 | public TrueType build() { 225 | checkNotNull(font, "font"); 226 | if (description == null) { 227 | description = makeDescription(); 228 | } 229 | return new TrueType(name, font, oversample); 230 | } 231 | } 232 | } 233 | 234 | private static void checkNotNull(Object obj, String message) { 235 | check(obj, Objects::nonNull, message); 236 | } 237 | 238 | private static void check(T obj, Predicate condition, String message) { 239 | if (!condition.test(obj)) { 240 | throw new IllegalStateException(message); 241 | } 242 | } 243 | 244 | private interface BuilderBase { 245 | T build(); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/ResourcePath.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonPrimitive; 7 | import com.google.gson.JsonSerializationContext; 8 | import com.google.gson.JsonSerializer; 9 | import com.google.gson.annotations.JsonAdapter; 10 | 11 | @JsonAdapter(ResourcePath.Serializer.class) 12 | public record ResourcePath(String namespace, String path) implements ZipPath { 13 | 14 | public ResourcePath(String path) { 15 | this("minecraft", path); 16 | } 17 | 18 | @Override 19 | public String getFileLocation() { 20 | return String.format("assets/%s/%s", namespace, path); 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return String.format("%s:%s", namespace, path); 26 | } 27 | 28 | static class Serializer implements JsonSerializer { 29 | 30 | @Override 31 | public JsonElement serialize(ResourcePath src, Type typeOfSrc, JsonSerializationContext context) { 32 | return new JsonPrimitive(src.toString()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/TextureSize.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | public enum TextureSize { 4 | 5 | x32(32), 6 | x64(64), 7 | x128(128), 8 | x256(256), 9 | x512(512); 10 | 11 | private final int textureSize; 12 | 13 | TextureSize(int size) { 14 | this.textureSize = size; 15 | } 16 | 17 | public int getTextureSize() { 18 | return textureSize; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/ZipPath.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack; 2 | 3 | public interface ZipPath { 4 | String getFileLocation(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/generator/AbstractFontProviderPackGenerator.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.generator; 2 | 3 | import java.util.Map; 4 | 5 | import mnm.hdfontgen.pack.FontPack; 6 | import mnm.hdfontgen.pack.PackSettings; 7 | import mnm.hdfontgen.pack.ResourcePath; 8 | import mnm.hdfontgen.pack.provider.FontProvidersJson; 9 | 10 | abstract class AbstractFontProviderPackGenerator extends AbstractPackGenerator { 11 | AbstractFontProviderPackGenerator(PackSettings settings) { 12 | super(settings); 13 | } 14 | 15 | @Override 16 | protected final void populatePack(FontPack pack) { 17 | for (Map.Entry e : settings.fonts.entrySet()) { 18 | 19 | var providers = new FontProvidersJson(e.getKey()); 20 | populateFontProviders(providers, e.getValue()); 21 | providers.setup(pack); 22 | } 23 | } 24 | 25 | protected abstract void populateFontProviders(FontProvidersJson providers, PackSettings.FontTexture font); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/generator/AbstractPackGenerator.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.generator; 2 | 3 | import mnm.hdfontgen.pack.FontPack; 4 | import mnm.hdfontgen.pack.PackSettings; 5 | import mnm.hdfontgen.pack.PackGenerator; 6 | 7 | abstract class AbstractPackGenerator implements PackGenerator { 8 | 9 | protected final PackSettings settings; 10 | 11 | protected AbstractPackGenerator(PackSettings settings) { 12 | this.settings = settings; 13 | } 14 | 15 | @Override 16 | public final FontPack generate() { 17 | var pack = new FontPack(); 18 | pack.addResource(settings.getPackJson()); 19 | populatePack(pack); 20 | return pack; 21 | } 22 | 23 | protected abstract void populatePack(FontPack pack); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/generator/FontProviderFontGenerator.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.generator; 2 | 3 | import mnm.hdfontgen.pack.PackSettings; 4 | import mnm.hdfontgen.pack.provider.FontProvidersJson; 5 | 6 | public class FontProviderFontGenerator extends AbstractFontProviderPackGenerator { 7 | 8 | public FontProviderFontGenerator(PackSettings settings) { 9 | super(settings); 10 | } 11 | 12 | @Override 13 | protected void populateFontProviders(FontProvidersJson providers, PackSettings.FontTexture font) { 14 | providers.addProviders(font.getProviders(settings.format)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/generator/LegacyFontGenerator.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.generator; 2 | 3 | import mnm.hdfontgen.pack.FontPack; 4 | import mnm.hdfontgen.pack.PackSettings; 5 | import mnm.hdfontgen.pack.provider.FontProvidersJson; 6 | 7 | public class LegacyFontGenerator extends AbstractPackGenerator { 8 | 9 | public LegacyFontGenerator(PackSettings settings) { 10 | super(settings); 11 | } 12 | 13 | @Override 14 | protected void populatePack(FontPack pack) { 15 | // minecraft:default should be the only provider available. The actual 16 | // name doesn't matter since font providers aren't supported. 17 | var fontTexture = settings.fonts.get(FontProvidersJson.DEFAULT_NAME); 18 | 19 | var providers = fontTexture.getProviders(settings.format); 20 | for (var provider : providers) { 21 | provider.setup(pack); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/provider/BitmapFontProvider.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.provider; 2 | 3 | import mnm.hdfontgen.pack.FontPack; 4 | import mnm.hdfontgen.pack.HDFont; 5 | import mnm.hdfontgen.pack.ResourcePath; 6 | import mnm.hdfontgen.pack.resource.BitmapFontResource; 7 | 8 | class BitmapFontProvider extends FontProvider { 9 | 10 | transient final HDFont font; 11 | final ResourcePath file; 12 | final Integer height; 13 | final int ascent; 14 | final String[] chars; 15 | 16 | BitmapFontProvider(HDFont font, ResourcePath file, Integer height, int ascent, String[] chars) { 17 | super("bitmap"); 18 | this.font = font; 19 | this.file = file; 20 | this.height = height; 21 | this.ascent = ascent; 22 | this.chars = chars; 23 | } 24 | 25 | BitmapFontProvider(HDFont font, ResourcePath file, int ascent, String[] chars) { 26 | this(font, file, null, ascent, chars); 27 | } 28 | 29 | @Override 30 | public void setup(FontPack pack) { 31 | String texturePath = String.format("textures/%s", file.path()); 32 | ResourcePath path = new ResourcePath(file.namespace(), texturePath); 33 | 34 | pack.addResource(new BitmapFontResource(path, font, chars)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/provider/FontProvider.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.provider; 2 | 3 | import mnm.hdfontgen.pack.FontPack; 4 | 5 | public abstract class FontProvider { 6 | public final String type; 7 | 8 | FontProvider(String type) { 9 | this.type = type; 10 | } 11 | 12 | public abstract void setup(FontPack pack); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/provider/FontProvidersJson.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.provider; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | import mnm.hdfontgen.pack.FontPack; 8 | import mnm.hdfontgen.pack.ResourcePath; 9 | import mnm.hdfontgen.pack.ZipPath; 10 | import mnm.hdfontgen.pack.resource.AbstractJsonResource; 11 | 12 | public class FontProvidersJson extends AbstractJsonResource { 13 | public static final ResourcePath DEFAULT_NAME = new ResourcePath("minecraft", "default"); 14 | 15 | private transient final ResourcePath path; 16 | private final List providers; 17 | 18 | public FontProvidersJson(ResourcePath name) { 19 | this.path = new ResourcePath(name.namespace(), String.format("font/%s.json", name.path())); 20 | this.providers = new ArrayList<>(); 21 | } 22 | 23 | @Override 24 | public ZipPath getPath() { 25 | return path; 26 | } 27 | 28 | public void addProvider(FontProvider provider) { 29 | this.providers.add(provider); 30 | } 31 | 32 | public void addProviders(Collection providers) { 33 | this.providers.addAll(providers); 34 | } 35 | 36 | public void setup(FontPack pack) { 37 | pack.addResource(this); 38 | for (FontProvider provider : providers) { 39 | provider.setup(pack); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/provider/LegacyUnicodeFontProvider.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.provider; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | import java.util.stream.IntStream; 6 | 7 | import mnm.hdfontgen.pack.FontPack; 8 | import mnm.hdfontgen.pack.HDFont; 9 | import mnm.hdfontgen.pack.ResourcePath; 10 | import mnm.hdfontgen.pack.resource.BitmapFontResource; 11 | import mnm.hdfontgen.pack.resource.GlyphSizesResource; 12 | 13 | class LegacyUnicodeFontProvider extends FontProvider { 14 | 15 | private transient final HDFont font; 16 | final ResourcePath sizes; 17 | final ResourcePath template; 18 | 19 | LegacyUnicodeFontProvider(HDFont font, ResourcePath sizes, ResourcePath template) { 20 | super("legacy_unicode"); 21 | this.font = font; 22 | this.sizes = sizes; 23 | this.template = template; 24 | } 25 | 26 | private List createUnicodePages(HDFont font) { 27 | // file template uses hex integers, json template uses strings 28 | var texturePath = template.path().replace("%s", "%02x"); 29 | return IntStream.range(0, 0x100).mapToObj(pageIndex -> { 30 | String[] charTable = getUnicodeTable(pageIndex); 31 | var path = new ResourcePath(template.namespace(), texturePath.formatted(pageIndex)); 32 | return new BitmapFontResource(path, font, charTable); 33 | }).collect(Collectors.toList()); 34 | } 35 | 36 | private static String[] getUnicodeTable(int tableIndex) { 37 | int a = tableIndex << 8; 38 | var table = new String[16]; 39 | for (int y = 0; y < 16; y++) { 40 | char[] line = new char[16]; 41 | int b = y << 4; 42 | for (int x = 0; x < 16; x++) { 43 | line[x] = (char) (a + b + x); 44 | } 45 | table[y] = new String(line); 46 | } 47 | return table; 48 | } 49 | 50 | @Override 51 | public void setup(FontPack pack) { 52 | var pages = this.createUnicodePages(font); 53 | pack.addResources(pages); 54 | pack.addResource(new GlyphSizesResource(this.sizes, pages)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/provider/StandardFontProviders.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.provider; 2 | 3 | import mnm.hdfontgen.pack.PackSettings; 4 | import mnm.hdfontgen.pack.ResourcePath; 5 | 6 | public final class StandardFontProviders { 7 | 8 | public static FontProvider ascii(PackSettings.Bitmap settings) { 9 | ResourcePath file = new ResourcePath("font/ascii.png"); 10 | return new BitmapFontProvider(settings.getFont(), file, 7, ASCII); 11 | } 12 | 13 | public static FontProvider accented(PackSettings.Bitmap settings) { 14 | ResourcePath file = new ResourcePath("font/accented.png"); 15 | return new BitmapFontProvider(settings.getFont(), file, 12, 10, ACCENTED); 16 | } 17 | 18 | public static FontProvider nonLatinEuropean(PackSettings.Bitmap settings) { 19 | ResourcePath file = new ResourcePath("font/nonlatin_european.png"); 20 | return new BitmapFontProvider(settings.getFont(), file, 7, NONLATIN_EUROPEAN); 21 | } 22 | 23 | public static FontProvider unicodePages(PackSettings.Bitmap settings) { 24 | ResourcePath sizes = new ResourcePath("font/glyph_sizes.bin"); 25 | ResourcePath template = new ResourcePath("textures/font/unicode_page_%s.png"); 26 | return new LegacyUnicodeFontProvider(settings.getFont(), sizes, template); 27 | } 28 | 29 | public static FontProvider trueType(PackSettings.TrueType settings) { 30 | return new TrueTypeFontProvider(settings.font, settings.name, new float[]{0, -0.5f}, 14, settings.oversample, ""); 31 | } 32 | 33 | private static final String[] ASCII = { 34 | "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130", 35 | "\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000", 36 | "\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f", 37 | "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f", 38 | "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f", 39 | "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\\\u005d\u005e\u005f", 40 | "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f", 41 | "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u0000", 42 | "\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5", 43 | "\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192", 44 | "\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb", 45 | "\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510", 46 | "\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567", 47 | "\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580", 48 | "\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229", 49 | "\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000" 50 | }; 51 | 52 | private static final String[] ACCENTED = { 53 | "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf", 54 | "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3", 55 | "\u00e4\u00e5\u00e6\u00e7\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa", 56 | "\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0102\u0103\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b", 57 | "\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0115\u0116\u0117\u0118\u0119\u011a\u011b", 58 | "\u011c\u011d\u1e20\u1e21\u011e\u011f\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129", 59 | "\u012a\u012b\u012c\u012d\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c", 60 | "\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014a\u014b\u014c\u014d", 61 | "\u014e\u014f\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d", 62 | "\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016c\u016d", 63 | "\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d", 64 | "\u017e\u01fc\u01fd\u01fe\u01ff\u0218\u0219\u021a\u021b\u0386\u0388\u0389\u038a\u038c\u038e\u038f", 65 | "\u0390\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03ca\u03cb\u03cc\u03cd\u03ce\u0400\u0401\u0403", 66 | "\u0407\u040c\u040d\u040e\u0419\u0439\u0450\u0451\u0452\u0453\u0457\u045b\u045c\u045d\u045e\u045f", 67 | "\u0490\u0491\u1e02\u1e03\u1e0a\u1e0b\u1e1e\u1e1f\u1e22\u1e23\u1e30\u1e31\u1e40\u1e41\u1e56\u1e57", 68 | "\u1e60\u1e61\u1e6a\u1e6b\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1ef2\u1ef3\u00e8\u00e9\u00ea\u00eb", 69 | "\u0149\u01e7\u01eb\u040f\u1e0d\u1e25\u1e5b\u1e6d\u1e92\u1eca\u1ecb\u1ecc\u1ecd\u1ee4\u1ee5\u2116", 70 | "\u0207\u0194\u0263\u0283\u2047\u01f1\u01f2\u01f3\u01c4\u01c5\u01c6\u01c7\u01c8\u01ca\u01cb\u01cc", 71 | "\u2139\u1d6b\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua73a\ua73c\ua73d\ua74e\ua74f\ua760\ua761", 72 | "\ufb04\ufb06\u16a1\u16b5\u01a0\u01a1\u01af\u01b0\u1eae\u1eaf\u1ea4\u1ea5\u1ebe\u1ebf\u1ed1\u1eda", 73 | "\u1edb\u1ee8\u1ee9\u1eb0\u1eb1\u1ea6\u1ea7\u1ec0\u1ec1\u1ed3\u1edc\u1edd\u1eea\u1eeb\u1ea2\u1ea3", 74 | "\u1eb2\u1eb3\u1ea8\u1ea9\u1eba\u1ebb\u1ed5\u1ede\u1ec2\u1ec3\u1ec8\u1ec9\u1ece\u1ecf\u1ed4\u1edf", 75 | "\u1ee6\u1ee7\u1eec\u1eed\u1ef6\u1ef7\u1ea0\u1ea1\u1eb6\u1eb7\u1eac\u1ead\u1eb8\u1eb9\u1ec6\u1ec7", 76 | "\u1ed8\u1ed9\u1ee2\u1ee3\u1ef0\u1ef1\u1ef4\u1ef5\u1ed0\u0195\u1eaa\u1eab\u1ed6\u1ed7\u1eef\u261e", 77 | "\u261c\u262e\u1eb4\u1eb5\u1ebc\u1ebd\u1ec4\u1ec5\u1ed2\u1ee0\u1ee1\u1eee\u1ef8\u1ef9\u0498\u0499", 78 | "\u04a0\u04a1\u04aa\u04ab\u01f6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 79 | }; 80 | 81 | private static final String[] NONLATIN_EUROPEAN = { 82 | "\u00a1\u2030\u00ad\u00b7\u20b4\u2260\u00bf\u00d7\u00d8\u00de\u04bb\u00f0\u00f8\u00fe\u0391\u0392", 83 | "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3", 84 | "\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba", 85 | "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u0402", 86 | "\u0405\u0406\u0408\u0409\u040a\u040b\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u041a", 87 | "\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a", 88 | "\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u043a\u043b", 89 | "\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b", 90 | "\u044c\u044d\u044e\u044f\u0454\u0455\u0456\u0458\u0459\u045a\u2013\u2014\u2018\u2019\u201c\u201d", 91 | "\u201e\u2026\u204a\u2190\u2191\u2192\u2193\u21c4\uff0b\u018f\u0259\u025b\u026a\u04ae\u04af\u04e8", 92 | "\u04e9\u02bb\u02cc\u037e\u0138\u1e9e\u00df\u20bd\u20ac\u0462\u0463\u0474\u0475\u04C0\u0472\u0473", 93 | "\u2070\u00b9\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207a\u207b\u207c\u207d\u207e\u2071\u2122", 94 | "\u0294\u0295\u29c8\u2694\u2620\u049a\u049b\u0492\u0493\u04b0\u04b1\u04d8\u04d9\u0496\u0497\u04a2", 95 | "\u04a3\u04ba\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc\u05de\u05dd", 96 | "\u05e0\u05df\u05e1\u05e2\u05e4\u05e3\u05e6\u05e5\u05e7\u05e8\u00a2\u00a4\u00a5\u00a9\u00ae\u00b5", 97 | "\u00b6\u00bc\u00bd\u00be\u0387\u2010\u201a\u2020\u2021\u2022\u2031\u2032\u2033\u2034\u2035\u2036", 98 | "\u2037\u2039\u203a\u203b\u203c\u203d\u2042\u2048\u2049\u204b\u204e\u204f\u2051\u2052\u2057\u2117", 99 | "\u2212\u2213\u221e\u2600\u2601\u2608\u0404\u2632\u2635\u263d\u2640\u2642\u26a5\u2660\u2663\u2665", 100 | "\u2666\u2669\u266a\u266b\u266c\u266d\u266e\u266f\u2680\u2681\u2682\u2683\u2684\u2685\u26a0\u26a1", 101 | "\u26cf\u2714\u2744\u274c\u2764\u2b50\u2e18\u2e2e\u2e35\u2e38\u2E41\u2E4B\u295d\u1614\u0190\u07c8", 102 | "\u03db\u3125\u2c6f\u15fa\u0186\u15e1\u018e\u2132\u2141\uA7B0\ua780\u0500\ua779\u1d1a\u27d8\u2229", 103 | "\u0245\u2144\u0250\u0254\u01dd\u025f\u1d77\u0265\u1d09\u027e\u029e\ua781\u026f\u0279\u0287\u028c", 104 | "\u028d\u028e\u0531\u0532\u0533\u0534\u0536\u0537\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540", 105 | "\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054b\u054c\u054d\u054e\u054f\u0550\u0551", 106 | "\u0552\u0553\u0554\u0555\u0556\u0559\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a", 107 | "\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a", 108 | "\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u05e9\u05ea\u0538", 109 | "\u055a\u055b\u055c\u055d\u055e\u055f\u0560\u0588\u058f\u00af\u017f\u01b7\u0292\u01f7\u01bf\u021c", 110 | "\u021d\u0224\u0225\u02d9\ua75a\ua75b\u2011\u214b\u23cf\u23e9\u23ea\u23ed\u23ee\u23ef\u23f4\u23f5", 111 | "\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u2b58\u25b2\u25b6\u25bc\u25c0\u25cf\u25e6\u25d8", 112 | "\u2693\u26e8\u0132\u0133\u01c9\ua728\ua729\ua739\ua73b\ufb00\ufb01\ufb02\ufb03\ufb05\ufffd\u0535", 113 | "\u054a\u16a0\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af", 114 | "\u16b0\u16b1\u16b2\u16b3\u16b4\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0", 115 | "\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0", 116 | "\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0", 117 | "\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u16ed\u16ee\u16ef\u16f0", 118 | "\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u263a\u263b\u00a6\u2639\u05da\u05f3\u05f4\u05f0", 119 | "\u05f1\u05f2\u05be\u05c3\u05c6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 120 | }; 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/provider/TrueTypeFontProvider.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.provider; 2 | 3 | import java.nio.file.Path; 4 | 5 | import mnm.hdfontgen.pack.FontPack; 6 | import mnm.hdfontgen.pack.ResourcePath; 7 | import mnm.hdfontgen.pack.resource.FileResource; 8 | import mnm.hdfontgen.pack.resource.Resource; 9 | 10 | class TrueTypeFontProvider extends FontProvider { 11 | 12 | transient final Path font; 13 | final ResourcePath file; 14 | final float[] shift; 15 | final float size; 16 | final float oversample; 17 | final String skip; 18 | 19 | TrueTypeFontProvider(Path font, ResourcePath file, float[] shift, float size, float oversample, String skip) { 20 | super("ttf"); 21 | this.font = font; 22 | this.file = file; 23 | this.shift = shift; 24 | this.size = size; 25 | this.oversample = oversample; 26 | this.skip = skip; 27 | } 28 | 29 | private Resource getFileResource(Path input) { 30 | ResourcePath output = new ResourcePath(file.namespace(), "font/" + file.path()); 31 | return new FileResource(input, output); 32 | } 33 | 34 | @Override 35 | public void setup(FontPack pack) { 36 | pack.addResource(getFileResource(font)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/resource/AbstractBitmapResource.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.resource; 2 | 3 | import javax.imageio.ImageIO; 4 | import java.awt.image.BufferedImage; 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | public abstract class AbstractBitmapResource implements Resource { 10 | 11 | protected abstract BufferedImage render(); 12 | 13 | @Override 14 | public final void writeTo(Path path) throws IOException { 15 | try (var out = Files.newOutputStream(path)) { 16 | ImageIO.write(this.render(), "png", out); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/resource/AbstractJsonResource.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.resource; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | 10 | public abstract class AbstractJsonResource implements Resource { 11 | private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); 12 | 13 | protected Object getRoot() { 14 | return this; 15 | } 16 | 17 | @Override 18 | public void writeTo(Path path) throws IOException { 19 | try (var writer = Files.newBufferedWriter(path)) { 20 | gson.toJson(getRoot(), writer); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/resource/BitmapFontResource.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.resource; 2 | 3 | import java.awt.image.BufferedImage; 4 | 5 | import mnm.hdfontgen.pack.HDFont; 6 | import mnm.hdfontgen.pack.ResourcePath; 7 | 8 | public class BitmapFontResource extends AbstractBitmapResource { 9 | 10 | private final ResourcePath path; 11 | private final HDFont font; 12 | private final String[] characters; 13 | private byte[] sizes; 14 | 15 | public BitmapFontResource(ResourcePath path, HDFont font, String[] characters) { 16 | this.path = path; 17 | this.font = font; 18 | this.characters = characters; 19 | } 20 | 21 | @Override 22 | public ResourcePath getPath() { 23 | return this.path; 24 | } 25 | 26 | @Override 27 | protected BufferedImage render() { 28 | var rendered = font.render(characters); 29 | sizes = rendered.sizes(); 30 | return rendered.image(); 31 | } 32 | 33 | public byte[] getGlyphSizes() { 34 | return checkNotNull(sizes, "Tried to get glyph sizes for %s before it was rendered".formatted(this.path)); 35 | } 36 | 37 | private static T checkNotNull(T obj, String message) { 38 | if (obj == null) { 39 | throw new IllegalStateException(message); 40 | } 41 | return obj; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/resource/FileResource.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.resource; 2 | 3 | import mnm.hdfontgen.pack.ZipPath; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | /** 10 | * A resource which copies the input into the output. 11 | */ 12 | public class FileResource implements Resource { 13 | 14 | private final Path input; 15 | private final ZipPath output; 16 | 17 | /** 18 | * Create a new FileResource. 19 | * 20 | * @param input The input file 21 | * @param output The zip path to the output file 22 | */ 23 | public FileResource(Path input, ZipPath output) { 24 | this.input = input; 25 | this.output = output; 26 | } 27 | 28 | @Override 29 | public ZipPath getPath() { 30 | return output; 31 | } 32 | 33 | @Override 34 | public void writeTo(Path path) throws IOException { 35 | Files.copy(this.input, path); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/resource/GlyphSizesResource.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.resource; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.List; 7 | 8 | import mnm.hdfontgen.IOUtils; 9 | import mnm.hdfontgen.pack.ResourcePath; 10 | import mnm.hdfontgen.pack.ZipPath; 11 | 12 | public class GlyphSizesResource implements Resource { 13 | 14 | private final ResourcePath path; 15 | private final List fontTextures; 16 | 17 | public GlyphSizesResource(ResourcePath path, List fontTextures) { 18 | this.path = path; 19 | this.fontTextures = fontTextures; 20 | } 21 | 22 | @Override 23 | public ZipPath getPath() { 24 | return path; 25 | } 26 | 27 | @Override 28 | public void writeTo(Path path) throws IOException { 29 | try (var o = Files.newOutputStream(path)) { 30 | fontTextures.stream() 31 | .map(BitmapFontResource::getGlyphSizes) 32 | .forEach(IOUtils.consume(o::write)); 33 | } 34 | } 35 | 36 | @Override 37 | public int order() { 38 | return 1; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/mnm/hdfontgen/pack/resource/Resource.java: -------------------------------------------------------------------------------- 1 | package mnm.hdfontgen.pack.resource; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Path; 5 | 6 | import mnm.hdfontgen.pack.ZipPath; 7 | 8 | public interface Resource { 9 | ZipPath getPath(); 10 | 11 | void writeTo(Path path) throws IOException; 12 | 13 | /** 14 | * The order of processing. Lower ordered resources will run before higher 15 | * ordered ones. 16 | * @return The order 17 | */ 18 | default int order() { 19 | return 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module hdfontgen { 2 | requires java.desktop; 3 | requires com.google.gson; 4 | opens mnm.hdfontgen.pack to com.google.gson; 5 | opens mnm.hdfontgen.pack.provider to com.google.gson; 6 | requires jdk.zipfs; 7 | } -------------------------------------------------------------------------------- /src/main/resources/unifont-7.0.06.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmess1221/HD-Font-Generator/df4e42f21962d5cdf97617e76e0d954e61bae583/src/main/resources/unifont-7.0.06.ttf --------------------------------------------------------------------------------