├── .github └── workflows │ └── build.yaml ├── .gitignore ├── AUTHORS.txt ├── build.sh ├── fonts ├── otf │ └── ChocolateClassicalSans-Regular.otf └── ttf │ └── ChocolateClassicalSans-Regular.ttf ├── history.md ├── img ├── dl.png └── img01.png ├── license.md ├── readme.md ├── requirements.txt └── source ├── ChocolateClassicalSans-Regular.ufoz ├── ChocolateClassicalSans.fcp ├── config.yaml ├── fcp_ufo_process.py └── scripts ├── compress.py └── extract.py /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: 構建字型 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | release: 9 | types: [published] 10 | 11 | 12 | jobs: 13 | build: 14 | name: 構建和保存字型文件 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write # 給機器人權限存進倉庫 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up Python 3.10 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: "3.10" 24 | - name: Install sys tools/deps 25 | run: | 26 | sudo apt-get update 27 | sudo apt-get install ttfautohint 28 | sudo snap install yq 29 | - uses: actions/cache@v4 30 | with: 31 | path: ./venv/ 32 | key: ${{ runner.os }}-venv-${{ hashFiles('**/requirements*.txt') }} 33 | restore-keys: | 34 | ${{ runner.os }}-venv- 35 | - name: gen zip file name 36 | id: zip-name 37 | shell: bash 38 | # Set the archive name to repo name + "-assets" e.g "MavenPro-assets" 39 | run: echo "ZIP_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')-fonts" >> $GITHUB_ENV 40 | - name: Change shell script permissions 41 | run: chmod +x ./build.sh 42 | - name: Checkout actual branch 43 | run: | 44 | git fetch origin main 45 | git checkout main 46 | - name: 運行構建字型的腳本 47 | run: ./build.sh 48 | - name: 儲存 構建後的字型 在個庫裏面 49 | uses: stefanzweifel/git-auto-commit-action@v5 50 | if: github.event_name != 'release' 51 | with: 52 | commit_message: 上傳構建完成字型 53 | file_pattern: fonts/** 54 | push_options: --force 55 | - name: 緩存 構建後的字型 在本次構建歷史記錄 56 | uses: actions/upload-artifact@v4 57 | with: 58 | name: ${{ env.ZIP_NAME }} 59 | path: | 60 | fonts 61 | out 62 | outputs: 63 | zip_name: ${{ env.ZIP_NAME }} 64 | 65 | # There are two ways a release can be created: either by pushing a tag, or by 66 | # creating a release from the GitHub UI. Pushing a tag does not automatically 67 | # create a release, so we have to do that ourselves. However, creating a 68 | # release from the GitHub UI *does* push a tag, and we don't want to create 69 | # a new release in that case because one already exists! 70 | 71 | release: 72 | name: 創建 release 和上傳字型文件 73 | needs: build 74 | runs-on: ubuntu-latest 75 | if: github.event_name == 'release' 76 | env: 77 | ZIP_NAME: ${{ needs.build.outputs.zip_name }} 78 | GH_TOKEN: ${{ github.token }} 79 | steps: 80 | - uses: actions/checkout@v4 81 | - name: Download font artefact files 82 | uses: actions/download-artifact@v4 83 | with: 84 | name: ${{ env.ZIP_NAME }} 85 | path: ${{ env.ZIP_NAME }} 86 | - name: 複製 license 文件 87 | run: cp license.md ${{ env.ZIP_NAME }}/OFL.txt 88 | - name: Remove proof/fontbakery stuff from release 89 | run: rm -rf ${{ env.ZIP_NAME }}/out 90 | - name: gen release file name 91 | shell: bash 92 | run: echo "RELEASE_ZIP_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')-${{github.ref_name}}" >> $GITHUB_ENV 93 | - name: 創建打包 zip 文件 94 | run: mv ${{ env.ZIP_NAME }} ${{ env.RELEASE_ZIP_NAME }}; zip -r ${{ env.RELEASE_ZIP_NAME }}.zip ${{ env.RELEASE_ZIP_NAME }} 95 | - name: 上傳字體到 release 96 | run: | 97 | gh release upload ${{ github.ref_name }} ${{ env.RELEASE_ZIP_NAME }}.zip --clobber 98 | gh release upload ${{ github.ref_name }} fonts/otf/ChocolateClassicalSans-Regular.otf --clobber 99 | gh release upload ${{ github.ref_name }} fonts/ttf/ChocolateClassicalSans-Regular.ttf --clobber 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | source/temp/ 2 | *.ninja 3 | *.ninja_log 4 | .DS_Store 5 | results.md 6 | venv/ 7 | *.ufo -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | # This is the official list of project authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS.txt file. 3 | # See the latter for an explanation. 4 | # 5 | # Names should be added to this file as: 6 | # Name or Organization =4.51.0 2 | gflanguages>=0.6.1 3 | gftools>=0.9.58 4 | glyphsets>=1.0.0 5 | fontbakery>=0.13.2 6 | ufoLib2>=0.16.0 7 | shaperglot >= 1.0.0a1 -------------------------------------------------------------------------------- /source/ChocolateClassicalSans-Regular.ufoz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoonlitOwen/ChocolateSans/2a151732b650e709e9a465884b1176e62e17365c/source/ChocolateClassicalSans-Regular.ufoz -------------------------------------------------------------------------------- /source/ChocolateClassicalSans.fcp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MoonlitOwen/ChocolateSans/2a151732b650e709e9a465884b1176e62e17365c/source/ChocolateClassicalSans.fcp -------------------------------------------------------------------------------- /source/config.yaml: -------------------------------------------------------------------------------- 1 | sources: 2 | - temp/ChocolateClassicalSans-Regular.ufo 3 | familyName: "Chocolate Classical Sans" 4 | # some glyphs from new source has overlaps 5 | removeOutlineOverlaps: True 6 | extraFontmakeArgs: "--production-names --overlaps-backend pathops" 7 | autohintOTF: False 8 | autohintTTF: False 9 | buildWebfont: False 10 | buildSmallCap: False 11 | # FontCreator UFO 版本號有問題,手動更新 12 | version: 1.005 13 | -------------------------------------------------------------------------------- /source/fcp_ufo_process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import glob 4 | from pathlib import Path 5 | import re 6 | import shutil 7 | import codecs 8 | import ufoLib2 9 | from fontTools.pens.reverseContourPen import ReverseContourPen 10 | from fontTools.pens.areaPen import AreaPen 11 | import copy 12 | import yaml # pyyaml installed by gftools 13 | 14 | SOURCE = Path("source/temp") 15 | 16 | # Processing invalid UFO generation from Font Creator version 15.0.0.2974 17 | 18 | for UFO in SOURCE.glob("*.ufo"): 19 | 20 | # Clearing BOM encoding in older versions of FCP. 21 | for file in UFO.rglob("*.*"): 22 | try: 23 | with open(str(file), 'rb') as f: 24 | content = f.read() 25 | 26 | if content.startswith(codecs.BOM_UTF8): 27 | content = content[len(codecs.BOM_UTF8):] 28 | with open(file, 'wb') as f: 29 | f.write(content) 30 | else: 31 | pass 32 | except Exception as e: 33 | print(f"An error occurred: {e}") 34 | 35 | 36 | 37 | # Older FCP does not add the encodingID properly 38 | fontInfo = UFO / "fontinfo.plist" 39 | 40 | plist = open(fontInfo, 'r') 41 | content = plist.read() 42 | content_new = re.sub( 43 | 'platformID\\n\\s+3', 44 | 'platformID3encodingID1', 45 | content 46 | ) 47 | plist.close() 48 | 49 | with open (fontInfo, 'w') as plistWriter: 50 | plistWriter.write (content_new) 51 | 52 | # Correcting invalid feature file data in older FCP output 53 | features = UFO / "features.fea" 54 | 55 | fea = open(features, 'r') 56 | content = fea.read() 57 | 58 | content_new = re.sub( 59 | r"@\\", 60 | "@", 61 | content 62 | ) 63 | fea.close() 64 | 65 | with open (features, 'w') as feaWriter: 66 | feaWriter.write(content_new) 67 | 68 | # ------------------------------------ 69 | 70 | VERSION = None 71 | 72 | # read version from config.yaml 73 | with open(SOURCE / ".." / "config.yaml") as f: 74 | config = yaml.safe_load(f) 75 | version_str = f"{config['version']:.3f}" 76 | version_minor, version_major = version_str.split(".") 77 | VERSION = (int(version_minor), int(version_major)) 78 | 79 | # ------------------------------------ 80 | 81 | # Now that the UFO is normalized, we can modify it more easily with ufoLib2 82 | print ("Adding some name records") 83 | font = ufoLib2.Font.open(UFO) 84 | 85 | # Setting useTypoMetrics flag 86 | if 7 in font.info.openTypeOS2Selection: 87 | font.info.openTypeOS2Selection.remove(7) 88 | 89 | # Setting version number override 90 | print(f"Overriding version to {VERSION[0]}.{VERSION[1]:03d}") 91 | font.info.versionMajor = VERSION[0] 92 | font.info.versionMinor = VERSION[1] 93 | 94 | name_records = [] 95 | 96 | for record in font.info.openTypeNameRecords: 97 | #FCP creates a NID 16 for the localized name, but not a NID1 98 | if record.get("nameID") == 16: 99 | new_record = { 100 | "nameID": 1, 101 | "platformID": record.get("platformID"), 102 | "encodingID": record.get("encodingID"), 103 | "languageID": record.get("languageID"), 104 | "string": record.get("string"), 105 | } 106 | name_records.append(new_record) 107 | elif record.get("nameID") == 17: 108 | pass 109 | else: 110 | name_records.append(record) 111 | font.info.openTypeNameRecords = name_records 112 | 113 | # ------------------------------------ 114 | # Correct the unicodeVariationSequences and add meta table 115 | print ("Overriding unicodeVariationSequences and adding meta table") 116 | variationSequences = { 117 | "FE00": { 118 | "2018": "quoteleft", 119 | "2019": "quoteright", 120 | "201C": "quotedblleft", 121 | "201D": "quotedblright", 122 | "3001": "uni3001.crnr", 123 | "3002": "uni3002.crnr", 124 | "FF01": "uniFF01.crnr", 125 | "FF0C": "uniFF0C.crnr", 126 | "FF0E": "uniFF0E.crnr", 127 | "FF1A": "uniFF1A.crnr", 128 | "FF1B": "uniFF1B.crnr", 129 | "FF1F": "uniFF1F.crnr" 130 | }, 131 | "FE01": { 132 | "2018": "quoteleft.fwid", 133 | "2019": "quoteright.fwid", 134 | "201C": "quotedblleft.fwid", 135 | "201D": "quotedblright.fwid", 136 | "3001": "uni3001", 137 | "3002": "uni3002", 138 | "FF01": "uniFF01", 139 | "FF0C": "uniFF0C", 140 | "FF0E": "uniFF0E", 141 | "FF1A": "uniFF1A", 142 | "FF1B": "uniFF1B", 143 | "FF1F": "uniFF1F" 144 | } 145 | } 146 | 147 | font.lib["public.unicodeVariationSequences"] = variationSequences 148 | 149 | metaTable = { 150 | "dlng": ["zh", "Hant", "zh-Hant", "nan", "hak", "yue", "nan-Latn", "nan-Latn-pehoeji", "nan-Latn-tailo", "Bopo", "Hanb"], 151 | "slng": ["zh", "Hans", "Hant", "zh-Hans", "zh-Hant", "nan", "hak", "yue", "Bopo", "Hanb", "zh-Latn", "zh-Latn-pinyin", "nan-Latn", "nan-Latn-pehoeji", "nan-Latn-tailo", "Latn", "Cyrl", "Grek"] 152 | } 153 | 154 | font.lib["public.openTypeMeta"] = metaTable 155 | # ------------------------------------ 156 | 157 | # Add the BASE table 158 | 159 | base_table_content = """ 160 | table BASE { 161 | HorizAxis.BaseTagList icfb icft ideo romn; 162 | HorizAxis.BaseScriptList DFLT ideo -74 834 -120 0, 163 | hani ideo -74 834 -120 0, 164 | kana ideo -74 834 -120 0, 165 | latn romn -74 834 -120 0, 166 | cyrl romn -74 834 -120 0, 167 | grek romn -74 834 -120 0; 168 | 169 | VertAxis.BaseTagList icfb icft ideo romn; 170 | VertAxis.BaseScriptList DFLT ideo 46 954 0 120, 171 | hani ideo 46 954 0 120, 172 | kana ideo 46 954 0 120, 173 | latn romn 46 954 0 120, 174 | cyrl romn 46 954 0 120, 175 | grek romn 46 954 0 120; 176 | } BASE; 177 | """ 178 | 179 | features = font.features.text if font.features.text else "" 180 | if "table BASE" in features: 181 | pass 182 | else: 183 | print("Adding BASE table to features.fea") 184 | features += f"\n\n{base_table_content}\n" 185 | font.features.text = features 186 | 187 | font.save(UFO,overwrite=True) -------------------------------------------------------------------------------- /source/scripts/compress.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import shutil 3 | from pathlib import Path 4 | import os 5 | import ufoLib2 6 | 7 | if os.path.exists("source/temp"): 8 | archive_list = Path("source/temp/").glob("*.ufo") 9 | 10 | for file in archive_list: 11 | print ("Packaging - "+str(file).split("/")[2]) 12 | ufo = ufoLib2.Font.open(file) 13 | ufo.save(str(file).replace("temp","")+"z",structure="zip",overwrite=True) 14 | 15 | else: 16 | print ("No temp folder found") -------------------------------------------------------------------------------- /source/scripts/extract.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import zipfile 3 | from pathlib import Path 4 | 5 | SOURCE = Path("source") 6 | OUTPUT = SOURCE/"temp" 7 | 8 | for ufoz in SOURCE.glob("*.ufoz"): 9 | print ("Extracting...") 10 | with zipfile.ZipFile(ufoz, 'r') as zip_ref: 11 | zip_ref.extractall(OUTPUT) --------------------------------------------------------------------------------