Skip to content

Commit

Permalink
add python package
Browse files Browse the repository at this point in the history
  • Loading branch information
simonsmh committed Dec 25, 2024
1 parent c88ffc8 commit d9aa9e2
Show file tree
Hide file tree
Showing 16 changed files with 974 additions and 64 deletions.
67 changes: 41 additions & 26 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
name: Upload zip
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Make release zip
run: |
wget https://github.com/topjohnwu/Magisk/raw/master/scripts/module_installer.sh -O META-INF/com/google/android/update-binary
zip -9 -x "*.git*" -x "extra/*" -x "extra" -r ../notocjk.zip ./
sha256sum ../notocjk.zip > ../notocjk.zip.sha256sum
- name: Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
../notocjk.zip
../notocjk.zip.sha256sum
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v4
with:
name: notocjk
path: ./

name: Upload zip
on:
- push
- pull_request
- workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.13'
cache: 'poetry'
- name: Install poetry
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install
- name: Run python script
run: |
poetry run python -m chws_subset
- name: Make release zip
run: |
zip -9 -x "*.git*" -x "chws_subset*" -x "extra*" -x "poetry.lock" -x "pyproject.toml" -r ../notocjk.zip ./
sha256sum ../notocjk.zip > ../notocjk.zip.sha256sum
- name: Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
../notocjk.zip
../notocjk.zip.sha256sum
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v4
with:
name: notocjk
path: ./

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.ttc
33 changes: 14 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,27 @@
[![Upload zip](https://github.com/simonsmh/notocjk/workflows/Upload%20zip/badge.svg)](https://github.com/simonsmh/notocjk/actions)
[![Download](https://img.shields.io/github/downloads/simonsmh/notocjk/total.svg)](https://github.com/simonsmh/notocjk/releases)

NotoSansCJK & NotoSerifCJK full weight patch for Android devices.
A full weight font patch for Android devices.
> NotoSansCJK full weight patch supports Android 8+
> NotoSerifCJK full weight patch supports Android 9+
* NotoSansCJK VF support as full weight patch applies to Android 8-15
* NotoSerifCJK full weight patch applies to Android 9-15
Fonts files are provided by [noto-cjk](https://github.com/googlefonts/noto-cjk) from Google.
> The fonts have been modified using [subset_noto_cjk.py](https://android.googlesource.com/platform/external/noto-fonts/+/refs/heads/main/scripts/subset_noto_cjk.py) to remove `cmap` entries for characters that should default to the emoji style on Android.
> The fonts have been modified using [chws_tool](https://github.com/googlefonts/chws_tool) to include a `chws` table.
> For more details, please visit https://github.com/WordlessEcho/patch-noto-cjk-for-android
Fonts are provided by [Google](https://github.com/googlefonts/noto-cjk).

## Maintenance
Currently, this module is still maintained. It was used to be stored at official repo but got removed in repo cleanup. Now you can download it directly in this repo's [release tabs](https://github.com/simonsmh/notocjk/releases).

[John Wu's Twitter for details](https://twitter.com/topjohnwu/status/1229896206584664065)
![Noto Serif CJK variable test](extra/serif-variable-test.gif)

## NOTICE
* Android 15+ doesn't need this module anymore.
* You should use latest Magisk Manager/KernelSU to install this module.

* You should use latest Magisk/KernelSU to install this module.
* For Android 15+, NotoSansCJK officially supports variable fonts but only within the weight range of 400-900. This module extends the range to 100-900.
* Recent fixes:

Remove invalid old fonts which prevent MinikinFont from loading with npe when Magisk/KernelSU triggers unmounting.

* Removed old fonts that prevent MinikinFont from loading with an NPE when Magisk/KernelSU triggers unmounting.
* Known issues:
* HK fonts style is pending & waiting for Google solution in later android versions.

HK fonts style is pending & waiting for Google solution in later android versions.


## Credit & Support

## Credit & Support & Maintenance
* Currently, this module is still maintained. It was used to be stored at official repo but got removed in repo cleanup. Now you can download it directly in this repo's [release tabs](https://github.com/simonsmh/notocjk/releases). [John Wu's Twitter for details](https://twitter.com/topjohnwu/status/1229896206584664065)
* Any issue or pull request is welcomed.
* Star this module at [GitHub](https://github.com/simonsmh/notocjk).
133 changes: 133 additions & 0 deletions chws_subset/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import logging
import shutil
from os import PathLike
from pathlib import Path

import chws_tool
import httpx
from fontTools import ttLib
from nototools import font_data, tool_utils
from tqdm import tqdm

## BEGIN: https://android.googlesource.com/platform/external/noto-fonts.git/+/refs/heads/android15-release/scripts/subset_noto_cjk.py
# Characters supported in Noto CJK fonts that UTR #51 recommends default to
# emoji-style.
EMOJI_IN_CJK = {
0x26BD, # ⚽ SOCCER BALL
0x26BE, # ⚾ BASEBALL
0x1F18E, # 🆎 NEGATIVE SQUARED AB
0x1F191, # 🆑 SQUARED CL
0x1F192, # 🆒 SQUARED COOL
0x1F193, # 🆓 SQUARED FREE
0x1F194, # 🆔 SQUARED ID
0x1F195, # 🆕 SQUARED NEW
0x1F196, # 🆖 SQUARED NG
0x1F197, # 🆗 SQUARED OK
0x1F198, # 🆘 SQUARED SOS
0x1F199, # 🆙 SQUARED UP WITH EXCLAMATION MARK
0x1F19A, # 🆚 SQUARED VS
0x1F201, # 🈁 SQUARED KATAKANA KOKO
0x1F21A, # 🈚 SQUARED CJK UNIFIED IDEOGRAPH-7121
0x1F22F, # 🈯 SQUARED CJK UNIFIED IDEOGRAPH-6307
0x1F232, # 🈲 SQUARED CJK UNIFIED IDEOGRAPH-7981
0x1F233, # 🈳 SQUARED CJK UNIFIED IDEOGRAPH-7A7A
0x1F234, # 🈴 SQUARED CJK UNIFIED IDEOGRAPH-5408
0x1F235, # 🈵 SQUARED CJK UNIFIED IDEOGRAPH-6E80
0x1F236, # 🈶 SQUARED CJK UNIFIED IDEOGRAPH-6709
0x1F238, # 🈸 SQUARED CJK UNIFIED IDEOGRAPH-7533
0x1F239, # 🈹 SQUARED CJK UNIFIED IDEOGRAPH-5272
0x1F23A, # 🈺 SQUARED CJK UNIFIED IDEOGRAPH-55B6
0x1F250, # 🉐 CIRCLED IDEOGRAPH ADVANTAGE
0x1F251, # 🉑 CIRCLED IDEOGRAPH ACCEPT
}
# Characters we have decided we are doing as emoji-style in Android,
# despite UTR #51's recommendation
ANDROID_EMOJI = {
0x2600, # ☀ BLACK SUN WITH RAYS
0x2601, # ☁ CLOUD
0x260E, # ☎ BLACK TELEPHONE
0x261D, # ☝ WHITE UP POINTING INDEX
0x263A, # ☺ WHITE SMILING FACE
0x2660, # ♠ BLACK SPADE SUIT
0x2663, # ♣ BLACK CLUB SUIT
0x2665, # ♥ BLACK HEART SUIT
0x2666, # ♦ BLACK DIAMOND SUIT
0x270C, # ✌ VICTORY HAND
0x2744, # ❄ SNOWFLAKE
0x2764, # ❤ HEAVY BLACK HEART
}
# We don't want support for ASCII control chars.
CONTROL_CHARS = set(tool_utils.parse_int_ranges("0000-001F"))
EXCLUDED_CODEPOINTS = frozenset(sorted(EMOJI_IN_CJK | ANDROID_EMOJI | CONTROL_CHARS))


def remove_codepoints_from_ttc(ttc_path, out_dir):
"""Removes a set of characters from a TTC font file's cmap table."""
logging.info("Loading %s", ttc_path)
ttc = ttLib.TTCollection(ttc_path)
logging.info("Subsetting %d fonts in the collection", len(ttc))
for font in ttc:
font_data.delete_from_cmap(font, EXCLUDED_CODEPOINTS)
out_path = out_dir / ttc_path.name
logging.info("Saving to %s", out_path)
ttc.save(out_path)
logging.info(
"Size: %d --> %d, delta=%d",
ttc_path.stat().st_size,
out_path.stat().st_size,
out_path.stat().st_size - ttc_path.stat().st_size,
)


## END: https://android.googlesource.com/platform/external/noto-fonts.git/+/refs/heads/android15-release/scripts/subset_noto_cjk.py


def download_file(
url: str, save_path_file_name: str | bytes | PathLike[str] | PathLike[bytes]
) -> bool:
with open(save_path_file_name, "wb") as f:
with httpx.stream("GET", url, follow_redirects=True) as response:
if response.status_code != 200:
logging.error(f"Failed to download {url}")
return False
with tqdm(
total=int(response.headers.get("content-length", 0)),
unit="B",
unit_divisor=1024,
unit_scale=True,
) as progress:
num_bytes_downloaded = response.num_bytes_downloaded
for chunk in response.iter_bytes():
f.write(chunk)
progress.update(
response.num_bytes_downloaded - num_bytes_downloaded
)
num_bytes_downloaded = response.num_bytes_downloaded
return True


def download_and_patch_noto_cjk_font(url):
base_file_name = url.split("/")[-1]
## Download
logging.info(f"Downloading {url}...")
input_dir = Path("temp/input")
input_dir.mkdir(parents=True, exist_ok=True)
input_file = input_dir / base_file_name
if not download_file(url, input_file):
logging.error("Failed to download")
return

## CHWS Patch
logging.info("Applying CHWS patch...")
output_path = Path("temp/chws_output")
output_path.mkdir(exist_ok=True)
output_file = output_path / base_file_name
chws_tool.add_chws(input_file, output_file)

## Subset
logging.info("Subsetting...")
result_path = Path("system/fonts")
result_path.mkdir(parents=True, exist_ok=True)
remove_codepoints_from_ttc(output_file, result_path)
logging.info("Done!")
shutil.rmtree(Path("temp"))
34 changes: 34 additions & 0 deletions chws_subset/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import argparse
import concurrent.futures

from . import download_file, download_and_patch_noto_cjk_font

DEFAULT_DOWNLOADING_FONTS = {
"NotoSerifCJK-VF.otf.ttc": "https://github.com/notofonts/noto-cjk/raw/refs/heads/main/Serif/Variable/OTC/NotoSerifCJK-VF.otf.ttc",
"NotoSansCJK-VF.otf.ttc": "https://github.com/notofonts/noto-cjk/raw/refs/heads/main/Sans/Variable/OTC/NotoSansCJK-VF.otf.ttc",
}


def main():
parser = argparse.ArgumentParser(
description="Download and patch Noto fonts with CHWS"
)
parser.add_argument("--url", help="URL to download and patch", default=None)
args = parser.parse_args()
if args.url:
download_and_patch_noto_cjk_font(args.url)
else:
## Download and patch all default fonts
with concurrent.futures.ProcessPoolExecutor() as executor:
executor.map(
download_and_patch_noto_cjk_font, DEFAULT_DOWNLOADING_FONTS.values()
)
## Download module_installer.sh
download_file(
"https://github.com/topjohnwu/Magisk/raw/master/scripts/module_installer.sh",
"META-INF/com/google/android/update-binary",
)


if __name__ == "__main__":
main()
45 changes: 35 additions & 10 deletions customize.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
if [ "$API" -gt 34 ]; then
ui_print "*********************************************************"
ui_print "! Android 15+ already supports full weight variable fonts"
ui_print "*********************************************************"
elif [ "$API" -lt 26 ]; then
if [ "$API" -lt 26 ]; then
ui_print "*********************************************************"
ui_print "! Please upgrade your system to Android 8+"
abort "*********************************************************"
fi
BAKPATH=/data/adb/notocjk_bak/
[ -x `which magisk` ] && {
if magisk --denylist ls &>/dev/null; then
CMDPREFIX="magisk --denylist exec"
elif magisk magiskhide ls &>/dev/null; then
CMDPREFIX="magisk magiskhide exec"
fi
} || unset CMDPREFIX
[ -f $BAKPATH/api_level ] && OLD_API=$(cat $BAKPATH/api_level 2>/dev/null) || OLD_API=$API
ui_print "OLD_API: $OLD_API"
if [ -z $CMDPREFIX ] && [ ! "$API" -eq "$OLD_API" ]; then
rm -rf $BAKPATH
ui_print "*********************************************************"
ui_print "! API level changed"
ui_print "! Please uninstall previous version and reboot, then install this version manually"
abort "*********************************************************"
fi
MODULE_NAME=$(basename $MODPATH)
ui_print "MODULE_NAME: $MODULE_NAME"
if [ -z $CMDPREFIX ] && [ ! -d $BAKPATH ] && [ -d "/data/adb/modules/$MODULE_NAME/system/etc" ]; then
ui_print "*********************************************************"
ui_print "! Backup missing"
ui_print "! Please uninstall previous version and reboot, then install this version manually"
abort "*********************************************************"
fi
mkdir -p $BAKPATH
echo "$API" > $BAKPATH/api_level
FILES="fonts.xml fonts_base.xml font_fallback.xml"
FILECUSTOM=fonts_customization.xml
FILEPATHS="/system/etc/ /system_ext/etc/"
Expand All @@ -27,8 +43,13 @@ case "$FILEPATH" in
/system/*) SYSTEMFILEPATH=$FILEPATH ;;
*) SYSTEMFILEPATH=/system$FILEPATH ;;
esac
mkdir -p $MODPATH$FILEPATH
$CMDPREFIX cp -af $FILEPATH$FILE $MODPATH$SYSTEMFILEPATH$FILE
mkdir -p $MODPATH$SYSTEMFILEPATH
if [ ! -f $BAKPATH$FILEPATH$FILE ]; then
ui_print "- Backup $FILE to $BAKPATH"
mkdir -p $BAKPATH$FILEPATH
$CMDPREFIX cp -af $FILEPATH$FILE $BAKPATH$FILEPATH$FILE
fi
cp -af $BAKPATH$FILEPATH$FILE $MODPATH$SYSTEMFILEPATH$FILE
# Disable MiSans for debugging
# sed -i '/<!-- # MIUI Edit Start -->/,/<!-- # MIUI Edit END -->/d;/<!-- MIUI fonts begin \/-->/,/<!-- MIUI fonts end \/-->/d;' $MODPATH$SYSTEMFILEPATH$FILE
# Disable OPlusSans for debugging
Expand Down Expand Up @@ -63,7 +84,6 @@ sed -i '
/<family lang=\"ko\">/,/<\/family>/ {:a;N;/<\/family>/!ba;
s/<family lang=\"ko\">.*Noto.*CJK.*<\/family>/<family lang="ko">\n<font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="100" \/><\/font>\n<font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="300" \/><\/font>\n<font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="400" \/><\/font>\n<font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="500" \/><\/font>\n<font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="600" \/><\/font>\n<font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="700" \/><\/font>\n<font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc<axis tag="wght" stylevalue="900" \/><\/font>\n<font weight="200" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="200" \/><\/font>\n<font weight="300" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="300" \/><\/font>\n<font weight="400" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="400" \/><\/font>\n<font weight="500" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="500" \/><\/font>\n<font weight="600" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="600" \/><\/font>\n<font weight="700" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="700" \/><\/font>\n<font weight="900" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-ExtraLight">NotoSerifCJK-VF.otf.ttc<axis tag="wght" stylevalue="900" \/><\/font>\n<\/family>\n<family lang="ko">\n<font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular">NotoSansCJK-Regular.ttc<\/font>\n<font weight="400" style="normal" index="1" fallbackFor="serif" postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc<\/font>\n<\/family>/};
' $MODPATH$SYSTEMFILEPATH$FILE
sed -i 's/<\/familyset>/<family>\n<font weight="400" style="normal">DroidSansFallbackFull.ttf<\/font>\n<\/family>\n<\/familyset>/g' $MODPATH$SYSTEMFILEPATH$FILE
fi
done
done
Expand All @@ -73,10 +93,15 @@ FILECUSTOMPATH=/product/etc/
SYSTEMFILECUSTOMPATH=/system$FILECUSTOMPATH
if [ -f $FILECUSTOMPATH$FILECUSTOM ]; then
ui_print "- Migrating $FILECUSTOM"
mkdir -p $MODPATH$SYSTEMFILECUSTOMPATH
if $CMDPREFIX grep -q "google-sans" $FILECUSTOMPATH$FILECUSTOM ; then
# Google Pixel's RRO
$CMDPREFIX cp -af $FILECUSTOMPATH$FILECUSTOM $MODPATH$SYSTEMFILECUSTOMPATH$FILECUSTOM
mkdir -p $MODPATH$SYSTEMFILECUSTOMPATH
if [ ! -f $BAKPATH$FILECUSTOMPATH$FILECUSTOM ]; then
ui_print "- Backup $FILE to $BAKPATH"
mkdir -p $BAKPATH$FILECUSTOMPATH
$CMDPREFIX cp -af $FILECUSTOMPATH$FILECUSTOM $BAKPATH$FILECUSTOMPATH$FILECUSTOM
fi
cp -af $BAKPATH$FILECUSTOMPATH$FILECUSTOM $MODPATH$SYSTEMFILECUSTOMPATH$FILECUSTOM
sed -i '
/<family customizationType=\"new-named-family\" name=\"google-sans-medium\">/,/<\/family>/ {/<\/family>/! d;
/<\/family>/ s/.*/ <alias name="google-sans-medium" to="google-sans" weight="500" \/>/};
Expand Down
11 changes: 5 additions & 6 deletions extra/changelog.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Changelog v27
# Changelog v28

* Added an installation warning for Android 15+
* Removed an old font that prevent MinikinFont from loading with an NPE when Magisk/KernelSU triggers unmounting.
* Fonts patching on the fly. See [chws_subset](../chws_subset/__init__.py)
* Removed old fonts that prevent MinikinFont from loading with an NPE when Magisk/KernelSU triggers unmounting.

> The fonts were modified by `subset_noto_cjk.py` to remove cmap entries for characters that should default to the emoji style on Android.
> The fonts have been modified to include a `chws` table.
> See https://github.com/WordlessEcho/patch-noto-cjk-for-android for more details.
> Fonts files are provided by [noto-cjk](https://github.com/googlefonts/noto-cjk) from Google.
> The fonts have been modified using [chws_subset](../chws_subset/__init__.py). For more details, please visit https://github.com/WordlessEcho/patch-noto-cjk-for-android
4 changes: 2 additions & 2 deletions module.prop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
id=notocjk
name=NotoCJK
version=27 (Sans-2.004-VF/Serif-2.003-VF)
versionCode=27
version=28 (Sans-2.004-VF/Serif-2.003-VF)
versionCode=28
author=simonsmh
description=NotoCJK (NotoSansCJK & NotoSerifCJK) full weight fonts patch for every Android devices.
updateJson=https://cdn.jsdelivr.net/gh/simonsmh/notocjk/version.json
Loading

0 comments on commit d9aa9e2

Please sign in to comment.