Merge branch 'epic/autocorrect' into refactor/web/compute-distance-helper

This commit is contained in:
Joshua Horton 2025-09-24 16:18:23 -05:00
commit 785b139b17
62 changed files with 5409 additions and 640 deletions

View file

@ -23,6 +23,7 @@ jobs:
GIT_BRANCH: ${{ steps.environment_step.outputs.GIT_BRANCH }}
GIT_BASE_BRANCH: ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}
GIT_USER: ${{ steps.environment_step.outputs.GIT_USER }}
SKIP_API_CHECK: ${{ steps.environment_step.outputs.SKIP_API_CHECK }}
steps:
- name: Restore artifacts
@ -40,7 +41,7 @@ jobs:
- name: Set pending status on PR builds
id: set_status
if: steps.environment_step.outputs.IS_TEST_BUILD == 'true'
if: steps.environment_step.outputs.IS_TEST_BUILD == 'true' && steps.environment_step.outputs.SKIP_API_CHECK != 'true'
shell: bash
run: |
gh api \
@ -53,6 +54,7 @@ jobs:
-f context="$STATUS_CONTEXT"
- name: Checkout
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
with:
ref: '${{ steps.environment_step.outputs.GIT_SHA }}'
@ -61,11 +63,13 @@ jobs:
path: ${{ github.workspace }}/keyman/
- name: Install devscripts
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
uses: ./keyman/.github/actions/apt-install
with:
packages: devscripts equivs
- name: "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }})"
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true'
run: |
echo "Verify API for libkeymancore*.so (${{ steps.environment_step.outputs.GIT_BRANCH }}, branch ${{ steps.environment_step.outputs.GIT_BASE_BRANCH }}, by ${{ steps.environment_step.outputs.GIT_USER }}):" >> $GITHUB_STEP_SUMMARY
@ -79,17 +83,17 @@ jobs:
verify 2>> $GITHUB_STEP_SUMMARY
- name: Archive .symbols file
if: steps.environment_step.outputs.SKIP_API_CHECK != 'true' && always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: libkeymancore.symbols
path: ${{ github.workspace }}/keyman/linux/debian/tmp/DEBIAN/symbols
if: always()
set_status:
name: Set result status on PR builds
needs: [api_verification]
runs-on: ubuntu-latest
if: ${{ always() && needs.api_verification.outputs.IS_TEST_BUILD == 'true' }}
if: ${{ always() && needs.api_verification.outputs.IS_TEST_BUILD == 'true' && needs.api_verification.outputs.SKIP_API_CHECK != 'true' }}
steps:
- name: Set success
if: needs.api_verification.result == 'success'

View file

@ -0,0 +1,90 @@
# This workflow runs the build, test and publish steps for the
# Keyman build Docker images.
# It is triggered by a schedule and repository_dispatch event.
name: "Build, test, and publish Docker images"
run-name: "Build, test, and publish Docker images (branch ${{ github.ref_name }} on ${{ github.event_name }}/${{ github.event.action }} by ${{ github.actor }})"
defaults:
run:
shell: bash
on:
schedule:
#run every Fri, 3 pm UTC
- cron: '0 15 * * 5'
repository_dispatch:
types: ['build-test-publish-docker:*']
env:
REGISTRY: ghcr.io
USERNAME: "${{ github.actor }}"
PASSWORD: "${{ github.token }}"
jobs:
message:
name: Print message
runs-on: ubuntu-latest
if: ${{ github.event.act }}
steps:
- name: Print message
id: print-message
run: |
echo "Build, test, and publish Docker images (branch ${{ github.ref_name }} on ${{ github.event_name }}/${{ github.event.action }} by ${{ github.actor }})"
build:
name: Build Docker images
runs-on: ubuntu-latest
steps:
- name: Checkout repository
id: checkout
uses: actions/checkout@v4
- name: Build Docker images
id: build
run: |
if [ -z "${{ env.USERNAME }}" ]; then
resources/docker-images/build.sh build: --registry ${{ env.REGISTRY }}
else
echo ${{ env.PASSWORD }} | resources/docker-images/build.sh build: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }}
fi
publish:
name: Publish Docker images
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
id: checkout
uses: actions/checkout@v4
- name: Publish Docker images
id: publish
run: |
if [ -z "${{ env.USERNAME }}" ]; then
resources/docker-images/build.sh publish: --registry ${{ env.REGISTRY }}
else
echo ${{ env.PASSWORD }} | resources/docker-images/build.sh publish: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }}
fi
test:
name: Test Docker images
runs-on: ubuntu-latest
needs: publish
steps:
- name: Checkout repository
id: checkout
uses: actions/checkout@v4
- name: Test Docker images
id: test
run: |
if [ -z "${{ env.USERNAME }}" ]; then
resources/docker-images/build.sh test: --registry ${{ env.REGISTRY }}
else
echo ${{ env.PASSWORD }} | resources/docker-images/build.sh test: --registry ${{ env.REGISTRY }} --username ${{ env.USERNAME }}
fi

View file

@ -23,6 +23,7 @@ on:
# baseRef: The ref of the previous commit. For a PR the same as `baseBranch`.
# user: The user that triggered the build or created the PR
# isTestBuild: false for Releases, otherwise true
# skipApiCheck: true to skip the API check, otherwise false
env:
COLOR_GREEN: "\e[32m"
@ -332,6 +333,7 @@ jobs:
echo "GIT_BRANCH=${{ github.event.client_payload.branch }}" >> artifacts/env
echo "GIT_BASE_BRANCH=${{ github.event.client_payload.baseBranch }}" >> artifacts/env
echo "GIT_USER=${{ github.event.client_payload.user }}" >> artifacts/env
echo "SKIP_API_CHECK=${{ github.event.client_payload.skipApiCheck }}" >> artifacts/env
- name: Cache artifacts
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3

View file

@ -0,0 +1,19 @@
on:
schedule:
#run every Fri, 6 pm UTC
- cron: '0 18 * * 5' #
jobs:
cleanup-images:
name: ghcr.io cleanup action
runs-on: ubuntu-latest
steps:
- uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 # v1.0.16
with:
dry-run: true
expand-packages: true
packages: keyman-*-ci
delete-untagged: true

View file

@ -1,4 +1,4 @@
# GENERATED FILE - DO NOT EDIT!
# GENERATED FILE - DO NOT EDIT!
#
# Keyman is copyright (C) SIL Global. MIT License.
#
@ -35,174 +35,194 @@ jobs:
id: run_pr_build_status_script
uses: actions/github-script@v7
with:
script: |
// This code is copied out of resources/build/pr-build-status/pr-build-status.mjs
// where it is tested. It is copied inline here in order to avoid requiring the
// repository to be checked out, which dramatically reduces the run time of the
// check.
//
// Note: we don't currently look at check runs, only statuses
//
// Verify the following statuses:
// 'user_testing'
// 'API Verification' (github-actions[bot])
//
// At least 1 of the following statuses must be found:
// 'Test*' (keyman-server), e.g. 'Test Build (Keyman)'
// 'Ubuntu Packaging' (github-actions[bot])
//
// Ignore the following statuses:
// check/web/file-size
//
function reduceStatuses(statuses) {
const filtered_statuses = statuses.reduce((o, status) => {
if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) {
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
} else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') {
if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};;
} else if(status.context == 'API Verification') {
if(!o[status.context]) o[status.context] = {type: 'check', state: status.state};
} else if(status.context == 'Ubuntu Packaging') {
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
} else if(status.context == 'check/web/file-size') {
// Ignore check/web/file-size -- we won't block automerge for this at this point
} else {
// We fail with an 'unknown status' response if we get a new status check
// so we can be sure we are not skipping known status checks
o[status.context] = {type: 'unknown', state: status.state};
}
return o;
}, {});
return filtered_statuses;
}
//
// Given the collection of status checks we care about, return
// an aggregate status -- error, failed, pending, or success,
// and a summary description
//
function calculateFinalStatus(filtered_statuses) {
const counts = {};
let hasBuilds = false;
for(const context of Object.keys(filtered_statuses)) {
const { state, type } = filtered_statuses[context];
if(type == 'unknown') {
// We special-case for unknown status checks, and never permit them
return [
'error', `An unknown context ${context} was found, cannot calculate build status.`
];
}
if(type == 'build') {
hasBuilds = true;
}
counts[state] = counts[state] ? counts[state] + 1 : 1;
}
// If we do not have any statuses yet, we wait
if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) {
return ['pending', 'Builds have not yet been triggered ⌛'];
}
const state =
counts.error ? 'error' :
counts.failed ? 'failed' :
counts.pending ? 'pending' :
'success';
let description = '';
function appendDescription(count, state) {
if(!count) return;
if(description != '') description += '; ';
description += `${count} check${count == 1 ? '' : 's'} ${state}`;
}
appendDescription(counts.error, 'in an error state ❌');
appendDescription(counts.failed, 'failed ❌');
appendDescription(counts.pending, 'pending ⌛');
appendDescription(counts.success, 'completed successfully ✅');
return [ state, description ];
}
async function getCommitStatuses(github, owner, repo, sha) {
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', {
owner,
repo,
sha,
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
return statuses;
}
async function getCommitCheckRuns(github, owner, repo, sha) {
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', {
owner,
repo,
sha,
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
return statuses;
}
function calculateCheckResult(statuses) {
if(!Array.isArray(statuses)) {
return ['error', 'Failed to retrieve status checks from GitHub ❌'];
}
const filtered_statuses = reduceStatuses(statuses);
const result = calculateFinalStatus(filtered_statuses);
return result;
}
async function test(github, owner, repo, sha) {
// Get statuses from sha
const statuses = await getCommitStatuses(github, owner, repo, sha);
return calculateCheckResult(statuses);
}
async function createCheck(github, owner, repo, sha) {
const check = await github.rest.checks.create({
owner,
repo,
head_sha: sha,
name: 'Build Outcome',
status: 'in_progress',
});
return check.data.id;
}
async function updateCheck(github, owner, repo, checkRunId, status, description) {
const checkStatus = status == 'pending' ? 'in_progress' : 'completed';
const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure');
await github.rest.checks.update({
owner,
repo,
check_run_id: checkRunId,
status: checkStatus,
conclusion,
output: {
title: description,
summary: ''
}
});
}
const { owner, repo } = context.repo;
const sha =
context.payload?.check_suite?.sha || /* check run completed */
context.payload?.inputs?.commit || /* manual run */
context.payload?.after || /* push */
context.payload?.commit?.sha || /* status */
context.sha; /* probably 'master'! */
const checkRunId = await createCheck(github, owner, repo, sha);
const res = await test(github, owner, repo, sha);
await updateCheck(github, owner, repo, checkRunId, res[0], res[1]);
script: |
// This code is copied out of resources/build/pr-build-status/pr-build-status.mjs
// where it is tested. It is copied inline here in order to avoid requiring the
// repository to be checked out, which dramatically reduces the run time of the
// check.
//
// Note: we don't currently look at check runs, only statuses
//
// Verify the following statuses:
// 'user_testing'
// 'API Verification' (github-actions[bot])
//
// At least 1 of the following statuses must be found:
// 'Test*' (keyman-server), e.g. 'Test Build (Keyman)'
// 'Ubuntu Packaging' (github-actions[bot])
//
// Ignore the following statuses:
// check/web/file-size
//
function addLog(message) {
console.log(message);
return message + String.fromCharCode(10);
}
function addStatus(o, type, context, state) {
if(!o[context]) {
o[context] = {type, state};
return addLog(`Found ${context}: ${state}`);
} else {
return addLog(`Skipping repeated ${context}: ${state}`);
}
}
function reduceStatuses(statuses) {
let summary = '';
const filtered_statuses = statuses.reduce((o, status) => {
if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) {
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') {
summary += addStatus(o, 'user-test', status.context, status.state);
} else if(status.context == 'API Verification') {
summary += addStatus(o, 'check', status.context, status.state);
} else if(status.context == 'Ubuntu Packaging') {
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.context == 'check/web/file-size') {
// Ignore check/web/file-size -- we won't block automerge for this at this point
summary += addLog(`Skipping ${status.context}`);
} else {
// We fail with an 'unknown status' response if we get a new status check
// so we can be sure we are not skipping known status checks
summary += addLog(`Found unknown ${status.context}: ${status.state}`);
o[status.context] = {type: 'unknown', state: status.state};
}
return o;
}, {});
return { filtered_statuses, summary };
}
//
// Given the collection of status checks we care about, return
// an aggregate status -- error, failure, pending, or success,
// and a summary description
//
function calculateFinalStatus(filtered_statuses, summary) {
const counts = {};
let hasBuilds = false;
for(const context of Object.keys(filtered_statuses)) {
const { state, type } = filtered_statuses[context];
if(type == 'unknown') {
// We special-case for unknown status checks, and never permit them
console.error(`An unknown context ${context} was found, cannot calculate build status.`);
return [
'error', `An unknown context ${context} was found, cannot calculate build status.`, summary
];
}
if(type == 'build') {
hasBuilds = true;
}
counts[state] = counts[state] ? counts[state] + 1 : 1;
}
// If we do not have any statuses yet, we wait
if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) {
console.log(`Builds not yet triggered`);
return ['pending', 'Builds have not yet been triggered ⌛', summary];
}
const state =
counts.error ? 'error' :
counts.failure ? 'failure' :
counts.pending ? 'pending' :
'success';
let description = '';
function appendDescription(count, state) {
if(!count) return;
if(description != '') description += '; ';
description += `${count} check${count == 1 ? '' : 's'} ${state}`;
}
appendDescription(counts.error, 'in an error state ❌');
appendDescription(counts.failure, 'failed ❌');
appendDescription(counts.pending, 'pending ⌛');
appendDescription(counts.success, 'completed successfully ✅');
console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`);
return [ state, description, summary ];
}
async function getCommitStatuses(github, owner, repo, sha) {
console.log(`Getting commit statuses for ${sha}`);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', {
owner,
repo,
sha,
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
return statuses;
}
async function getCommitCheckRuns(github, owner, repo, sha) {
console.log(`Getting commit check runs for ${sha}`);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', {
owner,
repo,
sha,
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
});
return statuses;
}
function calculateCheckResult(statuses) {
if(!Array.isArray(statuses)) {
return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array'];
}
const { filtered_statuses, summary } = reduceStatuses(statuses);
return calculateFinalStatus(filtered_statuses, summary);
}
async function test(github, owner, repo, sha) {
// Get statuses from sha
const statuses = await getCommitStatuses(github, owner, repo, sha);
return calculateCheckResult(statuses);
}
async function createCheck(github, owner, repo, sha) {
const check = await github.rest.checks.create({
owner,
repo,
head_sha: sha,
name: 'Build Outcome',
status: 'in_progress',
});
return check.data.id;
}
async function updateCheck(github, owner, repo, checkRunId, status, description, summary) {
const checkStatus = status == 'pending' ? 'in_progress' : 'completed';
const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure');
await github.rest.checks.update({
owner,
repo,
check_run_id: checkRunId,
status: checkStatus,
conclusion,
output: {
title: description,
summary
}
});
}
const { owner, repo } = context.repo;
const sha =
context.payload?.check_suite?.sha || /* check run completed */
context.payload?.inputs?.commit || /* manual run */
context.payload?.after || /* push */
context.payload?.commit?.sha || /* status */
context.sha; /* probably 'master'! */
const checkRunId = await createCheck(github, owner, repo, sha);
const res = await test(github, owner, repo, sha);
await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]);

View file

@ -1,5 +1,50 @@
# Keyman Version History
## 19.0.117 alpha 2025-09-11
* fix(resources): ignore KEYMAN_VERSION_FOR_FILENAME in test (#14730)
* fix(resources): pr build status - find 'failure' state in status checks and log (#14732)
## 19.0.116 alpha 2025-09-10
* fix(windows): kmshell continues when sm is in waitingrestart and returns to idle (#14644)
* maint(linux): allow to skip API check (#14699)
* maint(resources): adding GitHub actions workflow for Docker builds (#14674)
* maint(developer): use versioned filenames for PR artifacts (#14701)
* maint(windows): use versioned filenames for PR artifacts (#14702)
* maint(android): use versioned filenames for PR artifacts (#14703)
* maint(ios): use versioned filenames for PR artifacts (#14704)
* maint(mac): use versioned filenames for PR artifacts (#14705)
* maint(resources): rename KEYMAN_VERSION_FOR_PR_FILENAME (#14720)
## 19.0.115 alpha 2025-09-09
* fix(linux): don't crash if `kmp.json` is missing `keyboards` section (#14708)
## 19.0.114 alpha 2025-09-08
* docs(web): Improve empty What's New page for Web (#14660)
* maint(linux): remove obsolete Makefile (#14694)
* maint(resources): honor configure comment character in commit message (#14695)
* fix(web): fix error trying to calculate style scaling effects (#14684)
* refactor(resources): remove shellcheck warnings (#14698)
## 19.0.113 alpha 2025-09-04
* fix(android): Update KMSample1 layout when changing orientation (#14678)
## 19.0.112 alpha 2025-09-03
* fix(developer): add clear messages about validation failures to New LM Project Parameters form (#14659)
* docs(developer): tweak get-started for kmc and add entry to HISTORY.md (#14667)
* fix(android): Cleanup list layout in Engine and FirstVoices for edge to edge (#14655)
* docs(common): update license date and owner (#14668)
* maint(linux): skip tarball creation for `Build-bot: build` buildLevel (#14671)
* docs(common): update docs Keyman Desktop → Keyman for Windows (#14669)
* feat(web): adds transform cataloging methods to predictive-text worker (#14581)
* feat(web): batch correction-search paths by related subsets (#14592)
* refactor(web): simplify edit-distance calculation-object inputs (#14618)
## 19.0.111 alpha 2025-09-02
* change(developer): update SIL logo (#14656)

View file

@ -1 +1 @@
19.0.112
19.0.118

View file

@ -46,7 +46,7 @@ MODEL_PACKAGE_ID="nrc.en.mtnt"
MODELS_TARGET="android/KMAPro/kMAPro/src/main/assets/${MODEL_PACKAGE_ID}.model.kmp"
builder_describe_outputs \
configure "/${MODELS_TARGET}" \
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk
build /android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk
#### Build
@ -77,13 +77,15 @@ if builder_start_action build; then
# Copy Keyman Engine for Android
cp "${KEYMAN_ROOT}/android/KMEA/app/build/outputs/aar/keyman-engine-${CONFIG}.aar" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/libs/keyman-engine.aar"
# Convert markdown to html for offline help
build_help_html android KMAPro/kMAPro/src/main/assets/info
echo "BUILD_FLAGS $BUILD_FLAGS"
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
mv "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/$CONFIG/keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk"
builder_finish_action success build
fi

View file

@ -2,7 +2,9 @@ package com.keyman.kmsample1;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
@ -25,6 +27,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
public static Context context;
private ConstraintLayout constraintLayout;
private KMTextView textView;
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -99,6 +102,18 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
protected void onResume() {
super.onResume();
KMManager.onResume();
// onConfigurationChanged() only triggers when device is rotated while app is in foreground
// This handles when device is rotated while app is in background
// using KMManager.getOrientation() since getConfiguration().orientation is unreliable #10241
int newOrientation = KMManager.getOrientation(context);
if (newOrientation != lastOrientation) {
lastOrientation = newOrientation;
Configuration newConfig = this.getResources().getConfiguration();
KMManager.onConfigurationChanged(newConfig);
}
resizeTextView(textView.isKeyboardVisible());
KMManager.addKeyboardEventListener(this);
KMKeyboardDownloaderActivity.addKeyboardDownloadEventListener(this);
}
@ -116,6 +131,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
KMManager.onConfigurationChanged(newConfig);
resizeTextView(textView.isKeyboardVisible());
lastOrientation = newConfig.orientation;
}
@Override
@ -131,11 +150,44 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
@Override
public void onKeyboardShown() {
// Handle Keyman keyboard shown event here if needed
resizeTextView(true);
}
@Override
public void onKeyboardDismissed() {
// Handle Keyman keyboard dismissed event here if needed
resizeTextView(false);
}
public void resizeTextView(boolean isKeyboardVisible) {
int bannerHeight = 0;
int keyboardHeight = 0;
if (isKeyboardVisible) {
bannerHeight = KMManager.getBannerHeight(this);
keyboardHeight = KMManager.getKeyboardHeight(this);
}
TypedValue outValue = new TypedValue();
getTheme().resolveAttribute(android.R.attr.actionBarSize, outValue, true);
int actionBarHeight = getResources().getDimensionPixelSize(outValue.resourceId);
// *** TO DO: Try to check if status bar is visible, set statusBarHeight to 0 if it is not visible ***
int statusBarHeight = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
// Navigation bar height
int navigationBarHeight = 0;
resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
}
Point size = KMManager.getWindowSize(context);
int screenHeight = size.y;
textView.setHeight(
screenHeight - statusBarHeight - actionBarHeight - bannerHeight - keyboardHeight - navigationBarHeight);
}
@Override

View file

@ -62,11 +62,12 @@ function do_test_help() {
function archive_artifacts() {
local UPLOAD_PATH KEYMAN_ENGINE_ANDROID_ZIP KEYMAN_APK FIRSTVOICES_APK
local KEYMAN_FULLY_VERSIONED_APK FIRSTVOICES_FULLY_VERSIONED_APK
UPLOAD_PATH="${KEYMAN_ROOT}/android/upload/${KEYMAN_VERSION}"
KEYMAN_ENGINE_ANDROID_ZIP="keyman-engine-android-${KEYMAN_VERSION}.zip"
KEYMAN_APK="keyman-${KEYMAN_VERSION}.apk"
FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION}.apk"
KEYMAN_APK="keyman-${KEYMAN_VERSION_FOR_FILENAME}.apk"
FIRSTVOICES_APK="firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.apk"
rm -rf "${UPLOAD_PATH}"
mkdir -p "${UPLOAD_PATH}"
@ -87,12 +88,12 @@ function archive_artifacts() {
)
# Copy release APK
cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}"
cp "${KEYMAN_ROOT}/android/KMAPro/kMAPro/build/outputs/apk/release/${KEYMAN_APK}" "${UPLOAD_PATH}/${KEYMAN_APK}"
# FirstVoices app
if [[ "${RELEASE_OEM_FIRSTVOICES-false}" = true ]]; then
cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}"
cp "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/release/${FIRSTVOICES_APK}" "${UPLOAD_PATH}/${FIRSTVOICES_APK}"
fi
#

View file

@ -8,7 +8,7 @@ and tutorials, and full reference information.
## Guides and Tutorials
- [What is Keyman Developer?](guides/intro)
- [What's new in 19.0](whats-new)
- [What's new in 19.0](whatsnew)
- [Developing Keyman keyboard layouts](guides/develop)
- [Testing Keyman keyboards](guides/test)
- [Distributing Keyman keyboards](guides/distribute)
@ -35,4 +35,4 @@ and tutorials, and full reference information.
- [Keyman Engine for Windows](/developer/engine/windows/current-version/)
- [Keyman Engine for Web](/developer/engine/web/)
- [Keyman Engine for iPhone and iPad](/developer/engine/iphone-and-ipad/current-version/)
- [Keyman Engine for Android](/developer/engine/android/current-version/)
- [Keyman Engine for Android](/developer/engine/android/current-version/)

View file

@ -16,7 +16,7 @@ builder_describe "Installation files for Keyman Developer" \
# after all other builds complete
builder_describe_outputs \
publish /developer/src/inst/keymandeveloper-${KEYMAN_VERSION}.exe
publish /developer/src/inst/keymandeveloper-${KEYMAN_VERSION_FOR_FILENAME}.exe
builder_parse "$@"
@ -78,7 +78,7 @@ function do_publish() {
# Copy the installation archive
#
copy-kmdev
copy-keymandeveloper-installer-to-artifacts
builder_if_release_build_level verify-installer-signatures
}
@ -87,8 +87,8 @@ function do_test() {
verify-node-installer-version
}
function copy-kmdev() {
builder_heading copy-kmdev
function copy-keymandeveloper-installer-to-artifacts() {
builder_heading copy-keymandeveloper-installer-to-artifacts
mkdir -p "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}"
@ -96,7 +96,7 @@ function copy-kmdev() {
make-kmc-install-zip
cp -f "$DEVELOPER_ROOT/src/inst/keymandeveloper.msi" "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}/keymandeveloper.msi"
cp -f "$DEVELOPER_ROOT/src/inst/keymandeveloper-${KEYMAN_VERSION}.exe" "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}/keymandeveloper-${KEYMAN_VERSION}.exe"
cp -f "$DEVELOPER_ROOT/src/inst/keymandeveloper-${KEYMAN_VERSION_FOR_FILENAME}.exe" "$DEVELOPER_ROOT/release/${KEYMAN_VERSION}/keymandeveloper-${KEYMAN_VERSION_FOR_FILENAME}.exe"
}
function verify-program-signatures() {
@ -234,8 +234,8 @@ function make-installer() {
echo "MSIFileName=keymandeveloper.msi" >> setup.inf
echo "Title=Keyman Developer ${KEYMAN_VERSION_WITH_TAG}" >>setup.inf
add_zip_files setup.zip keymandeveloper.msi setup.inf
cat "$DEVELOPER_PROGRAM/setup.exe" setup.zip > "keymandeveloper-$KEYMAN_VERSION.exe"
wrap-signcode //d "Keyman Developer" "keymandeveloper-$KEYMAN_VERSION.exe"
cat "$DEVELOPER_PROGRAM/setup.exe" setup.zip > "keymandeveloper-$KEYMAN_VERSION_FOR_FILENAME.exe"
wrap-signcode //d "Keyman Developer" "keymandeveloper-$KEYMAN_VERSION_FOR_FILENAME.exe"
#
# Zip the files we distribute as part of the standalone kmc distro into release\$Version\kmcomp-$Version.zip
@ -251,7 +251,7 @@ function make-kmc-install-zip() {
cd "${DEVELOPER_ROOT}/bin"
# TODO: rename this to keyman-developer-cli-$Version.zip
local KMCOMP_ZIP="${DEVELOPER_ROOT}/release/${KEYMAN_VERSION}/kmcomp-${KEYMAN_VERSION}.zip"
local KMCOMP_ZIP="${DEVELOPER_ROOT}/release/${KEYMAN_VERSION}/kmcomp-${KEYMAN_VERSION_FOR_FILENAME}.zip"
add_zip_files "${KMCOMP_ZIP}" -q -r \
kmconvert.exe \

View file

@ -99,6 +99,17 @@ the API version number in `CORE_API_VERSION.md`. If the API version
numbers in `CORE_API_VERSION.md` and in `libkeymancore.symbols` don't
match the API verification will fail.
## Skipping the API check
Sometimes we have a PR that intentionally does not change the API version
number. To skip the API checks in this case, the trailer
```text
Keyman-Api-Check: skip
```
can be added to the PR description.
## How this works
`.github/workflows/api-verification.yml` contains a `Verify API` step

View file

@ -73,6 +73,6 @@ https://help.keyman.com/developer/engine/android/latest-version/
| KEYMAN_VERSION_JAVA | 21 |
| KEYMAN_VERSION_LANGTAGS | 2025-02-18 |
| KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY | 2025-03-10 |
| KEYMAN_VERSION_UNICODE | 16.0.0 |
| KEYMAN_VERSION_UNICODE | 17.0.0 |
> ### This file is auto-generated by publish-minimum-versions.sh. Do not modify

View file

@ -29,8 +29,7 @@ fi
. $KEYMAN_ROOT/resources/build/history/history-utils.sh #includes the following
#. ../resources/build/utils.inc.sh
BUILD_NUMBER=`cat ../VERSION.md`
KEYMAN_TIER=`cat ../TIER.md`
BUILD_NUMBER="${KEYMAN_VERSION}"
KEYMAN_CHANGELOG="changelog-${BUILD_NUMBER}-${KEYMAN_TIER}.txt"
CHANGELOG_PATH="upload/$BUILD_NUMBER/$KEYMAN_CHANGELOG"
@ -59,7 +58,7 @@ mv -f "$CHANGELOG_PATH.1" "$CHANGELOG_PATH"
# Keyman Engine
#
KMEI_DST_NAME="keyman-engine-ios-${BUILD_NUMBER}.zip"
KMEI_DST_NAME="keyman-engine-ios-${KEYMAN_VERSION_FOR_FILENAME}.zip"
KMEI_DST="${WORK_DIR}/${UPLOAD_DIR}/${KMEI_DST_NAME}"
KMEI_FRAMEWORK_BASE="build/Build/Products/Release/"
@ -87,14 +86,14 @@ cd "$WORK_DIR"
#
KEYMANAPP_IPA="build/Build/Products/Release-iphoneos/Keyman.ipa"
KEYMANAPP_IPA_DST="keyman-ios-${BUILD_NUMBER}.ipa"
KEYMANAPP_IPA_DST="keyman-ios-${KEYMAN_VERSION_FOR_FILENAME}.ipa"
echo "Copying Keyman IPA ${KEYMANAPP_IPA} => ${UPLOAD_DIR}/${KEYMANAPP_IPA_DST}..."
cp "${KEYMANAPP_IPA}" "${WORK_DIR}/${UPLOAD_DIR}/${KEYMANAPP_IPA_DST}"
KEYMANAPP_SIM_FOLDER="build/Build/Products/Release-iphonesimulator"
KEYMANAPP_SIM_APP="$KEYMANAPP_SIM_FOLDER/Keyman.app"
KEYMANAPP_SIM_APP_DST="keyman-ios-simulator-${BUILD_NUMBER}.app.zip"
KEYMANAPP_SIM_APP_DST="keyman-ios-simulator-${KEYMAN_VERSION_FOR_FILENAME}.app.zip"
echo "Zipping Keyman simulator artifact ${KEYMANAPP_SIM_APP} => ${UPLOAD_DIR}/${KEYMANAPP_SIM_APP_DST}..."
cd "${KEYMANAPP_SIM_FOLDER}"
@ -108,14 +107,14 @@ cd "$WORK_DIR"
if [ "${RELEASE_OEM_FIRSTVOICES}" = true ]; then
FIRSTVOICESAPP_IPA="../oem/firstvoices/ios/build/Build/Products/Release-iphoneos/FirstVoices.ipa"
FIRSTVOICESAPP_IPA_DST="firstvoices-ios-${BUILD_NUMBER}.ipa"
FIRSTVOICESAPP_IPA_DST="firstvoices-ios-${KEYMAN_VERSION_FOR_FILENAME}.ipa"
echo "Copying FirstVoices IPA ${FIRSTVOICESAPP_IPA} => ${UPLOAD_DIR}/${FIRSTVOICESAPP_IPA_DST}..."
cp "${FIRSTVOICESAPP_IPA}" "${WORK_DIR}/${UPLOAD_DIR}/${FIRSTVOICESAPP_IPA_DST}"
FIRSTVOICESAPP_SIM_FOLDER="../oem/firstvoices/ios/build/Build/Products/Release-iphonesimulator/"
FIRSTVOICESAPP_SIM_APP="$FIRSTVOICESAPP_SIM_FOLDER/FirstVoices.app"
FIRSTVOICESAPP_SIM_APP_DST="firstvoices-ios-simulator-${BUILD_NUMBER}.app.zip"
FIRSTVOICESAPP_SIM_APP_DST="firstvoices-ios-simulator-${KEYMAN_VERSION_FOR_FILENAME}.app.zip"
echo "Zipping FirstVoices simulator artifact ${FIRSTVOICESAPP_SIM_APP} => ${UPLOAD_DIR}/${KEYMANAPP_SIM_APP_DST}..."
cd "${FIRSTVOICESAPP_SIM_FOLDER}"

View file

@ -1,49 +0,0 @@
#!/usr/bin/make
default: # builds only
BUILDONLY="yes" ./scripts/build.sh
clean:
rm -rf build-*
reconf: clean
./scripts/reconf.sh
configure:
CONFIGUREONLY="yes" ./scripts/build.sh
fullbuild: # configure and build
./scripts/build.sh
tmpinstall: reconf
INSTALLDIR="/tmp/keyman" ./scripts/build.sh
INSTALLDIR="/tmp/keyman" ./scripts/install.sh
install: # run as sudo
SUDOINSTALL="yes" ./scripts/install.sh
uninstall: # run as sudo
SUDOINSTALL="uninstall" ./scripts/install.sh
sources:
./scripts/dist.sh
tmpsources:
PKG_CONFIG_PATH="../../core/build/arch/release/meson-private" ./scripts/dist.sh
origdist:
./scripts/dist.sh origdist
sourcepackage: reconf origdist
./scripts/deb.sh
nightlydist: reconf sources
echo "Built nightly source release in dist/"
check:
cd keyman-config && ./run-tests.sh
cd ibus-keyman && ./build.sh test
tmpcheck: check
tests: check

View file

@ -178,9 +178,9 @@ class InstallKmp():
logging.info("Converting %s to LDML and installing both as as keyman file",
f['name'])
name, ext = os.path.splitext(f['name'])
ldml = convert_kvk_to_ldml(name, fpath)
ldmlfile = os.path.join(self.packageDir, f"{name}.ldml")
output_ldml(ldmlfile, ldml)
if ldml := convert_kvk_to_ldml(name, fpath):
ldmlfile = os.path.join(self.packageDir, f"{name}.ldml")
output_ldml(ldmlfile, ldml)
elif ftype == KMFileTypes.KM_ICON:
# Special handling of icon to convert to PNG
logging.info("Converting %s to PNG and installing both as keyman files",

View file

@ -366,14 +366,17 @@ def convert_ldml(keyboardName, kvkData, kmpJsonFilename):
info, system, options, keyboards, files = parsemetadata(kmpJsonFilename)
if not keyboards:
return None
ldml = etree.Element("keyboard", locale="zzz-keyman")
for keyboard in keyboards:
if keyboard['id'] != keyboardName:
continue
if 'oskFont' in keyboard:
fontFile = os.path.join(os.path.dirname(kmpJsonFilename), keyboard['oskFont'])
font = _fontFacename(fontFile)
if font:
if font := _fontFacename(fontFile):
ldml.set('keymanFacename', font)
break

View file

@ -8,31 +8,34 @@ import unittest
from keyman_config.kvk2ldml import KVKData, NFont, convert_ldml
class Kvk2LdmlTests(unittest.TestCase):
def _createKmpJson(self, packagedir):
def _createKmpJson(self, packagedir, add_keyboards=True):
kmpJsonFilename = os.path.join(packagedir, 'kmp.json')
system = '''"system": {
"keymanDeveloperVersion": "18.0",
"fileVersion": "7.0"
}'''
files = '''"files": [ {
"name": "khmer_angkor.kmx",
"description": "Keyboard Khmer Angkor"
}, {
"name": "kmp.json",
"description": "Package information (JSON)"
} ]'''
if add_keyboards:
keyboards = '''"keyboards": [ {
"name": "Khmer Angkor",
"id": "khmer_angkor",
"version": "1.5",
"oskFont": "keymanweb-osk.ttf",
"languages": [ {
"name": "Central Khmer (Khmer, Cambodia)",
"id": "km"
} ]}
]'''
else:
keyboards = ''
with open(kmpJsonFilename, 'w') as file:
file.write('''{
"system": {
"keymanDeveloperVersion": "18.0",
"fileVersion": "7.0"
},
"files": [ {
"name": "khmer_angkor.kmx",
"description": "Keyboard Khmer Angkor"
}, {
"name": "kmp.json",
"description": "Package information (JSON)"
} ],
"keyboards": [ {
"name": "Khmer Angkor",
"id": "khmer_angkor",
"version": "1.5",
"oskFont": "keymanweb-osk.ttf",
"languages": [ {
"name": "Central Khmer (Khmer, Cambodia)",
"id": "km"
} ]}
]}''')
file.write(f'{{ {system}, {files}, {keyboards} }}')
return kmpJsonFilename
def test_convert_ldml__adds_keymanFacename(self):
@ -56,3 +59,20 @@ class Kvk2LdmlTests(unittest.TestCase):
self.assertEqual(ldml.get('keymanFacename'), 'SymChar')
workdir.cleanup()
def test_convert_ldml__no_crash_without_keyboards(self):
# Setup
keyboardName = 'khmer_angkor'
kvkData = KVKData()
kvkData.AssociatedKeyboard = keyboardName
workdir = tempfile.TemporaryDirectory()
kmpJsonFilename = self._createKmpJson(workdir.name, add_keyboards=False)
# Execute
ldml = convert_ldml(keyboardName, kvkData, kmpJsonFilename)
# Verify
self.assertIsNone(ldml)
workdir.cleanup()

View file

@ -212,7 +212,7 @@ if (( DETACH_SUCCESS < 999 )); then
fi
# Step 5 - Convert image to a compressed readonly DMG image
DMG_FILE_PATH="$DEST_DIR/keyman-$KEYMAN_VERSION.dmg"
DMG_FILE_PATH="$DEST_DIR/keyman-$KEYMAN_VERSION_FOR_FILENAME.dmg"
builder_echo info "Converting/compressing image to create \"$DMG_FILE_PATH\""
if [[ -e "$DMG_FILE_PATH" ]] ; then
if [[ "$VERBOSITY" != "-quiet" ]] ; then

View file

@ -293,7 +293,7 @@ do_publish() {
"${KM4MIM_BASE_PATH}/make-km-dmg.sh"
local UPLOAD_PATH="${KM4MIM_BASE_PATH}/output/upload/${KEYMAN_VERSION}"
write_download_info "${UPLOAD_PATH}" "keyman-${KEYMAN_VERSION}.dmg" "Keyman4MacIM" dmg mac
write_download_info "${UPLOAD_PATH}" "keyman-${KEYMAN_VERSION_FOR_FILENAME}.dmg" "Keyman4MacIM" dmg mac
if builder_is_ci_build && builder_is_ci_build_level_release; then
do_sentry

View file

@ -38,7 +38,7 @@ if builder_is_debug_build; then
TEST_FLAGS="-x assembleDebug lintDebug testDebug"
fi
ARTIFACT="firstvoices-$KEYMAN_VERSION.apk"
ARTIFACT="firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.apk"
KEYBOARD_PACKAGE_ID="fv_all"
KEYBOARDS_TARGET="oem/firstvoices/android/app/src/main/assets/${KEYBOARD_PACKAGE_ID}.kmp"
@ -76,6 +76,8 @@ if builder_start_action build; then
echo "BUILD_FLAGS: $BUILD_FLAGS"
./gradlew $DAEMON_FLAG clean $BUILD_FLAGS
mv "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/$CONFIG/firstvoices-${KEYMAN_VERSION}.apk" "${KEYMAN_ROOT}/oem/firstvoices/android/app/build/outputs/apk/$CONFIG/${ARTIFACT}"
builder_finish_action success build
fi

View file

@ -16,7 +16,7 @@ builder_describe "Installation files for FirstVoices Keyboards" \
# after all other builds complete
builder_if_release_build_level builder_describe_outputs \
publish /windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION}.exe
publish /windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.exe
builder_parse "$@"
@ -103,7 +103,7 @@ function copy-installer() {
mkdir -p "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}"
cp firstvoices.msi "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices.msi"
cp firstvoices.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION}.exe"
cp firstvoices.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION_FOR_FILENAME}.exe"
verify-installer-signatures
}

View file

@ -13,6 +13,7 @@
# KEYMAN_VERSION_GIT_TAG: Git tag for the release, "release@$KEYMAN_VERSION_WITH_TAG", e.g. "release@14.0.1-alpha-test-1234"
# KEYMAN_ROOT: fully resolved root path of Keyman repository
# KEYMAN_VERSION_ENVIRONMENT: One of: local, test, alpha, beta, stable
# KEYMAN_VERSION_FOR_FILENAME: KEYMAN_VERSION, for release builds, or KEYMAN_VERSION_WITH_TAG, for PR and local builds; see #10521
# UPLOAD_SENTRY: true - if KEYMAN_VERSION_ENVIRONMENT is one of alpha, beta, stable
# false - if local, test. Indicates if debug artifacts should be uploaded to Sentry
# BUILDER_OS: win|mac|linux -- current build environment
@ -115,6 +116,16 @@ function _builder_basic_find_version() {
KEYMAN_VERSION_WITH_TAG="$KEYMAN_VERSION$KEYMAN_VERSION_TAG"
KEYMAN_VERSION_GIT_TAG="release@$KEYMAN_VERSION_WITH_TAG"
# For local builds, and for PR builds, we will use a filename including the tag
# but for release builds, we omit the tag for now. In the future, we may include
# the tag but that involves changing a number of other websites as dealing with
# the rename there too.
if builder_is_ci_release_build; then
KEYMAN_VERSION_FOR_FILENAME="${KEYMAN_VERSION}"
else
KEYMAN_VERSION_FOR_FILENAME="${KEYMAN_VERSION_WITH_TAG}"
fi
readonly KEYMAN_VERSION
readonly KEYMAN_VERSION_MAJOR
readonly KEYMAN_VERSION_MINOR
@ -125,6 +136,7 @@ function _builder_basic_find_version() {
readonly KEYMAN_VERSION_WITH_TAG
readonly KEYMAN_VERSION_ENVIRONMENT
readonly KEYMAN_VERSION_GIT_TAG
readonly KEYMAN_VERSION_FOR_FILENAME
# Export version strings so places like version.gradle can access them
export KEYMAN_VERSION
@ -137,6 +149,7 @@ function _builder_basic_find_version() {
export KEYMAN_VERSION_WITH_TAG
export KEYMAN_VERSION_ENVIRONMENT
export KEYMAN_VERSION_GIT_TAG
export KEYMAN_VERSION_FOR_FILENAME
}
function _builder_basic_find_tier() {
@ -163,18 +176,19 @@ function _builder_basic_print_build_number_for_teamcity() {
}
function _builder_basic_print_version_utils_debug() {
echo "KEYMAN_ROOT: $KEYMAN_ROOT"
echo "KEYMAN_VERSION: $KEYMAN_VERSION"
echo "KEYMAN_VERSION_WIN: $KEYMAN_VERSION_WIN"
echo "KEYMAN_VERSION_RELEASE: $KEYMAN_VERSION_RELEASE"
echo "KEYMAN_VERSION_MAJOR: $KEYMAN_VERSION_MAJOR"
echo "KEYMAN_VERSION_MINOR: $KEYMAN_VERSION_MINOR"
echo "KEYMAN_VERSION_PATCH: $KEYMAN_VERSION_PATCH"
echo "KEYMAN_TIER: $KEYMAN_TIER"
echo "KEYMAN_VERSION_TAG: $KEYMAN_VERSION_TAG"
echo "KEYMAN_VERSION_WITH_TAG: $KEYMAN_VERSION_WITH_TAG"
echo "KEYMAN_VERSION_GIT_TAG: $KEYMAN_VERSION_GIT_TAG"
echo "KEYMAN_VERSION_ENVIRONMENT: $KEYMAN_VERSION_ENVIRONMENT"
echo "KEYMAN_ROOT: $KEYMAN_ROOT"
echo "KEYMAN_VERSION: $KEYMAN_VERSION"
echo "KEYMAN_VERSION_WIN: $KEYMAN_VERSION_WIN"
echo "KEYMAN_VERSION_RELEASE: $KEYMAN_VERSION_RELEASE"
echo "KEYMAN_VERSION_MAJOR: $KEYMAN_VERSION_MAJOR"
echo "KEYMAN_VERSION_MINOR: $KEYMAN_VERSION_MINOR"
echo "KEYMAN_VERSION_PATCH: $KEYMAN_VERSION_PATCH"
echo "KEYMAN_TIER: $KEYMAN_TIER"
echo "KEYMAN_VERSION_TAG: $KEYMAN_VERSION_TAG"
echo "KEYMAN_VERSION_WITH_TAG: $KEYMAN_VERSION_WITH_TAG"
echo "KEYMAN_VERSION_GIT_TAG: $KEYMAN_VERSION_GIT_TAG"
echo "KEYMAN_VERSION_ENVIRONMENT: $KEYMAN_VERSION_ENVIRONMENT"
echo "KEYMAN_VERSION_FOR_FILENAME: $KEYMAN_VERSION_FOR_FILENAME"
}
# TODO: consolidate with buildLevel, see #14285
@ -209,6 +223,7 @@ replaceVersionStrings() {
s/\$KEYMAN_VERSION_GIT_TAG/$KEYMAN_VERSION_GIT_TAG/g;
s/\$KEYMAN_VERSION_ENVIRONMENT/$KEYMAN_VERSION_ENVIRONMENT/g;
s/\$KEYMAN_VERSION/$KEYMAN_VERSION/g;
s/\$KEYMAN_VERSION_FOR_FILENAME/$KEYMAN_VERSION_FOR_FILENAME/g;
" "$infile" > "$outfile"
}
@ -249,6 +264,7 @@ replaceVersionStrings_Mkver() {
s/\$KEYMAN_VERSION_WITH_TAG/$KEYMAN_VERSION_WITH_TAG/g;
s/\$KEYMAN_VERSION_GIT_TAG/$KEYMAN_VERSION_GIT_TAG/g;
s/\$KEYMAN_VERSION_ENVIRONMENT/$KEYMAN_VERSION_ENVIRONMENT/g;
s/\$KEYMAN_VERSION_FOR_FILENAME/$KEYMAN_VERSION_FOR_FILENAME/g;
s/\$KEYMAN_VERSION/$KEYMAN_VERSION_WIN/g;

View file

@ -39,6 +39,34 @@ function test_bot_check_pr_body() {
fi
}
#
# Check the Keyman-Api-Check command whether or not to run API checks.
# Any value other than 'skip' or no Keyman-Api-Check command will run
# the API checks.
#
# Parameters:
# 1: PR number (not currently used)
# 2: PR JSON data from api.github.com/repos/keymanapp/keyman/pulls/#
#
function is_skip_keyman_api_check() {
local PRNUM=$1
local prinfo="$2"
local prbody prApiCheck
set -o noglob
IFS=$'\n'
prbody="$(echo "${prinfo}" | "${JQ}" -r '.body')"
prApiCheck="$(echo -e "${prbody}" | grep '^Keyman-Api-Check:' | cut -d: -f 2 - | tr -d '[:space:]')"
unset IFS
set +o noglob
if [[ "${prApiCheck}" == "skip" ]]; then
echo "true"
else
echo "false"
fi
}
#
# Check PR commit messages for Build-bot commands. Later commands override
# earlier ones. Also checks PR body for any overriding commands. Note, only

View file

@ -84,31 +84,34 @@ function triggerTeamCityBuild() {
# 2: 'skip' - don't run build; 'build' - run build and unit tests; 'test' - run build, unit tests, and deploy
# 3: Action name
# 4: branch name in git
# 5: 'true' to skip API checks, 'false' to run them (optional, default 'false')
#
function triggerGitHubActionsBuild() {
local IS_TEST_BUILD="$1"
local BUILD_LEVEL="$2"
local GITHUB_ACTION="$3"
local GIT_BRANCH="$4"
local SKIP_API_CHECK="${5:-"false"}"
local GIT_BASE_BRANCH="${GIT_BRANCH}"
local GIT_USER="keyman-server"
local GIT_BUILD_SHA GIT_BASE_REF JSON
local GITHUB_SERVER=https://api.github.com/repos/keymanapp/keyman
if [ "${action:-""}" == "commit" ]; then
if [[ "${action:-""}" == "commit" ]]; then
# This will only be true if we created and pushed a tag
GIT_BUILD_SHA="$(git rev-parse "refs/tags/release@$KEYMAN_VERSION_WITH_TAG")"
GIT_BUILD_SHA="$(git rev-parse "refs/tags/release@${KEYMAN_VERSION_WITH_TAG}")"
GIT_BASE_REF="$(git rev-parse "${GIT_BUILD_SHA}^")"
GIT_EVENT_TYPE="${GITHUB_ACTION}: release@${KEYMAN_VERSION_WITH_TAG}"
elif [[ $GIT_BRANCH =~ ^[0-9]+$ ]]; then
elif [[ ${GIT_BRANCH} =~ ^[0-9]+$ ]]; then
# pull request
local JSON=$(call_curl "${GITHUB_SERVER}/pulls/${GIT_BRANCH}" --header "Authorization: token $GITHUB_TOKEN")
GIT_BUILD_SHA="$(echo "$JSON" | $JQ -r '.head.sha')"
local JSON
JSON=$(call_curl "${GITHUB_SERVER}/pulls/${GIT_BRANCH}" --header "Authorization: token ${GITHUB_TOKEN}")
GIT_BUILD_SHA="$(echo "${JSON}" | ${JQ} -r '.head.sha')"
GIT_EVENT_TYPE="${GITHUB_ACTION}: PR #${GIT_BRANCH}"
GIT_USER="$(echo "$JSON" | $JQ -r '.user.login')"
GIT_BASE_BRANCH="$(echo "$JSON" | $JQ -r '.base.ref')"
GIT_BASE_REF="$(echo "$JSON" | $JQ -r '.base.sha')"
GIT_USER="$(echo "${JSON}" | ${JQ} -r '.user.login')"
GIT_BASE_BRANCH="$(echo "${JSON}" | ${JQ} -r '.base.ref')"
GIT_BASE_REF="$(echo "${JSON}" | ${JQ} -r '.base.sha')"
GIT_BRANCH="PR-${GIT_BRANCH}"
else
# another branch: stable-x.y, beta, or master
@ -118,20 +121,21 @@ function triggerGitHubActionsBuild() {
fi
local DATA="{
\"event_type\": \"$GIT_EVENT_TYPE\", \
\"event_type\": \"${GIT_EVENT_TYPE}\", \
\"client_payload\": { \
\"buildSha\": \"$GIT_BUILD_SHA\", \
\"branch\": \"$GIT_BRANCH\", \
\"baseBranch\": \"$GIT_BASE_BRANCH\", \
\"baseRef\": \"$GIT_BASE_REF\", \
\"user\": \"$GIT_USER\", \
\"isTestBuild\": \"$IS_TEST_BUILD\", \
\"buildLevel\": \"$BUILD_LEVEL\" \
\"buildSha\": \"${GIT_BUILD_SHA}\", \
\"branch\": \"${GIT_BRANCH}\", \
\"baseBranch\": \"${GIT_BASE_BRANCH}\", \
\"baseRef\": \"${GIT_BASE_REF}\", \
\"user\": \"${GIT_USER}\", \
\"isTestBuild\": \"${IS_TEST_BUILD}\", \
\"buildLevel\": \"${BUILD_LEVEL}\", \
\"skipApiCheck\": \"${SKIP_API_CHECK}\" \
}}"
echo "GitHub Action Data: $DATA"
echo "GitHub Action Data: ${DATA}"
if [[ "$GIT_BUILD_SHA" == null ]]; then
if [[ "${GIT_BUILD_SHA}" == null ]]; then
echo "Invalid GIT_BUILD_SHA"
exit 1
fi
@ -141,8 +145,8 @@ function triggerGitHubActionsBuild() {
call_curl "${GITHUB_SERVER}/dispatches" \
--request POST \
--header "Accept: application/vnd.github+json" \
--header "Authorization: token $GITHUB_TOKEN" \
--data "$DATA"
--header "Authorization: token ${GITHUB_TOKEN}" \
--data "${DATA}"
echo
}
@ -157,31 +161,32 @@ function call_curl() {
local curl_result
local http_code
local output_file=$(mktemp)
local output_file
output_file=$(mktemp)
set +e
if [[ $# -gt 0 ]]; then
http_code=$(curl --silent --output $output_file --no-progress-meter --write-out "%{http_code}" "$@" "$url")
http_code=$(curl --silent --output "${output_file}" --no-progress-meter --write-out "%{http_code}" "$@" "${url}")
else
http_code=$(curl --silent --output $output_file --no-progress-meter --write-out "%{http_code}" "$url")
http_code=$(curl --silent --output "${output_file}" --no-progress-meter --write-out "%{http_code}" "${url}")
fi
curl_result=$?
if [[ ${curl_result} != 0 ]]; then
echo "curl call to '${url}' failed with code '${curl_result}'" >& 2
>&2 cat $output_file
rm -f $output_file
exit $curl_result
>&2 cat "${output_file}"
rm -f "${output_file}"
exit ${curl_result}
fi
if [[ ${http_code} -lt 200 || ${http_code} -gt 299 ]] ; then
echo "curl call to '${url}' failed with HTTP error code '${http_code}'" >& 2
>&2 cat $output_file
rm -f $output_file
>&2 cat "${output_file}"
rm -f "${output_file}"
exit 22
fi
set -e
cat $output_file
rm -f $output_file
cat "${output_file}"
rm -f "${output_file}"
}

View file

@ -112,7 +112,7 @@ vcs_test=HttpsGithubComKeymanappKeymanPRs
bc_master_android=(KeymanAndroid_Build)
bc_master_ios=(Keyman_iOS_Master)
bc_master_linux=(KeymanLinux_Master deb-release-packaging_GitHub)
bc_master_linux=(KeymanLinux_Master deb-release-packaging_GitHub build-test-publish-docker_GitHub)
bc_master_mac=(KeymanMac_Master)
bc_master_windows=(Keyman_Build)
bc_master_web=(Keymanweb_Build)

View file

@ -3,7 +3,7 @@
# This is a list of minimum, maximum, and specific versions of any external
# components or dependencies found in Keyman.
#
# Original (outdated) reference
# Original (outdated) reference
# https://docs.google.com/document/d/1Uy3U2YXeA4rCEbUbT7O6QzUGJDZBeViFecL8fjUOLkE/edit?usp=sharing
# NOTE: After changing any values, you should run publish-minimum-versions.sh.
@ -50,4 +50,4 @@ KEYMAN_VERSION_CLDR=46 # LDML Keyboards version
KEYMAN_VERSION_ISO639_3=2024-05-22 # Date of last import
KEYMAN_VERSION_LANGTAGS=2025-02-18 # _version value
KEYMAN_VERSION_LANGUAGE_SUBTAG_REGISTRY=2025-03-10 # Date from first line of language-subtag-registry
KEYMAN_VERSION_UNICODE=16.0.0 # UCD + related data
KEYMAN_VERSION_UNICODE=17.0.0 # UCD + related data

View file

@ -19,7 +19,7 @@ function do_build() {
local YML_PREFIX=$(cat "$YML_PREFIX_FILE")
local SCRIPT_CLONE=$(sed -n '/CLONE:START/,/CLONE:END/{//!p}' < "$MJS_FILE")
local SCRIPT_SUFFIX=$(sed -n '/CLONE-COMMENTED:START/,/CLONE-COMMENTED:END/{//!p}' < "$MJS_FILE" | sed 's/\/\/ //g')
echo -e "# GENERATED FILE - DO NOT EDIT!\n$YML_PREFIX\n$SCRIPT_CLONE\n$SCRIPT_SUFFIX\n" > "$YML_FILE"
echo $'# GENERATED FILE - DO NOT EDIT!\n'"$YML_PREFIX"$'\n'"$SCRIPT_CLONE"$'\n'"$SCRIPT_SUFFIX"$'\n' > "$YML_FILE"
}
builder_run_action build do_build

View file

@ -0,0 +1,660 @@
[
{
"id": 50109411782,
"name": "Build Outcome",
"node_id": "CR_kwDOAY2xT88AAAALqsDxxg",
"head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb",
"external_id": "",
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50109411782",
"html_url": "https://github.com/keymanapp/keyman/runs/50109411782",
"details_url": "https://github.com/keymanapp/keyman/runs/50109411782",
"status": "completed",
"conclusion": "success",
"started_at": "2025-09-11T05:29:44Z",
"completed_at": "2025-09-11T05:29:45Z",
"output": {
"title": "21 checks completed successfully ✅",
"summary": "",
"text": null,
"annotations_count": 0,
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50109411782/annotations"
},
"check_suite": {
"id": 45240794914
},
"app": {
"id": 15368,
"client_id": "Iv1.05c79e9ad1f6bdfa",
"slug": "github-actions",
"node_id": "MDM6QXBwMTUzNjg=",
"owner": {
"login": "github",
"id": 9919,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github",
"html_url": "https://github.com/github",
"followers_url": "https://api.github.com/users/github/followers",
"following_url": "https://api.github.com/users/github/following{/other_user}",
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
"organizations_url": "https://api.github.com/users/github/orgs",
"repos_url": "https://api.github.com/users/github/repos",
"events_url": "https://api.github.com/users/github/events{/privacy}",
"received_events_url": "https://api.github.com/users/github/received_events",
"type": "Organization",
"user_view_type": "public",
"site_admin": false
},
"name": "GitHub Actions",
"description": "Automate your workflow from idea to production",
"external_url": "https://help.github.com/en/actions",
"html_url": "https://github.com/apps/github-actions",
"created_at": "2018-07-30T09:30:17Z",
"updated_at": "2025-03-07T16:35:00Z",
"permissions": {
"actions": "write",
"administration": "read",
"attestations": "write",
"checks": "write",
"contents": "write",
"deployments": "write",
"discussions": "write",
"issues": "write",
"merge_queues": "write",
"metadata": "read",
"models": "read",
"packages": "write",
"pages": "write",
"pull_requests": "write",
"repository_hooks": "write",
"repository_projects": "write",
"security_events": "write",
"statuses": "write",
"vulnerability_alerts": "read"
},
"events": [
"branch_protection_rule",
"check_run",
"check_suite",
"create",
"delete",
"deployment",
"deployment_status",
"discussion",
"discussion_comment",
"fork",
"gollum",
"issues",
"issue_comment",
"label",
"merge_group",
"milestone",
"page_build",
"project",
"project_card",
"project_column",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"push",
"registry_package",
"release",
"repository",
"repository_dispatch",
"status",
"watch",
"workflow_dispatch",
"workflow_run"
]
},
"pull_requests": [
{
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728",
"id": 2817549354,
"number": 14728,
"head": {
"ref": "chore/common/unicode-17",
"sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
},
"base": {
"ref": "master",
"sha": "898cd340a33f29b766b08da66a519be7e3d79fe6",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
}
}
]
},
{
"id": 50107239504,
"name": "GitGuardian Security Checks",
"node_id": "CR_kwDOAY2xT88AAAALqp_MUA",
"head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb",
"external_id": "",
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107239504",
"html_url": "https://github.com/keymanapp/keyman/runs/50107239504",
"details_url": "https://dashboard.gitguardian.com",
"status": "completed",
"conclusion": "success",
"started_at": "2025-09-11T04:38:47Z",
"completed_at": "2025-09-11T04:38:48Z",
"output": {
"title": "No secrets detected ✅",
"summary": "**2** commits were scanned without uncovering any secrets.\n",
"text": "Commits scanned: **2**\n\n\n\n- Pull request #14728: `chore/common/unicode-17` 👉 `master`\n \n\n🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&amp;utm_source=github&amp;utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/>\n",
"annotations_count": 0,
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107239504/annotations"
},
"check_suite": {
"id": 45240794052
},
"app": {
"id": 46505,
"client_id": "Iv1.ec3c001966b4cc5a",
"slug": "gitguardian",
"node_id": "MDM6QXBwNDY1MDU=",
"owner": {
"login": "GitGuardian",
"id": 27360172,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjI3MzYwMTcy",
"avatar_url": "https://avatars.githubusercontent.com/u/27360172?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/GitGuardian",
"html_url": "https://github.com/GitGuardian",
"followers_url": "https://api.github.com/users/GitGuardian/followers",
"following_url": "https://api.github.com/users/GitGuardian/following{/other_user}",
"gists_url": "https://api.github.com/users/GitGuardian/gists{/gist_id}",
"starred_url": "https://api.github.com/users/GitGuardian/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/GitGuardian/subscriptions",
"organizations_url": "https://api.github.com/users/GitGuardian/orgs",
"repos_url": "https://api.github.com/users/GitGuardian/repos",
"events_url": "https://api.github.com/users/GitGuardian/events{/privacy}",
"received_events_url": "https://api.github.com/users/GitGuardian/received_events",
"type": "Organization",
"user_view_type": "public",
"site_admin": false
},
"name": "GitGuardian",
"description": "# 🦉 What is GitGuardian?\r\n\r\nGitGuardian Secrets Detection detects and fixes vulnerabilities in source code at every step of the software development lifecycle, covering 350+ types of secrets like API keys, database connection strings, private keys, certificates, and more. The platform’s policy engine enables security teams to monitor and enforce rules across their VCS, DevOps tools, and infrastructure-as-code configurations. Our automated remediation playbooks and collaboration features bring security and development teams together to resolve incidents fast and in full.\r\n\r\n## 1. Scan your codebase for 350+ types of secrets\r\nGitGuardian scans your GitHub repositories and raises alerts only for critical secrets, such as API keys or other credentials. At scale, GitGuardian’s detection algorithm has been battle-tested on over three years of activity in all public GitHub repositories – totaling over 1 billion scanned commits!\r\n\r\n## 2. Quickly remediate your hard-coded secrets\r\nIf you ever experience a leak involving a credential, we have a complete remediation guide used by 100k+ developers each year. We’ll show you how to revoke the secret and remove it from your git history.\r\n\r\n## 3. Prevent secrets from reaching GitHub\r\nInstall ggshield, the GitGuardian CLI, and add secrets detection to your local development workflow using pre-commit and pre-push git hooks integrations.\r\n\r\n# 🙋‍♂️ FAQ\r\n\r\n**What is your pricing?**\r\nGitGuardian is free for teams under 25 developers and offers a 30-day trial for larger teams.\r\n\r\n**How can I be sure that GitGuardian won’t raise too many false positives?**\r\nWe have scanned billions of commits, sent millions of alerts since 2018, and integrated each feedback to improve our algorithm. Our alerts currently receive 91% “true positive” feedback from developers.\r\n\r\n**My repositories are private; why should I install automated secret detection?**\r\nImagine if there were a plain text file with all your credit card numbers inside, you wouldn’t put this file inside your company’s git repository. Secrets are just as sensitive and should be handled with special care.\r\n\r\n**Is GitGuardian available to be installed on-premise?**\r\nYes, you can contact one of our security specialists to look over the possibility of installing GitGuardian on-premise on your repositories.\r\n\r\n# ⚒️ Installation notes \r\n\r\nYou should install GitGuardian directly through your [GitGuardian](https://dashboard.gitguardian.com/) workspace on the Integration settings page. \r\n\r\nSo that you know, your GitHub organization or GitHub account can only be associated with a single GitGuardian workspace.\r\n\r\n# 👋 Support\r\n\r\nIf you experience any difficulties or have any questions, please reach out to us by email ([support@gitguardian.com](mailto:support@gitguardian.com)).",
"external_url": "https://dashboard.gitguardian.com",
"html_url": "https://github.com/apps/gitguardian",
"created_at": "2019-11-12T15:44:31Z",
"updated_at": "2023-07-18T09:06:22Z",
"permissions": {
"checks": "write",
"contents": "read",
"emails": "read",
"issues": "write",
"members": "read",
"metadata": "read",
"organization_hooks": "write",
"pull_requests": "write"
},
"events": [
"check_run",
"check_suite",
"commit_comment",
"create",
"delete",
"organization",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"push",
"repository"
]
},
"pull_requests": [
{
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728",
"id": 2817549354,
"number": 14728,
"head": {
"ref": "chore/common/unicode-17",
"sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
},
"base": {
"ref": "master",
"sha": "898cd340a33f29b766b08da66a519be7e3d79fe6",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
}
}
]
},
{
"id": 50107238217,
"name": "build",
"node_id": "CR_kwDOAY2xT88AAAALqp_HSQ",
"head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb",
"external_id": "584213e9-9caa-5e7a-be2b-4a4fb7afc574",
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238217",
"html_url": "https://github.com/keymanapp/keyman/actions/runs/17634180116/job/50107238217",
"details_url": "https://github.com/keymanapp/keyman/actions/runs/17634180116/job/50107238217",
"status": "completed",
"conclusion": "skipped",
"started_at": "2025-09-11T04:38:46Z",
"completed_at": "2025-09-11T04:38:45Z",
"output": {
"title": null,
"summary": null,
"text": null,
"annotations_count": 0,
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238217/annotations"
},
"check_suite": {
"id": 45240899113
},
"app": {
"id": 15368,
"client_id": "Iv1.05c79e9ad1f6bdfa",
"slug": "github-actions",
"node_id": "MDM6QXBwMTUzNjg=",
"owner": {
"login": "github",
"id": 9919,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github",
"html_url": "https://github.com/github",
"followers_url": "https://api.github.com/users/github/followers",
"following_url": "https://api.github.com/users/github/following{/other_user}",
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
"organizations_url": "https://api.github.com/users/github/orgs",
"repos_url": "https://api.github.com/users/github/repos",
"events_url": "https://api.github.com/users/github/events{/privacy}",
"received_events_url": "https://api.github.com/users/github/received_events",
"type": "Organization",
"user_view_type": "public",
"site_admin": false
},
"name": "GitHub Actions",
"description": "Automate your workflow from idea to production",
"external_url": "https://help.github.com/en/actions",
"html_url": "https://github.com/apps/github-actions",
"created_at": "2018-07-30T09:30:17Z",
"updated_at": "2025-03-07T16:35:00Z",
"permissions": {
"actions": "write",
"administration": "read",
"attestations": "write",
"checks": "write",
"contents": "write",
"deployments": "write",
"discussions": "write",
"issues": "write",
"merge_queues": "write",
"metadata": "read",
"models": "read",
"packages": "write",
"pages": "write",
"pull_requests": "write",
"repository_hooks": "write",
"repository_projects": "write",
"security_events": "write",
"statuses": "write",
"vulnerability_alerts": "read"
},
"events": [
"branch_protection_rule",
"check_run",
"check_suite",
"create",
"delete",
"deployment",
"deployment_status",
"discussion",
"discussion_comment",
"fork",
"gollum",
"issues",
"issue_comment",
"label",
"merge_group",
"milestone",
"page_build",
"project",
"project_card",
"project_column",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"push",
"registry_package",
"release",
"repository",
"repository_dispatch",
"status",
"watch",
"workflow_dispatch",
"workflow_run"
]
},
"pull_requests": [
{
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728",
"id": 2817549354,
"number": 14728,
"head": {
"ref": "chore/common/unicode-17",
"sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
},
"base": {
"ref": "master",
"sha": "898cd340a33f29b766b08da66a519be7e3d79fe6",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
}
}
]
},
{
"id": 50107238052,
"name": "triage",
"node_id": "CR_kwDOAY2xT88AAAALqp_GpA",
"head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb",
"external_id": "90e392c4-1805-5238-9d01-622e4d746355",
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238052",
"html_url": "https://github.com/keymanapp/keyman/actions/runs/17634180101/job/50107238052",
"details_url": "https://github.com/keymanapp/keyman/actions/runs/17634180101/job/50107238052",
"status": "completed",
"conclusion": "success",
"started_at": "2025-09-11T04:38:47Z",
"completed_at": "2025-09-11T04:38:52Z",
"output": {
"title": null,
"summary": null,
"text": null,
"annotations_count": 0,
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107238052/annotations"
},
"check_suite": {
"id": 45240899080
},
"app": {
"id": 15368,
"client_id": "Iv1.05c79e9ad1f6bdfa",
"slug": "github-actions",
"node_id": "MDM6QXBwMTUzNjg=",
"owner": {
"login": "github",
"id": 9919,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github",
"html_url": "https://github.com/github",
"followers_url": "https://api.github.com/users/github/followers",
"following_url": "https://api.github.com/users/github/following{/other_user}",
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
"organizations_url": "https://api.github.com/users/github/orgs",
"repos_url": "https://api.github.com/users/github/repos",
"events_url": "https://api.github.com/users/github/events{/privacy}",
"received_events_url": "https://api.github.com/users/github/received_events",
"type": "Organization",
"user_view_type": "public",
"site_admin": false
},
"name": "GitHub Actions",
"description": "Automate your workflow from idea to production",
"external_url": "https://help.github.com/en/actions",
"html_url": "https://github.com/apps/github-actions",
"created_at": "2018-07-30T09:30:17Z",
"updated_at": "2025-03-07T16:35:00Z",
"permissions": {
"actions": "write",
"administration": "read",
"attestations": "write",
"checks": "write",
"contents": "write",
"deployments": "write",
"discussions": "write",
"issues": "write",
"merge_queues": "write",
"metadata": "read",
"models": "read",
"packages": "write",
"pages": "write",
"pull_requests": "write",
"repository_hooks": "write",
"repository_projects": "write",
"security_events": "write",
"statuses": "write",
"vulnerability_alerts": "read"
},
"events": [
"branch_protection_rule",
"check_run",
"check_suite",
"create",
"delete",
"deployment",
"deployment_status",
"discussion",
"discussion_comment",
"fork",
"gollum",
"issues",
"issue_comment",
"label",
"merge_group",
"milestone",
"page_build",
"project",
"project_card",
"project_column",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"push",
"registry_package",
"release",
"repository",
"repository_dispatch",
"status",
"watch",
"workflow_dispatch",
"workflow_run"
]
},
"pull_requests": [
{
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728",
"id": 2817549354,
"number": 14728,
"head": {
"ref": "chore/common/unicode-17",
"sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
},
"base": {
"ref": "master",
"sha": "898cd340a33f29b766b08da66a519be7e3d79fe6",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
}
}
]
},
{
"id": 50107108425,
"name": "Summarize build status checks",
"node_id": "CR_kwDOAY2xT88AAAALqp3MSQ",
"head_sha": "390a5bc6dba73cd32bc8c7290ac73321f6504deb",
"external_id": "7e5f7f3d-df60-5a09-9991-ce263cfb45c6",
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107108425",
"html_url": "https://github.com/keymanapp/keyman/actions/runs/17634132698/job/50107108425",
"details_url": "https://github.com/keymanapp/keyman/actions/runs/17634132698/job/50107108425",
"status": "completed",
"conclusion": "success",
"started_at": "2025-09-11T04:35:52Z",
"completed_at": "2025-09-11T04:35:55Z",
"output": {
"title": null,
"summary": null,
"text": null,
"annotations_count": 0,
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/50107108425/annotations"
},
"check_suite": {
"id": 45240794914
},
"app": {
"id": 15368,
"client_id": "Iv1.05c79e9ad1f6bdfa",
"slug": "github-actions",
"node_id": "MDM6QXBwMTUzNjg=",
"owner": {
"login": "github",
"id": 9919,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github",
"html_url": "https://github.com/github",
"followers_url": "https://api.github.com/users/github/followers",
"following_url": "https://api.github.com/users/github/following{/other_user}",
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
"organizations_url": "https://api.github.com/users/github/orgs",
"repos_url": "https://api.github.com/users/github/repos",
"events_url": "https://api.github.com/users/github/events{/privacy}",
"received_events_url": "https://api.github.com/users/github/received_events",
"type": "Organization",
"user_view_type": "public",
"site_admin": false
},
"name": "GitHub Actions",
"description": "Automate your workflow from idea to production",
"external_url": "https://help.github.com/en/actions",
"html_url": "https://github.com/apps/github-actions",
"created_at": "2018-07-30T09:30:17Z",
"updated_at": "2025-03-07T16:35:00Z",
"permissions": {
"actions": "write",
"administration": "read",
"attestations": "write",
"checks": "write",
"contents": "write",
"deployments": "write",
"discussions": "write",
"issues": "write",
"merge_queues": "write",
"metadata": "read",
"models": "read",
"packages": "write",
"pages": "write",
"pull_requests": "write",
"repository_hooks": "write",
"repository_projects": "write",
"security_events": "write",
"statuses": "write",
"vulnerability_alerts": "read"
},
"events": [
"branch_protection_rule",
"check_run",
"check_suite",
"create",
"delete",
"deployment",
"deployment_status",
"discussion",
"discussion_comment",
"fork",
"gollum",
"issues",
"issue_comment",
"label",
"merge_group",
"milestone",
"page_build",
"project",
"project_card",
"project_column",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"push",
"registry_package",
"release",
"repository",
"repository_dispatch",
"status",
"watch",
"workflow_dispatch",
"workflow_run"
]
},
"pull_requests": [
{
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14728",
"id": 2817549354,
"number": 14728,
"head": {
"ref": "chore/common/unicode-17",
"sha": "ebeacc83d0b5cfd7ab523874631e5740721f1f2f",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
},
"base": {
"ref": "master",
"sha": "898cd340a33f29b766b08da66a519be7e3d79fe6",
"repo": {
"id": 26063183,
"url": "https://api.github.com/repos/keymanapp/keyman",
"name": "keyman"
}
}
}
]
}
]

View file

@ -27,43 +27,61 @@
// check/web/file-size
//
function addLog(message) {
console.log(message);
return message + String.fromCharCode(13);
}
function addStatus(o, type, context, state) {
if(!o[context]) {
o[context] = {type, state};
return addLog(`Found ${context}: ${state}`);
} else {
return addLog(`Skipping repeated ${context}: ${state}`);
}
}
function reduceStatuses(statuses) {
let summary = '';
const filtered_statuses = statuses.reduce((o, status) => {
if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) {
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') {
if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};;
summary += addStatus(o, 'user-test', status.context, status.state);
} else if(status.context == 'API Verification') {
if(!o[status.context]) o[status.context] = {type: 'check', state: status.state};
summary += addStatus(o, 'check', status.context, status.state);
} else if(status.context == 'Ubuntu Packaging') {
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.context == 'check/web/file-size') {
// Ignore check/web/file-size -- we won't block automerge for this at this point
summary += addLog(`Skipping ${status.context}`);
} else {
// We fail with an 'unknown status' response if we get a new status check
// so we can be sure we are not skipping known status checks
summary += addLog(`Found unknown ${status.context}: ${status.state}`);
o[status.context] = {type: 'unknown', state: status.state};
}
return o;
}, {});
return filtered_statuses;
return { filtered_statuses, summary };
}
//
// Given the collection of status checks we care about, return
// an aggregate status -- error, failed, pending, or success,
// an aggregate status -- error, failure, pending, or success,
// and a summary description
//
function calculateFinalStatus(filtered_statuses) {
function calculateFinalStatus(filtered_statuses, summary) {
const counts = {};
let hasBuilds = false;
for(const context of Object.keys(filtered_statuses)) {
const { state, type } = filtered_statuses[context];
if(type == 'unknown') {
// We special-case for unknown status checks, and never permit them
console.error(`An unknown context ${context} was found, cannot calculate build status.`);
return [
'error', `An unknown context ${context} was found, cannot calculate build status.`
'error', `An unknown context ${context} was found, cannot calculate build status.`, summary
];
}
if(type == 'build') {
@ -74,12 +92,13 @@
// If we do not have any statuses yet, we wait
if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) {
return ['pending', 'Builds have not yet been triggered ⌛'];
console.log(`Builds not yet triggered`);
return ['pending', 'Builds have not yet been triggered ⌛', summary];
}
const state =
counts.error ? 'error' :
counts.failed ? 'failed' :
counts.failure ? 'failure' :
counts.pending ? 'pending' :
'success';
@ -90,14 +109,16 @@
description += `${count} check${count == 1 ? '' : 's'} ${state}`;
}
appendDescription(counts.error, 'in an error state ❌');
appendDescription(counts.failed, 'failed ❌');
appendDescription(counts.failure, 'failed ❌');
appendDescription(counts.pending, 'pending ⌛');
appendDescription(counts.success, 'completed successfully ✅');
return [ state, description ];
console.log(`Finished filtering and reviewing status checks: ${state}: ${description}`);
return [ state, description, summary ];
}
async function getCommitStatuses(github, owner, repo, sha) {
console.log(`Getting commit statuses for ${sha}`);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', {
owner,
repo,
@ -110,6 +131,7 @@
}
async function getCommitCheckRuns(github, owner, repo, sha) {
console.log(`Getting commit check runs for ${sha}`);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', {
owner,
repo,
@ -123,13 +145,11 @@
function calculateCheckResult(statuses) {
if(!Array.isArray(statuses)) {
return ['error', 'Failed to retrieve status checks from GitHub ❌'];
return ['error', 'Failed to retrieve status checks from GitHub ❌', 'statuses is not an array'];
}
const filtered_statuses = reduceStatuses(statuses);
const result = calculateFinalStatus(filtered_statuses);
return result;
const { filtered_statuses, summary } = reduceStatuses(statuses);
return calculateFinalStatus(filtered_statuses, summary);
}
async function test(github, owner, repo, sha) {
@ -149,7 +169,7 @@
return check.data.id;
}
async function updateCheck(github, owner, repo, checkRunId, status, description) {
async function updateCheck(github, owner, repo, checkRunId, status, description, summary) {
const checkStatus = status == 'pending' ? 'in_progress' : 'completed';
const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure');
@ -161,7 +181,7 @@
conclusion,
output: {
title: description,
summary: ''
summary
}
});
}
@ -179,7 +199,7 @@
// const checkRunId = await createCheck(github, owner, repo, sha);
// const res = await test(github, owner, repo, sha);
// await updateCheck(github, owner, repo, checkRunId, res[0], res[1]);
// await updateCheck(github, owner, repo, checkRunId, res[0], res[1], res[2]);
// CLONE-COMMENTED:END

View file

@ -1,6 +1,7 @@
import * as fs from 'node:fs';
import 'mocha';
import { assert } from 'chai';
import sinon from 'sinon';
import { unitTestEndpoints } from './pr-build-status.mjs';
@ -9,9 +10,10 @@ import { getOctokit } from '@actions/github';
import * as process from 'node:process';
const commits = {
'390a5bc6dba73cd32bc8c7290ac73321f6504deb': [ 'failure', '3 checks failed ❌; 21 checks completed successfully ✅'],
'825f7d2df0069fa56a8f2a534b42a49ee1f21ca1': [ 'success', '24 checks completed successfully ✅' ],
'870d37f56b489ef5067b4352a06505369d9038e0': [ 'error', '6 checks in an error state ❌; 17 checks completed successfully ✅' ],
'11a558e926370253db37026c95b7ff5a6aa1e8fc': [ 'success', '22 checks completed successfully ✅' ],
'870d37f56b489ef5067b4352a06505369d9038e0': [ 'error', '6 checks in an error state ❌; 1 check failed ❌; 17 checks completed successfully ✅' ],
'11a558e926370253db37026c95b7ff5a6aa1e8fc': [ 'failure', '2 checks failed ❌; 22 checks completed successfully ✅' ],
'8fd5ccc9026b85d6a780d8bcf862380e7a89aefb': [ 'pending', 'Builds have not yet been triggered ⌛' ],
'153683cfb007c6066c9dcf71d25afa4c66efa17f': [ 'pending', 'Builds have not yet been triggered ⌛' ],
};
@ -32,13 +34,23 @@ if(debug) {
}
describe('pr-build-status', function () {
beforeEach(() => {
// just silencing the logging from pr-build-status bot
// in order to make the test results clearer
sinon.stub(console, 'log');
});
afterEach(() => {
console.log.restore();
});
for(const sha of Object.keys(commits)) {
it(`should return '${commits[sha][1]}' for sha ${sha}`, function () {
const json = JSON.parse(fs.readFileSync(`fixtures/${sha}-statuses.json`,'utf-8'));
const status = unitTestEndpoints.calculateCheckResult(json);
assert.isNotNull(status);
assert.isArray(status);
assert.deepStrictEqual(status, commits[sha]);
assert.deepStrictEqual(status.splice(0, 2), commits[sha]);
});
}
});

View file

@ -35,7 +35,7 @@ generate_table() {
KEYMAN_VERSION | KEYMAN_VERSION_MAJOR | KEYMAN_VERSION_MINOR | \
KEYMAN_VERSION_PATCH | KEYMAN_VERSION_RELEASE | KEYMAN_VERSION_WIN | \
KEYMAN_VERSION_TAG | KEYMAN_VERSION_WITH_TAG | \
KEYMAN_VERSION_ENVIRONMENT | KEYMAN_VERSION_GIT_TAG)
KEYMAN_VERSION_ENVIRONMENT | KEYMAN_VERSION_GIT_TAG | KEYMAN_VERSION_FOR_FILENAME)
# ignore the variables that contain the Keyman version
;;
*)

View file

@ -216,7 +216,25 @@ _do_test_test_bot_check_pr_body() {
#----------------------------------------------------------------------------------------------------
test_is_skip_keyman_api_check() {
builder_echo start test_is_skip_keyman_api_check 'START TEST: test_is_skip_keyman_api_check'
result=$(is_skip_keyman_api_check 1 '{ "body": "Keyman-Api-Check: skip" }')
assert-equal "${result}" "true" "PR #1: 'Keyman-Api-Check: skip' should return true"
result=$(is_skip_keyman_api_check 2 '{ "body": "PR message" }')
assert-equal "${result}" "false" "PR #2: omitted 'Keyman-Api-Check' should return false"
result=$(is_skip_keyman_api_check 3 '{ "body": "Keyman-Api-Check: invalid" }')
assert-equal "${result}" "false" "PR #3: invalid data for 'Keyman-Api-Check' should return false"
result=$(is_skip_keyman_api_check 4 '{ "body": "Some message\nKeyman-Api-Check: skip" }')
assert-equal "${result}" "true" "PR #4: 'Keyman-Api-Check: skip' at start of line should return true"
result=$(is_skip_keyman_api_check 5 '{ "body": "Here Keyman-Api-Check: skip\nis in the middle of the line" }')
assert-equal "${result}" "false" "PR #5: 'Keyman-Api-Check: skip' in the middle of the line should return false"
builder_echo end test_is_skip_keyman_api_check success 'SUCCESS: test_is_skip_keyman_api_check'
}
#----------------------------------------------------------------------------------------------------
test_build_bot_verify_platforms
test_build_bot_update_commands
test_build_bot_check_messages
test_test_bot_check_pr_body
test_is_skip_keyman_api_check

View file

@ -107,3 +107,38 @@ Running tests locally with the `run.sh` looks like:
resources/docker-images/run.sh :web --registry 'myregistry:5678' --username 'user' --password 'password' -- web/build.sh test
```
## Running GitHub Actions locally
There exist [GitHub actions for building the Docker images](../../.github/workflows/build-test-publish-docker.yml).
For testing and developing the workflow can be started locally.
As a prerequisite [act](https://nektosact.com/) must be installed.
This is an exmaple for running the workflow with act:
```bash
#!/bin/bash
# runs GitHub workflow locally for building, testing and publishing Docker images
# requires act (https://nektosact.com/)
# requires a GitHub token with repo permissions (e.g. GITHUB_TOKEN from a personal access token)
# requires Docker with buildx support
tempfile=/tmp/event.json
cat > ${tempfile} <<EOF
{
"client_payload": {
},
"action": "build-test-publish-docker",
"act": true
}
EOF
act repository_dispatch \
-e /tmp/event.json \
--actor='<username>' \
-s GITHUB_TOKEN='<GitHub token>' \
--container-options "--user runner:$(getent group docker | cut -d: -f3)" \
-W '.github/workflows/build-test-publish-docker.yml'
rm -f ${tempfile}
```

View file

@ -69,7 +69,7 @@ publish_action() {
local platform=$1
local version_list=("${build_version}")
builder_echo debug "publishing image for ${platform}"
builder_echo debug "publishing image for ${platform}."
if is_default_values; then
version_list+=("default")

View file

@ -96,16 +96,11 @@ setup_container_registry() {
local password=${REGISTRY_PASSWORD:-}
registry_slash=''
builder_echo debug "Setting up container registry."
builder_echo debug "Setting up container registry '${registry}'."
if [[ "${registry:-}" == "docker.io" ]]; then
builder_echo debug "Container registry is 'docker.io'."
registry=
else
registry_slash="${registry}/"
registry_parameters="--registry ${registry}"
build_args+=("--build-arg=REGISTRY=${registry_slash}")
fi
registry_slash="${registry}/"
registry_parameters="--registry ${registry}"
build_args+=("--build-arg=REGISTRY=${registry_slash}")
if [[ -n ${username:-} ]] ; then
if [[ -z ${password:-} ]] ; then

View file

@ -114,18 +114,25 @@ function prepend_scope() {
fi
fi
commentchar="#"
if [[ -n "$(git config core.commentString)" ]]; then
commentchar="$(git config core.commentString)"
elif [[ -n "$(git config core.commentChar)" ]]; then
commentchar="$(git config core.commentChar)"
fi
postfix=$(
cat <<EOF
\n# Keyman Conventional Commit suggestions:
#
# - Link to a Sentry issue with git trailer:
# Fixes: _MODULE_-_ID_
# - Give credit to co-authors:
# Co-authored-by: _Name_ <_email_>
# - Use imperative, present tense ('attach' not 'attaches', 'attached' etc)
# - Don't include a period at the end of the title
# - Always include a blank line before trailers
# - More: https://github.com/keymanapp/keyman/wiki/Pull-Request-and-Commit-workflow-notes
\n${commentchar} Keyman Conventional Commit suggestions:
${commentchar}
${commentchar} - Link to a Sentry issue with git trailer:
${commentchar} Fixes: _MODULE_-_ID_
${commentchar} - Give credit to co-authors:
${commentchar} Co-authored-by: _Name_ <_email_>
${commentchar} - Use imperative, present tense ('attach' not 'attaches', 'attached' etc)
${commentchar} - Don't include a period at the end of the title
${commentchar} - Always include a blank line before trailers
${commentchar} - More: https://github.com/keymanapp/keyman/wiki/Pull-Request-and-Commit-workflow-notes
EOF
)
@ -170,4 +177,4 @@ case $COMMIT_SOURCE in
# Known possibilities: message (-m), template (-t)
exit 0
;;
esac
esac

View file

@ -57,13 +57,9 @@ See also [ldml-keyboards/readme.md](ldml-keyboards/readme.md).
### unicode-character-database
Data from <https://www.unicode.org/Public/15.1.0/ucd/>:
Data from <https://www.unicode.org/Public/17.0.0/ucd/>:
```bash
UNICODE_VERSION=15.1.0
wget https://www.unicode.org/Public/${UNICODE_VERSION}/ucd/Blocks.txt -O unicode-character-database/Blocks.txt
wget https://www.unicode.org/Public/${UNICODE_VERSION}/ucd/UnicodeData.txt -O unicode-character-database/UnicodeData.txt
```
See [download.sh](./unicode-character-database/download.sh)
### windows-lcid-to-bcp-47

View file

@ -1,6 +1,6 @@
# Blocks-16.0.0.txt
# Date: 2024-02-02
# © 2024 Unicode®, Inc.
# Blocks-17.0.0.txt
# Date: 2025-08-01
# © 2025 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use and license, see https://www.unicode.org/terms_of_use.html
#
@ -228,6 +228,7 @@ FFF0..FFFF; Specials
108E0..108FF; Hatran
10900..1091F; Phoenician
10920..1093F; Lydian
10940..1095F; Sidetic
10980..1099F; Meroitic Hieroglyphs
109A0..109FF; Meroitic Cursive
10A00..10A5F; Kharoshthi
@ -279,11 +280,13 @@ FFF0..FFFF; Specials
11AB0..11ABF; Unified Canadian Aboriginal Syllabics Extended-A
11AC0..11AFF; Pau Cin Hau
11B00..11B5F; Devanagari Extended-A
11B60..11B7F; Sharada Supplement
11BC0..11BFF; Sunuwar
11C00..11C6F; Bhaiksuki
11C70..11CBF; Marchen
11D00..11D5F; Masaram Gondi
11D60..11DAF; Gunjala Gondi
11DB0..11DEF; Tolong Siki
11EE0..11EFF; Makasar
11F00..11F5F; Kawi
11FB0..11FBF; Lisu Supplement
@ -304,12 +307,14 @@ FFF0..FFFF; Specials
16B00..16B8F; Pahawh Hmong
16D40..16D7F; Kirat Rai
16E40..16E9F; Medefaidrin
16EA0..16EDF; Beria Erfe
16F00..16F9F; Miao
16FE0..16FFF; Ideographic Symbols and Punctuation
17000..187FF; Tangut
18800..18AFF; Tangut Components
18B00..18CFF; Khitan Small Script
18D00..18D7F; Tangut Supplement
18D80..18DFF; Tangut Components Supplement
1AFF0..1AFFF; Kana Extended-B
1B000..1B0FF; Kana Supplement
1B100..1B12F; Kana Extended-A
@ -318,6 +323,7 @@ FFF0..FFFF; Specials
1BC00..1BC9F; Duployan
1BCA0..1BCAF; Shorthand Format Controls
1CC00..1CEBF; Symbols for Legacy Computing Supplement
1CEC0..1CEFF; Miscellaneous Symbols Supplement
1CF00..1CFCF; Znamenny Musical Notation
1D000..1D0FF; Byzantine Musical Symbols
1D100..1D1FF; Musical Symbols
@ -336,6 +342,7 @@ FFF0..FFFF; Specials
1E2C0..1E2FF; Wancho
1E4D0..1E4FF; Nag Mundari
1E5D0..1E5FF; Ol Onal
1E6C0..1E6FF; Tai Yo
1E7E0..1E7FF; Ethiopic Extended-B
1E800..1E8DF; Mende Kikakui
1E900..1E95F; Adlam
@ -367,6 +374,7 @@ FFF0..FFFF; Specials
2F800..2FA1F; CJK Compatibility Ideographs Supplement
30000..3134F; CJK Unified Ideographs Extension G
31350..323AF; CJK Unified Ideographs Extension H
323B0..3347F; CJK Unified Ideographs Extension J
E0000..E007F; Tags
E0100..E01EF; Variation Selectors Supplement
F0000..FFFFF; Supplementary Private Use Area-A

View file

@ -659,7 +659,7 @@
0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7
0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;;
0294;LATIN LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;;
0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Lo;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;;
0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;;
0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;;
@ -2121,6 +2121,7 @@
088C;ARABIC LETTER TAH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
088D;ARABIC LETTER KEHEH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;;
088E;ARABIC VERTICAL TAIL;Lo;0;AL;;;;;N;;;;;
088F;ARABIC LETTER NOON WITH RING ABOVE;Lo;0;AL;;;;;N;;;;;
0890;ARABIC POUND MARK ABOVE;Cf;0;AN;;;;;N;;;;;
0891;ARABIC PIASTRE MARK ABOVE;Cf;0;AN;;;;;N;;;;;
0897;ARABIC PEPET;Mn;230;NSM;;;;;N;;;;;
@ -2862,6 +2863,7 @@
0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;;
0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;;
0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;;
0C5C;TELUGU ARCHAIC SHRII;Lo;0;L;;;;;N;;;;;
0C5D;TELUGU LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;;
0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
@ -2958,6 +2960,7 @@
0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;;
0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;;
0CDC;KANNADA ARCHAIC SHRII;Lo;0;L;;;;;N;;;;;
0CDD;KANNADA LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;;
0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;;
0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
@ -6137,6 +6140,33 @@
1ACC;COMBINING LATIN SMALL LETTER INSULAR G;Mn;230;NSM;;;;;N;;;;;
1ACD;COMBINING LATIN SMALL LETTER INSULAR R;Mn;230;NSM;;;;;N;;;;;
1ACE;COMBINING LATIN SMALL LETTER INSULAR T;Mn;230;NSM;;;;;N;;;;;
1ACF;COMBINING DOUBLE CARON;Mn;230;NSM;;;;;N;;;;;
1AD0;COMBINING VERTICAL-LINE-ACUTE;Mn;230;NSM;;;;;N;;;;;
1AD1;COMBINING GRAVE-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;;
1AD2;COMBINING VERTICAL-LINE-GRAVE;Mn;230;NSM;;;;;N;;;;;
1AD3;COMBINING ACUTE-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;;
1AD4;COMBINING VERTICAL-LINE-MACRON;Mn;230;NSM;;;;;N;;;;;
1AD5;COMBINING MACRON-VERTICAL-LINE;Mn;230;NSM;;;;;N;;;;;
1AD6;COMBINING VERTICAL-LINE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;;
1AD7;COMBINING VERTICAL-LINE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;;
1AD8;COMBINING MACRON-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;;
1AD9;COMBINING SHARP SIGN;Mn;230;NSM;;;;;N;;;;;
1ADA;COMBINING FLAT SIGN;Mn;230;NSM;;;;;N;;;;;
1ADB;COMBINING DOWN TACK ABOVE;Mn;230;NSM;;;;;N;;;;;
1ADC;COMBINING DIAERESIS WITH RAISED LEFT DOT;Mn;230;NSM;;;;;N;;;;;
1ADD;COMBINING DOT-AND-RING BELOW;Mn;220;NSM;;;;;N;;;;;
1AE0;COMBINING LEFT TACK ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE1;COMBINING RIGHT TACK ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE2;COMBINING MINUS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE3;COMBINING INVERTED BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE4;COMBINING SQUARE ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE5;COMBINING SEAGULL ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE6;COMBINING DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;;;;;
1AE7;COMBINING DOUBLE ARCH ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE8;COMBINING EQUALS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;;
1AE9;COMBINING LEFT ANGLE CENTRED ABOVE;Mn;230;NSM;;;;;N;;;;;
1AEA;COMBINING UPWARDS ARROW ABOVE;Mn;230;NSM;;;;;N;;;;;
1AEB;COMBINING DOUBLE RIGHTWARDS ARROW ABOVE;Mn;234;NSM;;;;;N;;;;;
1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;;
1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;;
1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;;
@ -7545,6 +7575,7 @@
20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;;
20BF;BITCOIN SIGN;Sc;0;ET;;;;;N;;;;;
20C0;SOM SIGN;Sc;0;ET;;;;;N;;;;;
20C1;SAUDI RIYAL SIGN;Sc;0;ET;;;;;N;;;;;
20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;;
@ -10239,6 +10270,7 @@
2B93;NEWLINE RIGHT;So;0;ON;;;;;N;;;;;
2B94;FOUR CORNER ARROWS CIRCLING ANTICLOCKWISE;So;0;ON;;;;;N;;;;;
2B95;RIGHTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
2B96;EQUALS SIGN WITH INFINITY ABOVE;So;0;ON;;;;;N;;;;;
2B97;SYMBOL FOR TYPE A ELECTRONICS;So;0;ON;;;;;N;;;;;
2B98;THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
2B99;THREE-D RIGHT-LIGHTED UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
@ -14274,10 +14306,14 @@ A7CA;LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY;Ll;0;L;;;;;N;;;A7C9;;A7C9
A7CB;LATIN CAPITAL LETTER RAMS HORN;Lu;0;L;;;;;N;;;;0264;
A7CC;LATIN CAPITAL LETTER S WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A7CD;
A7CD;LATIN SMALL LETTER S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A7CC;;A7CC
A7CE;LATIN CAPITAL LETTER PHARYNGEAL VOICED FRICATIVE;Lu;0;L;;;;;N;;;;A7CF;
A7CF;LATIN SMALL LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;;;A7CE;;A7CE
A7D0;LATIN CAPITAL LETTER CLOSED INSULAR G;Lu;0;L;;;;;N;;;;A7D1;
A7D1;LATIN SMALL LETTER CLOSED INSULAR G;Ll;0;L;;;;;N;;;A7D0;;A7D0
A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;;;
A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;;;
A7D2;LATIN CAPITAL LETTER DOUBLE THORN;Lu;0;L;;;;;N;;;;A7D3;
A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;A7D2;;A7D2
A7D4;LATIN CAPITAL LETTER DOUBLE WYNN;Lu;0;L;;;;;N;;;;A7D5;
A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;A7D4;;A7D4
A7D6;LATIN CAPITAL LETTER MIDDLE SCOTS S;Lu;0;L;;;;;N;;;;A7D7;
A7D7;LATIN SMALL LETTER MIDDLE SCOTS S;Ll;0;L;;;;;N;;;A7D6;;A7D6
A7D8;LATIN CAPITAL LETTER SIGMOID S;Lu;0;L;;;;;N;;;;A7D9;
@ -14285,6 +14321,7 @@ A7D9;LATIN SMALL LETTER SIGMOID S;Ll;0;L;;;;;N;;;A7D8;;A7D8
A7DA;LATIN CAPITAL LETTER LAMBDA;Lu;0;L;;;;;N;;;;A7DB;
A7DB;LATIN SMALL LETTER LAMBDA;Ll;0;L;;;;;N;;;A7DA;;A7DA
A7DC;LATIN CAPITAL LETTER LAMBDA WITH STROKE;Lu;0;L;;;;;N;;;;019B;
A7F1;MODIFIER LETTER CAPITAL S;Lm;0;L;<super> 0053;;;;N;;;;;
A7F2;MODIFIER LETTER CAPITAL C;Lm;0;L;<super> 0043;;;;N;;;;;
A7F3;MODIFIER LETTER CAPITAL F;Lm;0;L;<super> 0046;;;;N;;;;;
A7F4;MODIFIER LETTER CAPITAL Q;Lm;0;L;<super> 0051;;;;N;;;;;
@ -15925,6 +15962,22 @@ FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;;
FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;;
FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;;
FBC2;ARABIC SYMBOL WASLA ABOVE;Sk;0;AL;;;;;N;;;;;
FBC3;ARABIC LIGATURE JALLA WA-ALAA;So;0;ON;;;;;N;;;;;
FBC4;ARABIC LIGATURE DAAMAT BARAKAATUHUM;So;0;ON;;;;;N;;;;;
FBC5;ARABIC LIGATURE RAHMATU ALLAAHI TAAALAA ALAYH;So;0;ON;;;;;N;;;;;
FBC6;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHIM;So;0;ON;;;;;N;;;;;
FBC7;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHIMAA;So;0;ON;;;;;N;;;;;
FBC8;ARABIC LIGATURE RAHIMAHUM ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;;
FBC9;ARABIC LIGATURE RAHIMAHUMAA ALLAAH;So;0;ON;;;;;N;;;;;
FBCA;ARABIC LIGATURE RAHIMAHUMAA ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;;
FBCB;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHUM;So;0;ON;;;;;N;;;;;
FBCC;ARABIC LIGATURE HAFIZAHU ALLAAH;So;0;ON;;;;;N;;;;;
FBCD;ARABIC LIGATURE HAFIZAHU ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;;
FBCE;ARABIC LIGATURE HAFIZAHUM ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;;
FBCF;ARABIC LIGATURE HAFIZAHUMAA ALLAAHU TAAALAA;So;0;ON;;;;;N;;;;;
FBD0;ARABIC LIGATURE SALLALLAAHU TAAALAA ALAYHI WA-SALLAM;So;0;ON;;;;;N;;;;;
FBD1;ARABIC LIGATURE AJJAL ALLAAHU FARAJAHU ASH-SHAREEF;So;0;ON;;;;;N;;;;;
FBD2;ARABIC LIGATURE ALAYHI AR-RAHMAH;So;0;ON;;;;;N;;;;;
FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;;
FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;;
FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;;
@ -16370,6 +16423,8 @@ FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645
FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;;
FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;;
FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;;
FD90;ARABIC LIGATURE RAHMATU ALLAAHI ALAYH;So;0;ON;;;;;N;;;;;
FD91;ARABIC LIGATURE RAHMATU ALLAAHI ALAYHAA;So;0;ON;;;;;N;;;;;
FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;;
FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;;
FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;;
@ -16424,6 +16479,13 @@ FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639
FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;;
FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;;
FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;;
FDC8;ARABIC LIGATURE RAHIMAHU ALLAAH TAAALAA;So;0;ON;;;;;N;;;;;
FDC9;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANH;So;0;ON;;;;;N;;;;;
FDCA;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHAA;So;0;ON;;;;;N;;;;;
FDCB;ARABIC LIGATURE RADI ALLAAHU TAAALAA ANHUMAA;So;0;ON;;;;;N;;;;;
FDCC;ARABIC LIGATURE SALLALLAHU ALAYHI WA-ALAA AALIHEE WA-SALLAM;So;0;ON;;;;;N;;;;;
FDCD;ARABIC LIGATURE AJJAL ALLAAHU TAAALAA FARAJAHU ASH-SHAREEF;So;0;ON;;;;;N;;;;;
FDCE;ARABIC LIGATURE KARRAMA ALLAAHU WAJHAH;So;0;ON;;;;;N;;;;;
FDCF;ARABIC LIGATURE SALAAMUHU ALAYNAA;So;0;ON;;;;;N;;;;;
FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;;
FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;;
@ -18708,6 +18770,32 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;;
10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;;
1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;;
10940;SIDETIC LETTER N01;Lo;0;R;;;;;N;;;;;
10941;SIDETIC LETTER N02;Lo;0;R;;;;;N;;;;;
10942;SIDETIC LETTER N03;Lo;0;R;;;;;N;;;;;
10943;SIDETIC LETTER N04;Lo;0;R;;;;;N;;;;;
10944;SIDETIC LETTER N05;Lo;0;R;;;;;N;;;;;
10945;SIDETIC LETTER N06;Lo;0;R;;;;;N;;;;;
10946;SIDETIC LETTER N07;Lo;0;R;;;;;N;;;;;
10947;SIDETIC LETTER N08;Lo;0;R;;;;;N;;;;;
10948;SIDETIC LETTER N09;Lo;0;R;;;;;N;;;;;
10949;SIDETIC LETTER N10;Lo;0;R;;;;;N;;;;;
1094A;SIDETIC LETTER N11;Lo;0;R;;;;;N;;;;;
1094B;SIDETIC LETTER N12;Lo;0;R;;;;;N;;;;;
1094C;SIDETIC LETTER N13;Lo;0;R;;;;;N;;;;;
1094D;SIDETIC LETTER N14;Lo;0;R;;;;;N;;;;;
1094E;SIDETIC LETTER N15;Lo;0;R;;;;;N;;;;;
1094F;SIDETIC LETTER N16;Lo;0;R;;;;;N;;;;;
10950;SIDETIC LETTER N17;Lo;0;R;;;;;N;;;;;
10951;SIDETIC LETTER N18;Lo;0;R;;;;;N;;;;;
10952;SIDETIC LETTER N19;Lo;0;R;;;;;N;;;;;
10953;SIDETIC LETTER N20;Lo;0;R;;;;;N;;;;;
10954;SIDETIC LETTER N21;Lo;0;R;;;;;N;;;;;
10955;SIDETIC LETTER N22;Lo;0;R;;;;;N;;;;;
10956;SIDETIC LETTER N23;Lo;0;R;;;;;N;;;;;
10957;SIDETIC LETTER N24;Lo;0;R;;;;;N;;;;;
10958;SIDETIC LETTER N25;Lo;0;R;;;;;N;;;;;
10959;SIDETIC LETTER N26;Lo;0;R;;;;;N;;;;;
10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;;
10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;;
10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;;
@ -19541,6 +19629,20 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10EC2;ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;;
10EC3;ARABIC LETTER TAH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;;
10EC4;ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;;
10EC5;ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW;Lm;0;AL;;;;;N;;;;;
10EC6;ARABIC LETTER THIN NOON;Lo;0;AL;;;;;N;;;;;
10EC7;ARABIC LETTER YEH WITH FOUR DOTS BELOW;Lo;0;AL;;;;;N;;;;;
10ED0;ARABIC BIBLICAL END OF VERSE;Po;0;ON;;;;;N;;;;;
10ED1;ARABIC LIGATURE ALAYHAA AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;;
10ED2;ARABIC LIGATURE ALAYHIM AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;;
10ED3;ARABIC LIGATURE ALAYHIMAA AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;;
10ED4;ARABIC LIGATURE QADDASA ALLAAHU SIRRAH;So;0;ON;;;;;N;;;;;
10ED5;ARABIC LIGATURE QUDDISA SIRRUHUM;So;0;ON;;;;;N;;;;;
10ED6;ARABIC LIGATURE QUDDISA SIRRUHUMAA;So;0;ON;;;;;N;;;;;
10ED7;ARABIC LIGATURE QUDDISAT ASRAARUHUM;So;0;ON;;;;;N;;;;;
10ED8;ARABIC LIGATURE NAWWARA ALLAAHU MARQADAH;So;0;ON;;;;;N;;;;;
10EFA;ARABIC DOUBLE VERTICAL BAR BELOW;Mn;220;NSM;;;;;N;;;;;
10EFB;ARABIC SMALL LOW NOON;Mn;220;NSM;;;;;N;;;;;
10EFC;ARABIC COMBINING ALEF OVERLAY;Mn;0;NSM;;;;;N;;;;;
10EFD;ARABIC SMALL LOW WORD SAKTA;Mn;220;NSM;;;;;N;;;;;
10EFE;ARABIC SMALL LOW WORD QASR;Mn;220;NSM;;;;;N;;;;;
@ -21521,6 +21623,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11B07;DEVANAGARI SIGN WESTERN NINE-LIKE BHALE;Po;0;L;;;;;N;;;;;
11B08;DEVANAGARI SIGN REVERSED NINE-LIKE BHALE;Po;0;L;;;;;N;;;;;
11B09;DEVANAGARI SIGN MINDU;Po;0;L;;;;;N;;;;;
11B60;SHARADA VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;;
11B61;SHARADA VOWEL SIGN OOE;Mc;0;L;;;;;N;;;;;
11B62;SHARADA VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
11B63;SHARADA VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;;
11B64;SHARADA VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;;
11B65;SHARADA VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;;
11B66;SHARADA VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
11B67;SHARADA VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
11BC0;SUNUWAR LETTER DEVI;Lo;0;L;;;;;N;;;;;
11BC1;SUNUWAR LETTER TASLA;Lo;0;L;;;;;N;;;;;
11BC2;SUNUWAR LETTER EKO;Lo;0;L;;;;;N;;;;;
@ -21868,6 +21978,60 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11DA7;GUNJALA GONDI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
11DA8;GUNJALA GONDI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
11DA9;GUNJALA GONDI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
11DB0;TOLONG SIKI LETTER I;Lo;0;L;;;;;N;;;;;
11DB1;TOLONG SIKI LETTER E;Lo;0;L;;;;;N;;;;;
11DB2;TOLONG SIKI LETTER U;Lo;0;L;;;;;N;;;;;
11DB3;TOLONG SIKI LETTER O;Lo;0;L;;;;;N;;;;;
11DB4;TOLONG SIKI LETTER A;Lo;0;L;;;;;N;;;;;
11DB5;TOLONG SIKI LETTER AA;Lo;0;L;;;;;N;;;;;
11DB6;TOLONG SIKI LETTER P;Lo;0;L;;;;;N;;;;;
11DB7;TOLONG SIKI LETTER PH;Lo;0;L;;;;;N;;;;;
11DB8;TOLONG SIKI LETTER B;Lo;0;L;;;;;N;;;;;
11DB9;TOLONG SIKI LETTER BH;Lo;0;L;;;;;N;;;;;
11DBA;TOLONG SIKI LETTER M;Lo;0;L;;;;;N;;;;;
11DBB;TOLONG SIKI LETTER T;Lo;0;L;;;;;N;;;;;
11DBC;TOLONG SIKI LETTER TH;Lo;0;L;;;;;N;;;;;
11DBD;TOLONG SIKI LETTER D;Lo;0;L;;;;;N;;;;;
11DBE;TOLONG SIKI LETTER DH;Lo;0;L;;;;;N;;;;;
11DBF;TOLONG SIKI LETTER N;Lo;0;L;;;;;N;;;;;
11DC0;TOLONG SIKI LETTER TT;Lo;0;L;;;;;N;;;;;
11DC1;TOLONG SIKI LETTER TTH;Lo;0;L;;;;;N;;;;;
11DC2;TOLONG SIKI LETTER DD;Lo;0;L;;;;;N;;;;;
11DC3;TOLONG SIKI LETTER DDH;Lo;0;L;;;;;N;;;;;
11DC4;TOLONG SIKI LETTER NN;Lo;0;L;;;;;N;;;;;
11DC5;TOLONG SIKI LETTER C;Lo;0;L;;;;;N;;;;;
11DC6;TOLONG SIKI LETTER CH;Lo;0;L;;;;;N;;;;;
11DC7;TOLONG SIKI LETTER J;Lo;0;L;;;;;N;;;;;
11DC8;TOLONG SIKI LETTER JH;Lo;0;L;;;;;N;;;;;
11DC9;TOLONG SIKI LETTER NY;Lo;0;L;;;;;N;;;;;
11DCA;TOLONG SIKI LETTER K;Lo;0;L;;;;;N;;;;;
11DCB;TOLONG SIKI LETTER KH;Lo;0;L;;;;;N;;;;;
11DCC;TOLONG SIKI LETTER G;Lo;0;L;;;;;N;;;;;
11DCD;TOLONG SIKI LETTER GH;Lo;0;L;;;;;N;;;;;
11DCE;TOLONG SIKI LETTER NG;Lo;0;L;;;;;N;;;;;
11DCF;TOLONG SIKI LETTER Y;Lo;0;L;;;;;N;;;;;
11DD0;TOLONG SIKI LETTER R;Lo;0;L;;;;;N;;;;;
11DD1;TOLONG SIKI LETTER L;Lo;0;L;;;;;N;;;;;
11DD2;TOLONG SIKI LETTER V;Lo;0;L;;;;;N;;;;;
11DD3;TOLONG SIKI LETTER NNY;Lo;0;L;;;;;N;;;;;
11DD4;TOLONG SIKI LETTER S;Lo;0;L;;;;;N;;;;;
11DD5;TOLONG SIKI LETTER H;Lo;0;L;;;;;N;;;;;
11DD6;TOLONG SIKI LETTER X;Lo;0;L;;;;;N;;;;;
11DD7;TOLONG SIKI LETTER RR;Lo;0;L;;;;;N;;;;;
11DD8;TOLONG SIKI LETTER RRH;Lo;0;L;;;;;N;;;;;
11DD9;TOLONG SIKI SIGN SELA;Lm;0;L;;;;;N;;;;;
11DDA;TOLONG SIKI SIGN HECAKA;Lo;0;L;;;;;N;;;;;
11DDB;TOLONG SIKI UNGGA;Lo;0;L;;;;;N;;;;;
11DE0;TOLONG SIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
11DE1;TOLONG SIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
11DE2;TOLONG SIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
11DE3;TOLONG SIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
11DE4;TOLONG SIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
11DE5;TOLONG SIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
11DE6;TOLONG SIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
11DE7;TOLONG SIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
11DE8;TOLONG SIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
11DE9;TOLONG SIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
11EE0;MAKASAR LETTER KA;Lo;0;L;;;;;N;;;;;
11EE1;MAKASAR LETTER GA;Lo;0;L;;;;;N;;;;;
11EE2;MAKASAR LETTER NGA;Lo;0;L;;;;;N;;;;;
@ -22088,8 +22252,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
12035;CUNEIFORM SIGN ARAD TIMES KUR;Lo;0;L;;;;;N;;;;;
12036;CUNEIFORM SIGN ARKAB;Lo;0;L;;;;;N;;;;;
12037;CUNEIFORM SIGN ASAL2;Lo;0;L;;;;;N;;;;;
12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;;N;;;;;
12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;;N;;;;;
12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;1;N;;;;;
12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;1;N;;;;;
1203A;CUNEIFORM SIGN ASH KABA TENU;Lo;0;L;;;;;N;;;;;
1203B;CUNEIFORM SIGN ASH OVER ASH TUG2 OVER TUG2 TUG2 OVER TUG2 PAP;Lo;0;L;;;;;N;;;;;
1203C;CUNEIFORM SIGN ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;;
@ -22153,7 +22317,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
12076;CUNEIFORM SIGN DIM2;Lo;0;L;;;;;N;;;;;
12077;CUNEIFORM SIGN DIN;Lo;0;L;;;;;N;;;;;
12078;CUNEIFORM SIGN DIN KASKAL U GUNU DISH;Lo;0;L;;;;;N;;;;;
12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;;N;;;;;
12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;1;N;;;;;
1207A;CUNEIFORM SIGN DU;Lo;0;L;;;;;N;;;;;
1207B;CUNEIFORM SIGN DU OVER DU;Lo;0;L;;;;;N;;;;;
1207C;CUNEIFORM SIGN DU GUNU;Lo;0;L;;;;;N;;;;;
@ -22582,12 +22746,12 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
12223;CUNEIFORM SIGN MA2;Lo;0;L;;;;;N;;;;;
12224;CUNEIFORM SIGN MAH;Lo;0;L;;;;;N;;;;;
12225;CUNEIFORM SIGN MAR;Lo;0;L;;;;;N;;;;;
12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;;N;;;;;
12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;1/2;N;;;;;
12227;CUNEIFORM SIGN MASH2;Lo;0;L;;;;;N;;;;;
12228;CUNEIFORM SIGN ME;Lo;0;L;;;;;N;;;;;
12229;CUNEIFORM SIGN MES;Lo;0;L;;;;;N;;;;;
1222A;CUNEIFORM SIGN MI;Lo;0;L;;;;;N;;;;;
1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;;N;;;;;
1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;2;N;;;;;
1222C;CUNEIFORM SIGN MU;Lo;0;L;;;;;N;;;;;
1222D;CUNEIFORM SIGN MU OVER MU;Lo;0;L;;;;;N;;;;;
1222E;CUNEIFORM SIGN MUG;Lo;0;L;;;;;N;;;;;
@ -22811,9 +22975,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
12308;CUNEIFORM SIGN TUM;Lo;0;L;;;;;N;;;;;
12309;CUNEIFORM SIGN TUR;Lo;0;L;;;;;N;;;;;
1230A;CUNEIFORM SIGN TUR OVER TUR ZA OVER ZA;Lo;0;L;;;;;N;;;;;
1230B;CUNEIFORM SIGN U;Lo;0;L;;;;;N;;;;;
1230B;CUNEIFORM SIGN U;Lo;0;L;;;;1;N;;;;;
1230C;CUNEIFORM SIGN U GUD;Lo;0;L;;;;;N;;;;;
1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;;N;;;;;
1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;3;N;;;;;
1230E;CUNEIFORM SIGN U OVER U PA OVER PA GAR OVER GAR;Lo;0;L;;;;;N;;;;;
1230F;CUNEIFORM SIGN U OVER U SUR OVER SUR;Lo;0;L;;;;;N;;;;;
12310;CUNEIFORM SIGN U OVER U U REVERSED OVER U REVERSED;Lo;0;L;;;;;N;;;;;
@ -22953,7 +23117,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
12396;CUNEIFORM SIGN SAG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
12397;CUNEIFORM SIGN TI2;Lo;0;L;;;;;N;;;;;
12398;CUNEIFORM SIGN UM TIMES ME;Lo;0;L;;;;;N;;;;;
12399;CUNEIFORM SIGN U U;Lo;0;L;;;;;N;;;;;
12399;CUNEIFORM SIGN U U;Lo;0;L;;;;2;N;;;;;
12400;CUNEIFORM NUMERIC SIGN TWO ASH;Nl;0;L;;;;2;N;;;;;
12401;CUNEIFORM NUMERIC SIGN THREE ASH;Nl;0;L;;;;3;N;;;;;
12402;CUNEIFORM NUMERIC SIGN FOUR ASH;Nl;0;L;;;;4;N;;;;;
@ -30124,6 +30288,56 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16E98;MEDEFAIDRIN FULL STOP;Po;0;L;;;;;N;;;;;
16E99;MEDEFAIDRIN SYMBOL AIVA;Po;0;L;;;;;N;;;;;
16E9A;MEDEFAIDRIN EXCLAMATION OH;Po;0;L;;;;;N;;;;;
16EA0;BERIA ERFE CAPITAL LETTER ARKAB;Lu;0;L;;;;;N;;;;16EBB;
16EA1;BERIA ERFE CAPITAL LETTER BASIGNA;Lu;0;L;;;;;N;;;;16EBC;
16EA2;BERIA ERFE CAPITAL LETTER DARBAI;Lu;0;L;;;;;N;;;;16EBD;
16EA3;BERIA ERFE CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;16EBE;
16EA4;BERIA ERFE CAPITAL LETTER FITKO;Lu;0;L;;;;;N;;;;16EBF;
16EA5;BERIA ERFE CAPITAL LETTER GOWAY;Lu;0;L;;;;;N;;;;16EC0;
16EA6;BERIA ERFE CAPITAL LETTER HIRDEABO;Lu;0;L;;;;;N;;;;16EC1;
16EA7;BERIA ERFE CAPITAL LETTER I;Lu;0;L;;;;;N;;;;16EC2;
16EA8;BERIA ERFE CAPITAL LETTER DJAI;Lu;0;L;;;;;N;;;;16EC3;
16EA9;BERIA ERFE CAPITAL LETTER KOBO;Lu;0;L;;;;;N;;;;16EC4;
16EAA;BERIA ERFE CAPITAL LETTER LAKKO;Lu;0;L;;;;;N;;;;16EC5;
16EAB;BERIA ERFE CAPITAL LETTER MERI;Lu;0;L;;;;;N;;;;16EC6;
16EAC;BERIA ERFE CAPITAL LETTER NINI;Lu;0;L;;;;;N;;;;16EC7;
16EAD;BERIA ERFE CAPITAL LETTER GNA;Lu;0;L;;;;;N;;;;16EC8;
16EAE;BERIA ERFE CAPITAL LETTER NGAY;Lu;0;L;;;;;N;;;;16EC9;
16EAF;BERIA ERFE CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;16ECA;
16EB0;BERIA ERFE CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;16ECB;
16EB1;BERIA ERFE CAPITAL LETTER ERIGO;Lu;0;L;;;;;N;;;;16ECC;
16EB2;BERIA ERFE CAPITAL LETTER ERIGO TAMURA;Lu;0;L;;;;;N;;;;16ECD;
16EB3;BERIA ERFE CAPITAL LETTER SERI;Lu;0;L;;;;;N;;;;16ECE;
16EB4;BERIA ERFE CAPITAL LETTER SHEP;Lu;0;L;;;;;N;;;;16ECF;
16EB5;BERIA ERFE CAPITAL LETTER TATASOUE;Lu;0;L;;;;;N;;;;16ED0;
16EB6;BERIA ERFE CAPITAL LETTER UI;Lu;0;L;;;;;N;;;;16ED1;
16EB7;BERIA ERFE CAPITAL LETTER WASSE;Lu;0;L;;;;;N;;;;16ED2;
16EB8;BERIA ERFE CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;16ED3;
16EBB;BERIA ERFE SMALL LETTER ARKAB;Ll;0;L;;;;;N;;;16EA0;;16EA0
16EBC;BERIA ERFE SMALL LETTER BASIGNA;Ll;0;L;;;;;N;;;16EA1;;16EA1
16EBD;BERIA ERFE SMALL LETTER DARBAI;Ll;0;L;;;;;N;;;16EA2;;16EA2
16EBE;BERIA ERFE SMALL LETTER EH;Ll;0;L;;;;;N;;;16EA3;;16EA3
16EBF;BERIA ERFE SMALL LETTER FITKO;Ll;0;L;;;;;N;;;16EA4;;16EA4
16EC0;BERIA ERFE SMALL LETTER GOWAY;Ll;0;L;;;;;N;;;16EA5;;16EA5
16EC1;BERIA ERFE SMALL LETTER HIRDEABO;Ll;0;L;;;;;N;;;16EA6;;16EA6
16EC2;BERIA ERFE SMALL LETTER I;Ll;0;L;;;;;N;;;16EA7;;16EA7
16EC3;BERIA ERFE SMALL LETTER DJAI;Ll;0;L;;;;;N;;;16EA8;;16EA8
16EC4;BERIA ERFE SMALL LETTER KOBO;Ll;0;L;;;;;N;;;16EA9;;16EA9
16EC5;BERIA ERFE SMALL LETTER LAKKO;Ll;0;L;;;;;N;;;16EAA;;16EAA
16EC6;BERIA ERFE SMALL LETTER MERI;Ll;0;L;;;;;N;;;16EAB;;16EAB
16EC7;BERIA ERFE SMALL LETTER NINI;Ll;0;L;;;;;N;;;16EAC;;16EAC
16EC8;BERIA ERFE SMALL LETTER GNA;Ll;0;L;;;;;N;;;16EAD;;16EAD
16EC9;BERIA ERFE SMALL LETTER NGAY;Ll;0;L;;;;;N;;;16EAE;;16EAE
16ECA;BERIA ERFE SMALL LETTER OI;Ll;0;L;;;;;N;;;16EAF;;16EAF
16ECB;BERIA ERFE SMALL LETTER PI;Ll;0;L;;;;;N;;;16EB0;;16EB0
16ECC;BERIA ERFE SMALL LETTER ERIGO;Ll;0;L;;;;;N;;;16EB1;;16EB1
16ECD;BERIA ERFE SMALL LETTER ERIGO TAMURA;Ll;0;L;;;;;N;;;16EB2;;16EB2
16ECE;BERIA ERFE SMALL LETTER SERI;Ll;0;L;;;;;N;;;16EB3;;16EB3
16ECF;BERIA ERFE SMALL LETTER SHEP;Ll;0;L;;;;;N;;;16EB4;;16EB4
16ED0;BERIA ERFE SMALL LETTER TATASOUE;Ll;0;L;;;;;N;;;16EB5;;16EB5
16ED1;BERIA ERFE SMALL LETTER UI;Ll;0;L;;;;;N;;;16EB6;;16EB6
16ED2;BERIA ERFE SMALL LETTER WASSE;Ll;0;L;;;;;N;;;16EB7;;16EB7
16ED3;BERIA ERFE SMALL LETTER AY;Ll;0;L;;;;;N;;;16EB8;;16EB8
16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;;
16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;;
16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;;
@ -30280,8 +30494,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16FE4;KHITAN SMALL SCRIPT FILLER;Mn;0;NSM;;;;;N;;;;;
16FF0;VIETNAMESE ALTERNATE READING MARK CA;Mc;6;L;;;;;N;;;;;
16FF1;VIETNAMESE ALTERNATE READING MARK NHAY;Mc;6;L;;;;;N;;;;;
16FF2;CHINESE SMALL SIMPLIFIED ER;Lm;0;L;;;;;N;;;;;
16FF3;CHINESE SMALL TRADITIONAL ER;Lm;0;L;;;;;N;;;;;
16FF4;YANGQIN SIGN SLOW ONE BEAT;Nl;0;L;;;;1;N;;;;;
16FF5;YANGQIN SIGN SLOW THREE HALF BEATS;Nl;0;L;;;;3/2;N;;;;;
16FF6;YANGQIN SIGN SLOW TWO BEATS;Nl;0;L;;;;2;N;;;;;
17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;;
187F7;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
187FF;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;;
18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;;
18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;;
@ -31522,7 +31741,122 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
18CD5;KHITAN SMALL SCRIPT CHARACTER-18CD5;Lo;0;L;;;;;N;;;;;
18CFF;KHITAN SMALL SCRIPT CHARACTER-18CFF;Lo;0;L;;;;;N;;;;;
18D00;<Tangut Ideograph Supplement, First>;Lo;0;L;;;;;N;;;;;
18D08;<Tangut Ideograph Supplement, Last>;Lo;0;L;;;;;N;;;;;
18D1E;<Tangut Ideograph Supplement, Last>;Lo;0;L;;;;;N;;;;;
18D80;TANGUT COMPONENT-769;Lo;0;L;;;;;N;;;;;
18D81;TANGUT COMPONENT-770;Lo;0;L;;;;;N;;;;;
18D82;TANGUT COMPONENT-771;Lo;0;L;;;;;N;;;;;
18D83;TANGUT COMPONENT-772;Lo;0;L;;;;;N;;;;;
18D84;TANGUT COMPONENT-773;Lo;0;L;;;;;N;;;;;
18D85;TANGUT COMPONENT-774;Lo;0;L;;;;;N;;;;;
18D86;TANGUT COMPONENT-775;Lo;0;L;;;;;N;;;;;
18D87;TANGUT COMPONENT-776;Lo;0;L;;;;;N;;;;;
18D88;TANGUT COMPONENT-777;Lo;0;L;;;;;N;;;;;
18D89;TANGUT COMPONENT-778;Lo;0;L;;;;;N;;;;;
18D8A;TANGUT COMPONENT-779;Lo;0;L;;;;;N;;;;;
18D8B;TANGUT COMPONENT-780;Lo;0;L;;;;;N;;;;;
18D8C;TANGUT COMPONENT-781;Lo;0;L;;;;;N;;;;;
18D8D;TANGUT COMPONENT-782;Lo;0;L;;;;;N;;;;;
18D8E;TANGUT COMPONENT-783;Lo;0;L;;;;;N;;;;;
18D8F;TANGUT COMPONENT-784;Lo;0;L;;;;;N;;;;;
18D90;TANGUT COMPONENT-785;Lo;0;L;;;;;N;;;;;
18D91;TANGUT COMPONENT-786;Lo;0;L;;;;;N;;;;;
18D92;TANGUT COMPONENT-787;Lo;0;L;;;;;N;;;;;
18D93;TANGUT COMPONENT-788;Lo;0;L;;;;;N;;;;;
18D94;TANGUT COMPONENT-789;Lo;0;L;;;;;N;;;;;
18D95;TANGUT COMPONENT-790;Lo;0;L;;;;;N;;;;;
18D96;TANGUT COMPONENT-791;Lo;0;L;;;;;N;;;;;
18D97;TANGUT COMPONENT-792;Lo;0;L;;;;;N;;;;;
18D98;TANGUT COMPONENT-793;Lo;0;L;;;;;N;;;;;
18D99;TANGUT COMPONENT-794;Lo;0;L;;;;;N;;;;;
18D9A;TANGUT COMPONENT-795;Lo;0;L;;;;;N;;;;;
18D9B;TANGUT COMPONENT-796;Lo;0;L;;;;;N;;;;;
18D9C;TANGUT COMPONENT-797;Lo;0;L;;;;;N;;;;;
18D9D;TANGUT COMPONENT-798;Lo;0;L;;;;;N;;;;;
18D9E;TANGUT COMPONENT-799;Lo;0;L;;;;;N;;;;;
18D9F;TANGUT COMPONENT-800;Lo;0;L;;;;;N;;;;;
18DA0;TANGUT COMPONENT-801;Lo;0;L;;;;;N;;;;;
18DA1;TANGUT COMPONENT-802;Lo;0;L;;;;;N;;;;;
18DA2;TANGUT COMPONENT-803;Lo;0;L;;;;;N;;;;;
18DA3;TANGUT COMPONENT-804;Lo;0;L;;;;;N;;;;;
18DA4;TANGUT COMPONENT-805;Lo;0;L;;;;;N;;;;;
18DA5;TANGUT COMPONENT-806;Lo;0;L;;;;;N;;;;;
18DA6;TANGUT COMPONENT-807;Lo;0;L;;;;;N;;;;;
18DA7;TANGUT COMPONENT-808;Lo;0;L;;;;;N;;;;;
18DA8;TANGUT COMPONENT-809;Lo;0;L;;;;;N;;;;;
18DA9;TANGUT COMPONENT-810;Lo;0;L;;;;;N;;;;;
18DAA;TANGUT COMPONENT-811;Lo;0;L;;;;;N;;;;;
18DAB;TANGUT COMPONENT-812;Lo;0;L;;;;;N;;;;;
18DAC;TANGUT COMPONENT-813;Lo;0;L;;;;;N;;;;;
18DAD;TANGUT COMPONENT-814;Lo;0;L;;;;;N;;;;;
18DAE;TANGUT COMPONENT-815;Lo;0;L;;;;;N;;;;;
18DAF;TANGUT COMPONENT-816;Lo;0;L;;;;;N;;;;;
18DB0;TANGUT COMPONENT-817;Lo;0;L;;;;;N;;;;;
18DB1;TANGUT COMPONENT-818;Lo;0;L;;;;;N;;;;;
18DB2;TANGUT COMPONENT-819;Lo;0;L;;;;;N;;;;;
18DB3;TANGUT COMPONENT-820;Lo;0;L;;;;;N;;;;;
18DB4;TANGUT COMPONENT-821;Lo;0;L;;;;;N;;;;;
18DB5;TANGUT COMPONENT-822;Lo;0;L;;;;;N;;;;;
18DB6;TANGUT COMPONENT-823;Lo;0;L;;;;;N;;;;;
18DB7;TANGUT COMPONENT-824;Lo;0;L;;;;;N;;;;;
18DB8;TANGUT COMPONENT-825;Lo;0;L;;;;;N;;;;;
18DB9;TANGUT COMPONENT-826;Lo;0;L;;;;;N;;;;;
18DBA;TANGUT COMPONENT-827;Lo;0;L;;;;;N;;;;;
18DBB;TANGUT COMPONENT-828;Lo;0;L;;;;;N;;;;;
18DBC;TANGUT COMPONENT-829;Lo;0;L;;;;;N;;;;;
18DBD;TANGUT COMPONENT-830;Lo;0;L;;;;;N;;;;;
18DBE;TANGUT COMPONENT-831;Lo;0;L;;;;;N;;;;;
18DBF;TANGUT COMPONENT-832;Lo;0;L;;;;;N;;;;;
18DC0;TANGUT COMPONENT-833;Lo;0;L;;;;;N;;;;;
18DC1;TANGUT COMPONENT-834;Lo;0;L;;;;;N;;;;;
18DC2;TANGUT COMPONENT-835;Lo;0;L;;;;;N;;;;;
18DC3;TANGUT COMPONENT-836;Lo;0;L;;;;;N;;;;;
18DC4;TANGUT COMPONENT-837;Lo;0;L;;;;;N;;;;;
18DC5;TANGUT COMPONENT-838;Lo;0;L;;;;;N;;;;;
18DC6;TANGUT COMPONENT-839;Lo;0;L;;;;;N;;;;;
18DC7;TANGUT COMPONENT-840;Lo;0;L;;;;;N;;;;;
18DC8;TANGUT COMPONENT-841;Lo;0;L;;;;;N;;;;;
18DC9;TANGUT COMPONENT-842;Lo;0;L;;;;;N;;;;;
18DCA;TANGUT COMPONENT-843;Lo;0;L;;;;;N;;;;;
18DCB;TANGUT COMPONENT-844;Lo;0;L;;;;;N;;;;;
18DCC;TANGUT COMPONENT-845;Lo;0;L;;;;;N;;;;;
18DCD;TANGUT COMPONENT-846;Lo;0;L;;;;;N;;;;;
18DCE;TANGUT COMPONENT-847;Lo;0;L;;;;;N;;;;;
18DCF;TANGUT COMPONENT-848;Lo;0;L;;;;;N;;;;;
18DD0;TANGUT COMPONENT-849;Lo;0;L;;;;;N;;;;;
18DD1;TANGUT COMPONENT-850;Lo;0;L;;;;;N;;;;;
18DD2;TANGUT COMPONENT-851;Lo;0;L;;;;;N;;;;;
18DD3;TANGUT COMPONENT-852;Lo;0;L;;;;;N;;;;;
18DD4;TANGUT COMPONENT-853;Lo;0;L;;;;;N;;;;;
18DD5;TANGUT COMPONENT-854;Lo;0;L;;;;;N;;;;;
18DD6;TANGUT COMPONENT-855;Lo;0;L;;;;;N;;;;;
18DD7;TANGUT COMPONENT-856;Lo;0;L;;;;;N;;;;;
18DD8;TANGUT COMPONENT-857;Lo;0;L;;;;;N;;;;;
18DD9;TANGUT COMPONENT-858;Lo;0;L;;;;;N;;;;;
18DDA;TANGUT COMPONENT-859;Lo;0;L;;;;;N;;;;;
18DDB;TANGUT COMPONENT-860;Lo;0;L;;;;;N;;;;;
18DDC;TANGUT COMPONENT-861;Lo;0;L;;;;;N;;;;;
18DDD;TANGUT COMPONENT-862;Lo;0;L;;;;;N;;;;;
18DDE;TANGUT COMPONENT-863;Lo;0;L;;;;;N;;;;;
18DDF;TANGUT COMPONENT-864;Lo;0;L;;;;;N;;;;;
18DE0;TANGUT COMPONENT-865;Lo;0;L;;;;;N;;;;;
18DE1;TANGUT COMPONENT-866;Lo;0;L;;;;;N;;;;;
18DE2;TANGUT COMPONENT-867;Lo;0;L;;;;;N;;;;;
18DE3;TANGUT COMPONENT-868;Lo;0;L;;;;;N;;;;;
18DE4;TANGUT COMPONENT-869;Lo;0;L;;;;;N;;;;;
18DE5;TANGUT COMPONENT-870;Lo;0;L;;;;;N;;;;;
18DE6;TANGUT COMPONENT-871;Lo;0;L;;;;;N;;;;;
18DE7;TANGUT COMPONENT-872;Lo;0;L;;;;;N;;;;;
18DE8;TANGUT COMPONENT-873;Lo;0;L;;;;;N;;;;;
18DE9;TANGUT COMPONENT-874;Lo;0;L;;;;;N;;;;;
18DEA;TANGUT COMPONENT-875;Lo;0;L;;;;;N;;;;;
18DEB;TANGUT COMPONENT-876;Lo;0;L;;;;;N;;;;;
18DEC;TANGUT COMPONENT-877;Lo;0;L;;;;;N;;;;;
18DED;TANGUT COMPONENT-878;Lo;0;L;;;;;N;;;;;
18DEE;TANGUT COMPONENT-879;Lo;0;L;;;;;N;;;;;
18DEF;TANGUT COMPONENT-880;Lo;0;L;;;;;N;;;;;
18DF0;TANGUT COMPONENT-881;Lo;0;L;;;;;N;;;;;
18DF1;TANGUT COMPONENT-882;Lo;0;L;;;;;N;;;;;
18DF2;TANGUT COMPONENT-883;Lo;0;L;;;;;N;;;;;
1AFF0;KATAKANA LETTER MINNAN TONE-2;Lm;0;L;;;;;N;;;;;
1AFF1;KATAKANA LETTER MINNAN TONE-3;Lm;0;L;;;;;N;;;;;
1AFF2;KATAKANA LETTER MINNAN TONE-4;Lm;0;L;;;;;N;;;;;
@ -32629,6 +32963,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1CCF7;OUTLINED DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
1CCF8;OUTLINED DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
1CCF9;OUTLINED DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
1CCFA;SNAKE SYMBOL;So;0;ON;;;;;N;;;;;
1CCFB;FLYING SAUCER SYMBOL;So;0;ON;;;;;N;;;;;
1CCFC;NOSE SYMBOL;So;0;ON;;;;;N;;;;;
1CD00;BLOCK OCTANT-3;So;0;ON;;;;;N;;;;;
1CD01;BLOCK OCTANT-23;So;0;ON;;;;;N;;;;;
1CD02;BLOCK OCTANT-123;So;0;ON;;;;;N;;;;;
@ -33065,6 +33402,46 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1CEB1;KEYHOLE;So;0;ON;;;;;N;;;;;
1CEB2;OLD PERSONAL COMPUTER WITH MONITOR IN PORTRAIT ORIENTATION;So;0;ON;;;;;N;;;;;
1CEB3;BLACK RIGHT TRIANGLE CARET;So;0;ON;;;;;N;;;;;
1CEBA;FRAGILE SYMBOL;So;0;ON;;;;;N;;;;;
1CEBB;OFFICE BUILDING SYMBOL;So;0;ON;;;;;N;;;;;
1CEBC;TREE SYMBOL;So;0;ON;;;;;N;;;;;
1CEBD;APPLE SYMBOL;So;0;ON;;;;;N;;;;;
1CEBE;CHERRY SYMBOL;So;0;ON;;;;;N;;;;;
1CEBF;STRAWBERRY SYMBOL;So;0;ON;;;;;N;;;;;
1CEC0;HEBE;So;0;ON;;;;;N;;;;;
1CEC1;IRIS;So;0;ON;;;;;N;;;;;
1CEC2;FLORA;So;0;ON;;;;;N;;;;;
1CEC3;METIS;So;0;ON;;;;;N;;;;;
1CEC4;PARTHENOPE;So;0;ON;;;;;N;;;;;
1CEC5;VICTORIA;So;0;ON;;;;;N;;;;;
1CEC6;EGERIA;So;0;ON;;;;;N;;;;;
1CEC7;IRENE;So;0;ON;;;;;N;;;;;
1CEC8;EUNOMIA;So;0;ON;;;;;N;;;;;
1CEC9;PSYCHE;So;0;ON;;;;;N;;;;;
1CECA;THETIS;So;0;ON;;;;;N;;;;;
1CECB;MELPOMENE;So;0;ON;;;;;N;;;;;
1CECC;FORTUNA;So;0;ON;;;;;N;;;;;
1CECD;ASTRONOMICAL SYMBOL FOR ASTEROID PROSERPINA;So;0;ON;;;;;N;;;;;
1CECE;BELLONA;So;0;ON;;;;;N;;;;;
1CECF;AMPHITRITE;So;0;ON;;;;;N;;;;;
1CED0;LEUKOTHEA;So;0;ON;;;;;N;;;;;
1CEE0;GEOMANTIC FIGURE POPULUS;So;0;ON;;;;;N;;;;;
1CEE1;GEOMANTIC FIGURE TRISTITIA;So;0;ON;;;;;N;;;;;
1CEE2;GEOMANTIC FIGURE ALBUS;So;0;ON;;;;;N;;;;;
1CEE3;GEOMANTIC FIGURE FORTUNA MAJOR;So;0;ON;;;;;N;;;;;
1CEE4;GEOMANTIC FIGURE RUBEUS;So;0;ON;;;;;N;;;;;
1CEE5;GEOMANTIC FIGURE ACQUISITIO;So;0;ON;;;;;N;;;;;
1CEE6;GEOMANTIC FIGURE CONJUNCTIO;So;0;ON;;;;;N;;;;;
1CEE7;GEOMANTIC FIGURE CAPUT DRACONIS;So;0;ON;;;;;N;;;;;
1CEE8;GEOMANTIC FIGURE LAETITIA;So;0;ON;;;;;N;;;;;
1CEE9;GEOMANTIC FIGURE CARCER;So;0;ON;;;;;N;;;;;
1CEEA;GEOMANTIC FIGURE AMISSIO;So;0;ON;;;;;N;;;;;
1CEEB;GEOMANTIC FIGURE PUELLA;So;0;ON;;;;;N;;;;;
1CEEC;GEOMANTIC FIGURE FORTUNA MINOR;So;0;ON;;;;;N;;;;;
1CEED;GEOMANTIC FIGURE PUER;So;0;ON;;;;;N;;;;;
1CEEE;GEOMANTIC FIGURE CAUDA DRACONIS;So;0;ON;;;;;N;;;;;
1CEEF;GEOMANTIC FIGURE VIA;So;0;ON;;;;;N;;;;;
1CEF0;MEDIUM SMALL WHITE CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;;
1CF00;ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;;
1CF01;ZNAMENNY COMBINING MARK NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;;
1CF02;ZNAMENNY COMBINING MARK TSATA ON LEFT;Mn;0;NSM;;;;;N;;;;;
@ -36004,6 +36381,61 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E5F9;OL ONAL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
1E5FA;OL ONAL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
1E5FF;OL ONAL ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
1E6C0;TAI YO LETTER LOW KO;Lo;0;L;;;;;N;;;;;
1E6C1;TAI YO LETTER HIGH KO;Lo;0;L;;;;;N;;;;;
1E6C2;TAI YO LETTER LOW KHO;Lo;0;L;;;;;N;;;;;
1E6C3;TAI YO LETTER HIGH KHO;Lo;0;L;;;;;N;;;;;
1E6C4;TAI YO LETTER GO;Lo;0;L;;;;;N;;;;;
1E6C5;TAI YO LETTER NGO;Lo;0;L;;;;;N;;;;;
1E6C6;TAI YO LETTER CO;Lo;0;L;;;;;N;;;;;
1E6C7;TAI YO LETTER LOW XO;Lo;0;L;;;;;N;;;;;
1E6C8;TAI YO LETTER HIGH XO;Lo;0;L;;;;;N;;;;;
1E6C9;TAI YO LETTER LOW NYO;Lo;0;L;;;;;N;;;;;
1E6CA;TAI YO LETTER HIGH NYO;Lo;0;L;;;;;N;;;;;
1E6CB;TAI YO LETTER DO;Lo;0;L;;;;;N;;;;;
1E6CC;TAI YO LETTER LOW TO;Lo;0;L;;;;;N;;;;;
1E6CD;TAI YO LETTER HIGH TO;Lo;0;L;;;;;N;;;;;
1E6CE;TAI YO LETTER THO;Lo;0;L;;;;;N;;;;;
1E6CF;TAI YO LETTER NO;Lo;0;L;;;;;N;;;;;
1E6D0;TAI YO LETTER BO;Lo;0;L;;;;;N;;;;;
1E6D1;TAI YO LETTER LOW PO;Lo;0;L;;;;;N;;;;;
1E6D2;TAI YO LETTER HIGH PO;Lo;0;L;;;;;N;;;;;
1E6D3;TAI YO LETTER PHO;Lo;0;L;;;;;N;;;;;
1E6D4;TAI YO LETTER LOW FO;Lo;0;L;;;;;N;;;;;
1E6D5;TAI YO LETTER HIGH FO;Lo;0;L;;;;;N;;;;;
1E6D6;TAI YO LETTER MO;Lo;0;L;;;;;N;;;;;
1E6D7;TAI YO LETTER YO;Lo;0;L;;;;;N;;;;;
1E6D8;TAI YO LETTER LO;Lo;0;L;;;;;N;;;;;
1E6D9;TAI YO LETTER VO;Lo;0;L;;;;;N;;;;;
1E6DA;TAI YO LETTER LOW HO;Lo;0;L;;;;;N;;;;;
1E6DB;TAI YO LETTER HIGH HO;Lo;0;L;;;;;N;;;;;
1E6DC;TAI YO LETTER QO;Lo;0;L;;;;;N;;;;;
1E6DD;TAI YO LETTER LOW KVO;Lo;0;L;;;;;N;;;;;
1E6DE;TAI YO LETTER HIGH KVO;Lo;0;L;;;;;N;;;;;
1E6E0;TAI YO LETTER AA;Lo;0;L;;;;;N;;;;;
1E6E1;TAI YO LETTER I;Lo;0;L;;;;;N;;;;;
1E6E2;TAI YO LETTER UE;Lo;0;L;;;;;N;;;;;
1E6E3;TAI YO SIGN UE;Mn;230;NSM;;;;;N;;;;;
1E6E4;TAI YO LETTER U;Lo;0;L;;;;;N;;;;;
1E6E5;TAI YO LETTER AE;Lo;0;L;;;;;N;;;;;
1E6E6;TAI YO SIGN AU;Mn;230;NSM;;;;;N;;;;;
1E6E7;TAI YO LETTER O;Lo;0;L;;;;;N;;;;;
1E6E8;TAI YO LETTER E;Lo;0;L;;;;;N;;;;;
1E6E9;TAI YO LETTER IA;Lo;0;L;;;;;N;;;;;
1E6EA;TAI YO LETTER UEA;Lo;0;L;;;;;N;;;;;
1E6EB;TAI YO LETTER UA;Lo;0;L;;;;;N;;;;;
1E6EC;TAI YO LETTER OO;Lo;0;L;;;;;N;;;;;
1E6ED;TAI YO LETTER AUE;Lo;0;L;;;;;N;;;;;
1E6EE;TAI YO SIGN AY;Mn;230;NSM;;;;;N;;;;;
1E6EF;TAI YO SIGN ANG;Mn;230;NSM;;;;;N;;;;;
1E6F0;TAI YO LETTER AN;Lo;0;L;;;;;N;;;;;
1E6F1;TAI YO LETTER AM;Lo;0;L;;;;;N;;;;;
1E6F2;TAI YO LETTER AK;Lo;0;L;;;;;N;;;;;
1E6F3;TAI YO LETTER AT;Lo;0;L;;;;;N;;;;;
1E6F4;TAI YO LETTER AP;Lo;0;L;;;;;N;;;;;
1E6F5;TAI YO SIGN OM;Mn;230;NSM;;;;;N;;;;;
1E6FE;TAI YO SYMBOL MUEANG;Lo;0;L;;;;;N;;;;;
1E6FF;TAI YO XAM LAI;Lm;0;L;;;;;N;;;;;
1E7E0;ETHIOPIC SYLLABLE HHYA;Lo;0;L;;;;;N;;;;;
1E7E1;ETHIOPIC SYLLABLE HHYU;Lo;0;L;;;;;N;;;;;
1E7E2;ETHIOPIC SYLLABLE HHYI;Lo;0;L;;;;;N;;;;;
@ -38079,6 +38511,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
1F6D6;HUT;So;0;ON;;;;;N;;;;;
1F6D7;ELEVATOR;So;0;ON;;;;;N;;;;;
1F6D8;LANDSLIDE;So;0;ON;;;;;N;;;;;
1F6DC;WIRELESS;So;0;ON;;;;;N;;;;;
1F6DD;PLAYGROUND SLIDE;So;0;ON;;;;;N;;;;;
1F6DE;WHEEL;So;0;ON;;;;;N;;;;;
@ -38228,6 +38661,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F774;LOT OF FORTUNE;So;0;ON;;;;;N;;;;;
1F775;OCCULTATION;So;0;ON;;;;;N;;;;;
1F776;LUNAR ECLIPSE;So;0;ON;;;;;N;;;;;
1F777;VESTA FORM TWO;So;0;ON;;;;;N;;;;;
1F778;ASTRAEA FORM TWO;So;0;ON;;;;;N;;;;;
1F779;HYGIEA FORM TWO;So;0;ON;;;;;N;;;;;
1F77A;PARTHENOPE FORM TWO;So;0;ON;;;;;N;;;;;
1F77B;HAUMEA;So;0;ON;;;;;N;;;;;
1F77C;MAKEMAKE;So;0;ON;;;;;N;;;;;
1F77D;GONGGONG;So;0;ON;;;;;N;;;;;
@ -38498,6 +38935,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F8BB;SOUTH WEST ARROW FROM BAR;So;0;ON;;;;;N;;;;;
1F8C0;LEFTWARDS ARROW FROM DOWNWARDS ARROW;So;0;ON;;;;;N;;;;;
1F8C1;RIGHTWARDS ARROW FROM DOWNWARDS ARROW;So;0;ON;;;;;N;;;;;
1F8D0;LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
1F8D1;LONG RIGHTWARDS HARPOON OVER LONG LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;;
1F8D2;LONG RIGHTWARDS HARPOON ABOVE SHORT LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;;
1F8D3;SHORT RIGHTWARDS HARPOON ABOVE LONG LEFTWARDS HARPOON;Sm;0;ON;;;;;N;;;;;
1F8D4;LONG LEFTWARDS HARPOON ABOVE SHORT RIGHTWARDS HARPOON;Sm;0;ON;;;;;N;;;;;
1F8D5;SHORT LEFTWARDS HARPOON ABOVE LONG RIGHTWARDS HARPOON;Sm;0;ON;;;;;N;;;;;
1F8D6;LONG RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;;
1F8D7;LONG RIGHTWARDS ARROW WITH DOUBLE SLASH;Sm;0;ON;;;;;N;;;;;
1F8D8;LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE;Sm;0;ON;;;;;N;;;;;
1F900;CIRCLED CROSS FORMEE WITH FOUR DOTS;So;0;ON;;;;;N;;;;;
1F901;CIRCLED CROSS FORMEE WITH TWO DOTS;So;0;ON;;;;;N;;;;;
1F902;CIRCLED CROSS FORMEE;So;0;ON;;;;;N;;;;;
@ -38838,6 +39284,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
1FA54;WHITE CHESS FERZ;So;0;ON;;;;;N;;;;;
1FA55;WHITE CHESS ALFIL;So;0;ON;;;;;N;;;;;
1FA56;BLACK CHESS FERZ;So;0;ON;;;;;N;;;;;
1FA57;BLACK CHESS ALFIL;So;0;ON;;;;;N;;;;;
1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;;
1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;;
1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;;
@ -38875,6 +39325,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA87;MARACAS;So;0;ON;;;;;N;;;;;
1FA88;FLUTE;So;0;ON;;;;;N;;;;;
1FA89;HARP;So;0;ON;;;;;N;;;;;
1FA8A;TROMBONE;So;0;ON;;;;;N;;;;;
1FA8E;TREASURE CHEST;So;0;ON;;;;;N;;;;;
1FA8F;SHOVEL;So;0;ON;;;;;N;;;;;
1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
1FA91;CHAIR;So;0;ON;;;;;N;;;;;
@ -38931,6 +39383,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAC4;PREGNANT PERSON;So;0;ON;;;;;N;;;;;
1FAC5;PERSON WITH CROWN;So;0;ON;;;;;N;;;;;
1FAC6;FINGERPRINT;So;0;ON;;;;;N;;;;;
1FAC8;HAIRY CREATURE;So;0;ON;;;;;N;;;;;
1FACD;ORCA;So;0;ON;;;;;N;;;;;
1FACE;MOOSE;So;0;ON;;;;;N;;;;;
1FACF;DONKEY;So;0;ON;;;;;N;;;;;
1FAD0;BLUEBERRIES;So;0;ON;;;;;N;;;;;
@ -38957,6 +39411,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAE7;BUBBLES;So;0;ON;;;;;N;;;;;
1FAE8;SHAKING FACE;So;0;ON;;;;;N;;;;;
1FAE9;FACE WITH BAGS UNDER EYES;So;0;ON;;;;;N;;;;;
1FAEA;DISTORTED FACE;So;0;ON;;;;;N;;;;;
1FAEF;FIGHT CLOUD;So;0;ON;;;;;N;;;;;
1FAF0;HAND WITH INDEX FINGER AND THUMB CROSSED;So;0;ON;;;;;N;;;;;
1FAF1;RIGHTWARDS HAND;So;0;ON;;;;;N;;;;;
1FAF2;LEFTWARDS HAND;So;0;ON;;;;;N;;;;;
@ -39215,14 +39671,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FBF7;SEGMENTED DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
1FBF8;SEGMENTED DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
1FBF9;SEGMENTED DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
1FBFA;ALARM BELL SYMBOL;So;0;ON;;;;;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
2A6DF;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
2B739;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
2B73F;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
2B740;<CJK Ideograph Extension D, First>;Lo;0;L;;;;;N;;;;;
2B81D;<CJK Ideograph Extension D, Last>;Lo;0;L;;;;;N;;;;;
2B820;<CJK Ideograph Extension E, First>;Lo;0;L;;;;;N;;;;;
2CEA1;<CJK Ideograph Extension E, Last>;Lo;0;L;;;;;N;;;;;
2CEAD;<CJK Ideograph Extension E, Last>;Lo;0;L;;;;;N;;;;;
2CEB0;<CJK Ideograph Extension F, First>;Lo;0;L;;;;;N;;;;;
2EBE0;<CJK Ideograph Extension F, Last>;Lo;0;L;;;;;N;;;;;
2EBF0;<CJK Ideograph Extension I, First>;Lo;0;L;;;;;N;;;;;
@ -39773,6 +40230,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
3134A;<CJK Ideograph Extension G, Last>;Lo;0;L;;;;;N;;;;;
31350;<CJK Ideograph Extension H, First>;Lo;0;L;;;;;N;;;;;
323AF;<CJK Ideograph Extension H, Last>;Lo;0;L;;;;;N;;;;;
323B0;<CJK Ideograph Extension J, First>;Lo;0;L;;;;;N;;;;;
33479;<CJK Ideograph Extension J, Last>;Lo;0;L;;;;;N;;;;;
E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;

View file

@ -1,6 +1,6 @@
# WordBreakProperty-16.0.0.txt
# Date: 2024-07-29, 16:27:36 GMT
# © 2024 Unicode®, Inc.
# WordBreakProperty-17.0.0.txt
# Date: 2025-06-30, 06:20:49 GMT
# © 2025 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use and license, see https://www.unicode.org/terms_of_use.html
#
@ -283,7 +283,8 @@ FB46..FB4F ; Hebrew_Letter # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW
1A7F ; Extend # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT
1AB0..1ABD ; Extend # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY
1ABF..1ACE ; Extend # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
1ABF..1ADD ; Extend # Mn [31] COMBINING LATIN SMALL LETTER W BELOW..COMBINING DOT-AND-RING BELOW
1AE0..1AEB ; Extend # Mn [12] COMBINING LEFT TACK ABOVE..COMBINING DOUBLE RIGHTWARDS ARROW ABOVE
1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
1B04 ; Extend # Mc BALINESE SIGN BISAH
1B34 ; Extend # Mn BALINESE SIGN REREKAN
@ -409,7 +410,7 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
10D24..10D27 ; Extend # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
10D69..10D6D ; Extend # Mn [5] GARAY VOWEL SIGN E..GARAY CONSONANT NASALIZATION MARK
10EAB..10EAC ; Extend # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
10EFC..10EFF ; Extend # Mn [4] ARABIC COMBINING ALEF OVERLAY..ARABIC SMALL LOW WORD MADDA
10EFA..10EFF ; Extend # Mn [6] ARABIC DOUBLE VERTICAL BAR BELOW..ARABIC SMALL LOW WORD MADDA
10F46..10F50 ; Extend # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
10F82..10F85 ; Extend # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
11000 ; Extend # Mc BRAHMI SIGN CANDRABINDU
@ -544,6 +545,12 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11A8A..11A96 ; Extend # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
11A97 ; Extend # Mc SOYOMBO SIGN VISARGA
11A98..11A99 ; Extend # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
11B60 ; Extend # Mn SHARADA VOWEL SIGN OE
11B61 ; Extend # Mc SHARADA VOWEL SIGN OOE
11B62..11B64 ; Extend # Mn [3] SHARADA VOWEL SIGN UE..SHARADA VOWEL SIGN SHORT E
11B65 ; Extend # Mc SHARADA VOWEL SIGN SHORT O
11B66 ; Extend # Mn SHARADA VOWEL SIGN CANDRA E
11B67 ; Extend # Mc SHARADA VOWEL SIGN CANDRA O
11C2F ; Extend # Mc BHAIKSUKI VOWEL SIGN AA
11C30..11C36 ; Extend # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
11C38..11C3D ; Extend # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
@ -617,13 +624,17 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E4EC..1E4EF ; Extend # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
1E5EE..1E5EF ; Extend # Mn [2] OL ONAL SIGN MU..OL ONAL SIGN IKIR
1E6E3 ; Extend # Mn TAI YO SIGN UE
1E6E6 ; Extend # Mn TAI YO SIGN AU
1E6EE..1E6EF ; Extend # Mn [2] TAI YO SIGN AY..TAI YO SIGN ANG
1E6F5 ; Extend # Mn TAI YO SIGN OM
1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG
E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
# Total code points: 2605
# Total code points: 2647
# ================================================
@ -679,6 +690,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK
0061..007A ; ALetter # L& [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
00AA ; ALetter # Lo FEMININE ORDINAL INDICATOR
00B5 ; ALetter # L& MICRO SIGN
00B8 ; ALetter # Sk CEDILLA
00BA ; ALetter # Lo MASCULINE ORDINAL INDICATOR
00C0..00D6 ; ALetter # L& [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS
00D8..00F6 ; ALetter # L& [31] LATIN CAPITAL LETTER O WITH STROKE..LATIN SMALL LETTER O WITH DIAERESIS
@ -687,8 +699,8 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK
01BC..01BF ; ALetter # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
01C0..01C3 ; ALetter # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
01C4..0293 ; ALetter # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
0294 ; ALetter # Lo LATIN LETTER GLOTTAL STOP
0295..02AF ; ALetter # L& [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
0294..0295 ; ALetter # Lo [2] LATIN LETTER GLOTTAL STOP..LATIN LETTER PHARYNGEAL VOICED FRICATIVE
0296..02AF ; ALetter # L& [26] LATIN LETTER INVERTED GLOTTAL STOP..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
02B0..02C1 ; ALetter # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
02C2..02C5 ; ALetter # Sk [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD
02C6..02D1 ; ALetter # Lm [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
@ -745,7 +757,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK
0840..0858 ; ALetter # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
0860..086A ; ALetter # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
0870..0887 ; ALetter # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
0889..088E ; ALetter # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
0889..088F ; ALetter # Lo [7] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC LETTER NOON WITH RING ABOVE
08A0..08C8 ; ALetter # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
08C9 ; ALetter # Lm ARABIC SMALL FARSI YEH
0904..0939 ; ALetter # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
@ -813,7 +825,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK
0C2A..0C39 ; ALetter # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA
0C3D ; ALetter # Lo TELUGU SIGN AVAGRAHA
0C58..0C5A ; ALetter # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
0C5D ; ALetter # Lo TELUGU LETTER NAKAARA POLLU
0C5C..0C5D ; ALetter # Lo [2] TELUGU ARCHAIC SHRII..TELUGU LETTER NAKAARA POLLU
0C60..0C61 ; ALetter # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
0C80 ; ALetter # Lo KANNADA SIGN SPACING CANDRABINDU
0C85..0C8C ; ALetter # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
@ -822,7 +834,7 @@ FF71..FF9D ; Katakana # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAK
0CAA..0CB3 ; ALetter # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA
0CB5..0CB9 ; ALetter # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA
0CBD ; ALetter # Lo KANNADA SIGN AVAGRAHA
0CDD..0CDE ; ALetter # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
0CDC..0CDE ; ALetter # Lo [3] KANNADA ARCHAIC SHRII..KANNADA LETTER FA
0CE0..0CE1 ; ALetter # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
0CF1..0CF2 ; ALetter # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
0D04..0D0C ; ALetter # Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L
@ -1002,11 +1014,8 @@ A788 ; ALetter # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
A789..A78A ; ALetter # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
A78B..A78E ; ALetter # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
A78F ; ALetter # Lo LATIN LETTER SINOLOGICAL DOT
A790..A7CD ; ALetter # L& [62] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH DIAGONAL STROKE
A7D0..A7D1 ; ALetter # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
A7D3 ; ALetter # L& LATIN SMALL LETTER DOUBLE THORN
A7D5..A7DC ; ALetter # L& [8] LATIN SMALL LETTER DOUBLE WYNN..LATIN CAPITAL LETTER LAMBDA WITH STROKE
A7F2..A7F4 ; ALetter # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
A790..A7DC ; ALetter # L& [77] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN CAPITAL LETTER LAMBDA WITH STROKE
A7F1..A7F4 ; ALetter # Lm [4] MODIFIER LETTER CAPITAL S..MODIFIER LETTER CAPITAL Q
A7F5..A7F6 ; ALetter # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
A7F7 ; ALetter # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I
A7F8..A7F9 ; ALetter # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@ -1115,6 +1124,7 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
108F4..108F5 ; ALetter # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
10900..10915 ; ALetter # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
10920..10939 ; ALetter # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C
10940..10959 ; ALetter # Lo [26] SIDETIC LETTER N01..SIDETIC LETTER N26
10980..109B7 ; ALetter # Lo [56] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC CURSIVE LETTER DA
109BE..109BF ; ALetter # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
10A00 ; ALetter # Lo KHAROSHTHI LETTER A
@ -1142,6 +1152,8 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
10E80..10EA9 ; ALetter # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET
10EB0..10EB1 ; ALetter # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
10EC2..10EC4 ; ALetter # Lo [3] ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS VERTICALLY BELOW
10EC5 ; ALetter # Lm ARABIC SMALL YEH BARREE WITH TWO DOTS BELOW
10EC6..10EC7 ; ALetter # Lo [2] ARABIC LETTER THIN NOON..ARABIC LETTER YEH WITH FOUR DOTS BELOW
10F00..10F1C ; ALetter # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
10F27 ; ALetter # Lo OLD SOGDIAN LIGATURE AYIN-DALETH
10F30..10F45 ; ALetter # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
@ -1232,6 +1244,9 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
11D67..11D68 ; ALetter # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI
11D6A..11D89 ; ALetter # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA
11D98 ; ALetter # Lo GUNJALA GONDI OM
11DB0..11DD8 ; ALetter # Lo [41] TOLONG SIKI LETTER I..TOLONG SIKI LETTER RRH
11DD9 ; ALetter # Lm TOLONG SIKI SIGN SELA
11DDA..11DDB ; ALetter # Lo [2] TOLONG SIKI SIGN HECAKA..TOLONG SIKI UNGGA
11EE0..11EF2 ; ALetter # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA
11F02 ; ALetter # Lo KAWI SIGN REPHA
11F04..11F10 ; ALetter # Lo [13] KAWI LETTER A..KAWI LETTER O
@ -1258,6 +1273,8 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
16D43..16D6A ; ALetter # Lo [40] KIRAT RAI LETTER A..KIRAT RAI VOWEL SIGN AU
16D6B..16D6C ; ALetter # Lm [2] KIRAT RAI SIGN VIRAMA..KIRAT RAI SIGN SAAT
16E40..16E7F ; ALetter # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
16EA0..16EB8 ; ALetter # L& [25] BERIA ERFE CAPITAL LETTER ARKAB..BERIA ERFE CAPITAL LETTER AY
16EBB..16ED3 ; ALetter # L& [25] BERIA ERFE SMALL LETTER ARKAB..BERIA ERFE SMALL LETTER AY
16F00..16F4A ; ALetter # Lo [75] MIAO LETTER PA..MIAO LETTER RTE
16F50 ; ALetter # Lo MIAO LETTER NASALIZATION
16F93..16F9F ; ALetter # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
@ -1311,6 +1328,13 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
1E4EB ; ALetter # Lm NAG MUNDARI SIGN OJOD
1E5D0..1E5ED ; ALetter # Lo [30] OL ONAL LETTER O..OL ONAL LETTER EG
1E5F0 ; ALetter # Lo OL ONAL SIGN HODDOND
1E6C0..1E6DE ; ALetter # Lo [31] TAI YO LETTER LOW KO..TAI YO LETTER HIGH KVO
1E6E0..1E6E2 ; ALetter # Lo [3] TAI YO LETTER AA..TAI YO LETTER UE
1E6E4..1E6E5 ; ALetter # Lo [2] TAI YO LETTER U..TAI YO LETTER AE
1E6E7..1E6ED ; ALetter # Lo [7] TAI YO LETTER O..TAI YO LETTER AUE
1E6F0..1E6F4 ; ALetter # Lo [5] TAI YO LETTER AN..TAI YO LETTER AP
1E6FE ; ALetter # Lo TAI YO SYMBOL MUEANG
1E6FF ; ALetter # Lm TAI YO XAM LAI
1E7E0..1E7E6 ; ALetter # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
1E7E8..1E7EB ; ALetter # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
1E7ED..1E7EE ; ALetter # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
@ -1355,7 +1379,7 @@ FFDA..FFDC ; ALetter # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL
1F150..1F169 ; ALetter # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
1F170..1F189 ; ALetter # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
# Total code points: 33791
# Total code points: 33973
# ================================================
@ -1467,6 +1491,7 @@ FF10..FF19 ; Numeric # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
11C50..11C59 ; Numeric # Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE
11D50..11D59 ; Numeric # Nd [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE
11DA0..11DA9 ; Numeric # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
11DE0..11DE9 ; Numeric # Nd [10] TOLONG SIKI DIGIT ZERO..TOLONG SIKI DIGIT NINE
11F50..11F59 ; Numeric # Nd [10] KAWI DIGIT ZERO..KAWI DIGIT NINE
16130..16139 ; Numeric # Nd [10] GURUNG KHEMA DIGIT ZERO..GURUNG KHEMA DIGIT NINE
16A60..16A69 ; Numeric # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE
@ -1482,7 +1507,7 @@ FF10..FF19 ; Numeric # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
1E950..1E959 ; Numeric # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE
1FBF0..1FBF9 ; Numeric # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
# Total code points: 774
# Total code points: 784
# ================================================

View file

@ -1,11 +1,11 @@
# emoji-data.txt
# Date: 2024-05-01, 21:25:24 GMT
# © 2024 Unicode®, Inc.
# Date: 2025-07-25, 17:54:31 GMT
# © 2025 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use and license, see https://www.unicode.org/terms_of_use.html
#
# Emoji Data for UTS #51
# Used with Emoji Version 16.0 and subsequent minor revisions (if any)
# Version: 17.0
#
# For documentation and usage, see https://www.unicode.org/reports/tr51
#
@ -340,6 +340,7 @@
1F6D1..1F6D2 ; Emoji # E3.0 [2] (🛑..🛒) stop sign..shopping cart
1F6D5 ; Emoji # E12.0 [1] (🛕) hindu temple
1F6D6..1F6D7 ; Emoji # E13.0 [2] (🛖..🛗) hut..elevator
1F6D8 ; Emoji # E17.0 [1] (🛘) landslide
1F6DC ; Emoji # E15.0 [1] (🛜) wireless
1F6DD..1F6DF ; Emoji # E14.0 [3] (🛝..🛟) playground slide..ring buoy
1F6E0..1F6E5 ; Emoji # E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat
@ -408,6 +409,8 @@
1FA83..1FA86 ; Emoji # E13.0 [4] (🪃..🪆) boomerang..nesting dolls
1FA87..1FA88 ; Emoji # E15.0 [2] (🪇..🪈) maracas..flute
1FA89 ; Emoji # E16.0 [1] (🪉) harp
1FA8A ; Emoji # E17.0 [1] (🪊) trombone
1FA8E ; Emoji # E17.0 [1] (🪎) treasure chest
1FA8F ; Emoji # E16.0 [1] (🪏) shovel
1FA90..1FA95 ; Emoji # E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Emoji # E13.0 [19] (🪖..🪨) military helmet..rock
@ -421,6 +424,8 @@
1FAC0..1FAC2 ; Emoji # E13.0 [3] (🫀..🫂) anatomical heart..people hugging
1FAC3..1FAC5 ; Emoji # E14.0 [3] (🫃..🫅) pregnant man..person with crown
1FAC6 ; Emoji # E16.0 [1] (🫆) fingerprint
1FAC8 ; Emoji # E17.0 [1] (🫈) hairy creature
1FACD ; Emoji # E17.0 [1] (🫍) orca
1FACE..1FACF ; Emoji # E15.0 [2] (🫎..🫏) moose..donkey
1FAD0..1FAD6 ; Emoji # E13.0 [7] (🫐..🫖) blueberries..teapot
1FAD7..1FAD9 ; Emoji # E14.0 [3] (🫗..🫙) pouring liquid..jar
@ -430,10 +435,12 @@
1FAE0..1FAE7 ; Emoji # E14.0 [8] (🫠..🫧) melting face..bubbles
1FAE8 ; Emoji # E15.0 [1] (🫨) shaking face
1FAE9 ; Emoji # E16.0 [1] (🫩) face with bags under eyes
1FAEA ; Emoji # E17.0 [1] (🫪) distorted face
1FAEF ; Emoji # E17.0 [1] (🫯) fight cloud
1FAF0..1FAF6 ; Emoji # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
1FAF7..1FAF8 ; Emoji # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
# Total elements: 1431
# Total elements: 1438
# ================================================
@ -640,6 +647,7 @@
1F6D1..1F6D2 ; Emoji_Presentation # E3.0 [2] (🛑..🛒) stop sign..shopping cart
1F6D5 ; Emoji_Presentation # E12.0 [1] (🛕) hindu temple
1F6D6..1F6D7 ; Emoji_Presentation # E13.0 [2] (🛖..🛗) hut..elevator
1F6D8 ; Emoji_Presentation # E17.0 [1] (🛘) landslide
1F6DC ; Emoji_Presentation # E15.0 [1] (🛜) wireless
1F6DD..1F6DF ; Emoji_Presentation # E14.0 [3] (🛝..🛟) playground slide..ring buoy
1F6EB..1F6EC ; Emoji_Presentation # E1.0 [2] (🛫..🛬) airplane departure..airplane arrival
@ -704,6 +712,8 @@
1FA83..1FA86 ; Emoji_Presentation # E13.0 [4] (🪃..🪆) boomerang..nesting dolls
1FA87..1FA88 ; Emoji_Presentation # E15.0 [2] (🪇..🪈) maracas..flute
1FA89 ; Emoji_Presentation # E16.0 [1] (🪉) harp
1FA8A ; Emoji_Presentation # E17.0 [1] (🪊) trombone
1FA8E ; Emoji_Presentation # E17.0 [1] (🪎) treasure chest
1FA8F ; Emoji_Presentation # E16.0 [1] (🪏) shovel
1FA90..1FA95 ; Emoji_Presentation # E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Emoji_Presentation # E13.0 [19] (🪖..🪨) military helmet..rock
@ -717,6 +727,8 @@
1FAC0..1FAC2 ; Emoji_Presentation # E13.0 [3] (🫀..🫂) anatomical heart..people hugging
1FAC3..1FAC5 ; Emoji_Presentation # E14.0 [3] (🫃..🫅) pregnant man..person with crown
1FAC6 ; Emoji_Presentation # E16.0 [1] (🫆) fingerprint
1FAC8 ; Emoji_Presentation # E17.0 [1] (🫈) hairy creature
1FACD ; Emoji_Presentation # E17.0 [1] (🫍) orca
1FACE..1FACF ; Emoji_Presentation # E15.0 [2] (🫎..🫏) moose..donkey
1FAD0..1FAD6 ; Emoji_Presentation # E13.0 [7] (🫐..🫖) blueberries..teapot
1FAD7..1FAD9 ; Emoji_Presentation # E14.0 [3] (🫗..🫙) pouring liquid..jar
@ -726,10 +738,12 @@
1FAE0..1FAE7 ; Emoji_Presentation # E14.0 [8] (🫠..🫧) melting face..bubbles
1FAE8 ; Emoji_Presentation # E15.0 [1] (🫨) shaking face
1FAE9 ; Emoji_Presentation # E16.0 [1] (🫩) face with bags under eyes
1FAEA ; Emoji_Presentation # E17.0 [1] (🫪) distorted face
1FAEF ; Emoji_Presentation # E17.0 [1] (🫯) fight cloud
1FAF0..1FAF6 ; Emoji_Presentation # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
1FAF7..1FAF8 ; Emoji_Presentation # E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
# Total elements: 1212
# Total elements: 1219
# ================================================
@ -827,7 +841,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
21A9..21AA ; Extended_Pictographic# E0.6 [2] (↩️..↪️) right arrow curving left..left arrow curving right
231A..231B ; Extended_Pictographic# E0.6 [2] (⌚..⌛) watch..hourglass done
2328 ; Extended_Pictographic# E1.0 [1] (⌨️) keyboard
2388 ; Extended_Pictographic# E0.0 [1] (⎈) HELM SYMBOL
23CF ; Extended_Pictographic# E1.0 [1] (⏏️) eject button
23E9..23EC ; Extended_Pictographic# E0.6 [4] (⏩..⏬) fast-forward button..fast down button
23ED..23EE ; Extended_Pictographic# E0.7 [2] (⏭️..⏮️) next track button..last track button
@ -844,106 +857,63 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
2600..2601 ; Extended_Pictographic# E0.6 [2] (☀️..☁️) sun..cloud
2602..2603 ; Extended_Pictographic# E0.7 [2] (☂️..☃️) umbrella..snowman
2604 ; Extended_Pictographic# E1.0 [1] (☄️) comet
2605 ; Extended_Pictographic# E0.0 [1] (★) BLACK STAR
2607..260D ; Extended_Pictographic# E0.0 [7] (☇..☍) LIGHTNING..OPPOSITION
260E ; Extended_Pictographic# E0.6 [1] (☎️) telephone
260F..2610 ; Extended_Pictographic# E0.0 [2] (☏..☐) WHITE TELEPHONE..BALLOT BOX
2611 ; Extended_Pictographic# E0.6 [1] (☑️) check box with check
2612 ; Extended_Pictographic# E0.0 [1] (☒) BALLOT BOX WITH X
2614..2615 ; Extended_Pictographic# E0.6 [2] (☔..☕) umbrella with rain drops..hot beverage
2616..2617 ; Extended_Pictographic# E0.0 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
2618 ; Extended_Pictographic# E1.0 [1] (☘️) shamrock
2619..261C ; Extended_Pictographic# E0.0 [4] (☙..☜) REVERSED ROTATED FLORAL HEART BULLET..WHITE LEFT POINTING INDEX
261D ; Extended_Pictographic# E0.6 [1] (☝️) index pointing up
261E..261F ; Extended_Pictographic# E0.0 [2] (☞..☟) WHITE RIGHT POINTING INDEX..WHITE DOWN POINTING INDEX
2620 ; Extended_Pictographic# E1.0 [1] (☠️) skull and crossbones
2621 ; Extended_Pictographic# E0.0 [1] (☡) CAUTION SIGN
2622..2623 ; Extended_Pictographic# E1.0 [2] (☢️..☣️) radioactive..biohazard
2624..2625 ; Extended_Pictographic# E0.0 [2] (☤..☥) CADUCEUS..ANKH
2626 ; Extended_Pictographic# E1.0 [1] (☦️) orthodox cross
2627..2629 ; Extended_Pictographic# E0.0 [3] (☧..☩) CHI RHO..CROSS OF JERUSALEM
262A ; Extended_Pictographic# E0.7 [1] (☪️) star and crescent
262B..262D ; Extended_Pictographic# E0.0 [3] (☫..☭) FARSI SYMBOL..HAMMER AND SICKLE
262E ; Extended_Pictographic# E1.0 [1] (☮️) peace symbol
262F ; Extended_Pictographic# E0.7 [1] (☯️) yin yang
2630..2637 ; Extended_Pictographic# E0.0 [8] (☰..☷) TRIGRAM FOR HEAVEN..TRIGRAM FOR EARTH
2638..2639 ; Extended_Pictographic# E0.7 [2] (☸️..☹️) wheel of dharma..frowning face
263A ; Extended_Pictographic# E0.6 [1] (☺️) smiling face
263B..263F ; Extended_Pictographic# E0.0 [5] (☻..☿) BLACK SMILING FACE..MERCURY
2640 ; Extended_Pictographic# E4.0 [1] (♀️) female sign
2641 ; Extended_Pictographic# E0.0 [1] (♁) EARTH
2642 ; Extended_Pictographic# E4.0 [1] (♂️) male sign
2643..2647 ; Extended_Pictographic# E0.0 [5] (♃..♇) JUPITER..PLUTO
2648..2653 ; Extended_Pictographic# E0.6 [12] (♈..♓) Aries..Pisces
2654..265E ; Extended_Pictographic# E0.0 [11] (♔..♞) WHITE CHESS KING..BLACK CHESS KNIGHT
265F ; Extended_Pictographic# E11.0 [1] (♟️) chess pawn
2660 ; Extended_Pictographic# E0.6 [1] (♠️) spade suit
2661..2662 ; Extended_Pictographic# E0.0 [2] (♡..♢) WHITE HEART SUIT..WHITE DIAMOND SUIT
2663 ; Extended_Pictographic# E0.6 [1] (♣️) club suit
2664 ; Extended_Pictographic# E0.0 [1] (♤) WHITE SPADE SUIT
2665..2666 ; Extended_Pictographic# E0.6 [2] (♥️..♦️) heart suit..diamond suit
2667 ; Extended_Pictographic# E0.0 [1] (♧) WHITE CLUB SUIT
2668 ; Extended_Pictographic# E0.6 [1] (♨️) hot springs
2669..267A ; Extended_Pictographic# E0.0 [18] (♩..♺) QUARTER NOTE..RECYCLING SYMBOL FOR GENERIC MATERIALS
267B ; Extended_Pictographic# E0.6 [1] (♻️) recycling symbol
267C..267D ; Extended_Pictographic# E0.0 [2] (♼..♽) RECYCLED PAPER SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
267E ; Extended_Pictographic# E11.0 [1] (♾️) infinity
267F ; Extended_Pictographic# E0.6 [1] (♿) wheelchair symbol
2680..2685 ; Extended_Pictographic# E0.0 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
2690..2691 ; Extended_Pictographic# E0.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
2692 ; Extended_Pictographic# E1.0 [1] (⚒️) hammer and pick
2693 ; Extended_Pictographic# E0.6 [1] (⚓) anchor
2694 ; Extended_Pictographic# E1.0 [1] (⚔️) crossed swords
2695 ; Extended_Pictographic# E4.0 [1] (⚕️) medical symbol
2696..2697 ; Extended_Pictographic# E1.0 [2] (⚖️..⚗️) balance scale..alembic
2698 ; Extended_Pictographic# E0.0 [1] (⚘) FLOWER
2699 ; Extended_Pictographic# E1.0 [1] (⚙️) gear
269A ; Extended_Pictographic# E0.0 [1] (⚚) STAFF OF HERMES
269B..269C ; Extended_Pictographic# E1.0 [2] (⚛️..⚜️) atom symbol..fleur-de-lis
269D..269F ; Extended_Pictographic# E0.0 [3] (⚝..⚟) OUTLINED WHITE STAR..THREE LINES CONVERGING LEFT
26A0..26A1 ; Extended_Pictographic# E0.6 [2] (⚠️..⚡) warning..high voltage
26A2..26A6 ; Extended_Pictographic# E0.0 [5] (⚢..⚦) DOUBLED FEMALE SIGN..MALE WITH STROKE SIGN
26A7 ; Extended_Pictographic# E13.0 [1] (⚧️) transgender symbol
26A8..26A9 ; Extended_Pictographic# E0.0 [2] (⚨..⚩) VERTICAL MALE WITH STROKE SIGN..HORIZONTAL MALE WITH STROKE SIGN
26AA..26AB ; Extended_Pictographic# E0.6 [2] (⚪..⚫) white circle..black circle
26AC..26AF ; Extended_Pictographic# E0.0 [4] (⚬..⚯) MEDIUM SMALL WHITE CIRCLE..UNMARRIED PARTNERSHIP SYMBOL
26B0..26B1 ; Extended_Pictographic# E1.0 [2] (⚰️..⚱️) coffin..funeral urn
26B2..26BC ; Extended_Pictographic# E0.0 [11] (⚲..⚼) NEUTER..SESQUIQUADRATE
26BD..26BE ; Extended_Pictographic# E0.6 [2] (⚽..⚾) soccer ball..baseball
26BF..26C3 ; Extended_Pictographic# E0.0 [5] (⚿..⛃) SQUARED KEY..BLACK DRAUGHTS KING
26C4..26C5 ; Extended_Pictographic# E0.6 [2] (⛄..⛅) snowman without snow..sun behind cloud
26C6..26C7 ; Extended_Pictographic# E0.0 [2] (⛆..⛇) RAIN..BLACK SNOWMAN
26C8 ; Extended_Pictographic# E0.7 [1] (⛈️) cloud with lightning and rain
26C9..26CD ; Extended_Pictographic# E0.0 [5] (⛉..⛍) TURNED WHITE SHOGI PIECE..DISABLED CAR
26CE ; Extended_Pictographic# E0.6 [1] (⛎) Ophiuchus
26CF ; Extended_Pictographic# E0.7 [1] (⛏️) pick
26D0 ; Extended_Pictographic# E0.0 [1] (⛐) CAR SLIDING
26D1 ; Extended_Pictographic# E0.7 [1] (⛑️) rescue worker’s helmet
26D2 ; Extended_Pictographic# E0.0 [1] (⛒) CIRCLED CROSSING LANES
26D3 ; Extended_Pictographic# E0.7 [1] (⛓️) chains
26D4 ; Extended_Pictographic# E0.6 [1] (⛔) no entry
26D5..26E8 ; Extended_Pictographic# E0.0 [20] (⛕..⛨) ALTERNATE ONE-WAY LEFT WAY TRAFFIC..BLACK CROSS ON SHIELD
26E9 ; Extended_Pictographic# E0.7 [1] (⛩️) shinto shrine
26EA ; Extended_Pictographic# E0.6 [1] (⛪) church
26EB..26EF ; Extended_Pictographic# E0.0 [5] (⛫..⛯) CASTLE..MAP SYMBOL FOR LIGHTHOUSE
26F0..26F1 ; Extended_Pictographic# E0.7 [2] (⛰️..⛱️) mountain..umbrella on ground
26F2..26F3 ; Extended_Pictographic# E0.6 [2] (⛲..⛳) fountain..flag in hole
26F4 ; Extended_Pictographic# E0.7 [1] (⛴️) ferry
26F5 ; Extended_Pictographic# E0.6 [1] (⛵) sailboat
26F6 ; Extended_Pictographic# E0.0 [1] (⛶) SQUARE FOUR CORNERS
26F7..26F9 ; Extended_Pictographic# E0.7 [3] (⛷️..⛹️) skier..person bouncing ball
26FA ; Extended_Pictographic# E0.6 [1] (⛺) tent
26FB..26FC ; Extended_Pictographic# E0.0 [2] (⛻..⛼) JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL
26FD ; Extended_Pictographic# E0.6 [1] (⛽) fuel pump
26FE..2701 ; Extended_Pictographic# E0.0 [4] (⛾..✁) CUP ON BLACK SQUARE..UPPER BLADE SCISSORS
2702 ; Extended_Pictographic# E0.6 [1] (✂️) scissors
2703..2704 ; Extended_Pictographic# E0.0 [2] (✃..✄) LOWER BLADE SCISSORS..WHITE SCISSORS
2705 ; Extended_Pictographic# E0.6 [1] (✅) check mark button
2708..270C ; Extended_Pictographic# E0.6 [5] (✈️..✌️) airplane..victory hand
270D ; Extended_Pictographic# E0.7 [1] (✍️) writing hand
270E ; Extended_Pictographic# E0.0 [1] (✎) LOWER RIGHT PENCIL
270F ; Extended_Pictographic# E0.6 [1] (✏️) pencil
2710..2711 ; Extended_Pictographic# E0.0 [2] (✐..✑) UPPER RIGHT PENCIL..WHITE NIB
2712 ; Extended_Pictographic# E0.6 [1] (✒️) black nib
2714 ; Extended_Pictographic# E0.6 [1] (✔️) check mark
2716 ; Extended_Pictographic# E0.6 [1] (✖️) multiply
@ -959,7 +929,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
2757 ; Extended_Pictographic# E0.6 [1] (❗) red exclamation mark
2763 ; Extended_Pictographic# E1.0 [1] (❣️) heart exclamation
2764 ; Extended_Pictographic# E0.6 [1] (❤️) red heart
2765..2767 ; Extended_Pictographic# E0.0 [3] (❥..❧) ROTATED HEAVY BLACK HEART BULLET..ROTATED FLORAL HEART BULLET
2795..2797 ; Extended_Pictographic# E0.6 [3] (➕..➗) plus..divide
27A1 ; Extended_Pictographic# E0.6 [1] (➡️) right arrow
27B0 ; Extended_Pictographic# E0.6 [1] (➰) curly loop
@ -973,19 +942,19 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
303D ; Extended_Pictographic# E0.6 [1] (〽️) part alternation mark
3297 ; Extended_Pictographic# E0.6 [1] (㊗️) Japanese “congratulations” button
3299 ; Extended_Pictographic# E0.6 [1] (㊙️) Japanese “secret” button
1F000..1F003 ; Extended_Pictographic# E0.0 [4] (🀀..🀃) MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND
1F004 ; Extended_Pictographic# E0.6 [1] (🀄) mahjong red dragon
1F005..1F0CE ; Extended_Pictographic# E0.0 [202] (🀅..🃎) MAHJONG TILE GREEN DRAGON..PLAYING CARD KING OF DIAMONDS
1F02C..1F02F ; Extended_Pictographic# E0.0 [4] (🀬..🀯) <reserved-1F02C>..<reserved-1F02F>
1F094..1F09F ; Extended_Pictographic# E0.0 [12] (🂔..🂟) <reserved-1F094>..<reserved-1F09F>
1F0AF..1F0B0 ; Extended_Pictographic# E0.0 [2] (🂯..🂰) <reserved-1F0AF>..<reserved-1F0B0>
1F0C0 ; Extended_Pictographic# E0.0 [1] (🃀) <reserved-1F0C0>
1F0CF ; Extended_Pictographic# E0.6 [1] (🃏) joker
1F0D0..1F0FF ; Extended_Pictographic# E0.0 [48] (🃐..🃿) <reserved-1F0D0>..<reserved-1F0FF>
1F10D..1F10F ; Extended_Pictographic# E0.0 [3] (🄍..🄏) CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH
1F12F ; Extended_Pictographic# E0.0 [1] (🄯) COPYLEFT SYMBOL
1F16C..1F16F ; Extended_Pictographic# E0.0 [4] (🅬..🅯) RAISED MR SIGN..CIRCLED HUMAN FIGURE
1F0D0 ; Extended_Pictographic# E0.0 [1] (🃐) <reserved-1F0D0>
1F0F6..1F0FF ; Extended_Pictographic# E0.0 [10] (🃶..🃿) <reserved-1F0F6>..<reserved-1F0FF>
1F170..1F171 ; Extended_Pictographic# E0.6 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
1F17E..1F17F ; Extended_Pictographic# E0.6 [2] (🅾️..🅿️) O button (blood type)..P button
1F18E ; Extended_Pictographic# E0.6 [1] (🆎) AB button (blood type)
1F191..1F19A ; Extended_Pictographic# E0.6 [10] (🆑..🆚) CL button..VS button
1F1AD..1F1E5 ; Extended_Pictographic# E0.0 [57] (🆭..🇥) MASK WORK SYMBOL..<reserved-1F1E5>
1F1AE..1F1E5 ; Extended_Pictographic# E0.0 [56] (🆮..🇥) <reserved-1F1AE>..<reserved-1F1E5>
1F201..1F202 ; Extended_Pictographic# E0.6 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
1F203..1F20F ; Extended_Pictographic# E0.0 [13] (🈃..🈏) <reserved-1F203>..<reserved-1F20F>
1F21A ; Extended_Pictographic# E0.6 [1] (🈚) Japanese “free of charge” button
@ -994,7 +963,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F23C..1F23F ; Extended_Pictographic# E0.0 [4] (🈼..🈿) <reserved-1F23C>..<reserved-1F23F>
1F249..1F24F ; Extended_Pictographic# E0.0 [7] (🉉..🉏) <reserved-1F249>..<reserved-1F24F>
1F250..1F251 ; Extended_Pictographic# E0.6 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
1F252..1F2FF ; Extended_Pictographic# E0.0 [174] (🉒..🋿) <reserved-1F252>..<reserved-1F2FF>
1F252..1F25F ; Extended_Pictographic# E0.0 [14] (🉒..🉟) <reserved-1F252>..<reserved-1F25F>
1F266..1F2FF ; Extended_Pictographic# E0.0 [154] (🉦..🋿) <reserved-1F266>..<reserved-1F2FF>
1F300..1F30C ; Extended_Pictographic# E0.6 [13] (🌀..🌌) cyclone..milky way
1F30D..1F30E ; Extended_Pictographic# E0.7 [2] (🌍..🌎) globe showing Europe-Africa..globe showing Americas
1F30F ; Extended_Pictographic# E0.6 [1] (🌏) globe showing Asia-Australia
@ -1010,7 +980,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F31D..1F31E ; Extended_Pictographic# E1.0 [2] (🌝..🌞) full moon face..sun with face
1F31F..1F320 ; Extended_Pictographic# E0.6 [2] (🌟..🌠) glowing star..shooting star
1F321 ; Extended_Pictographic# E0.7 [1] (🌡️) thermometer
1F322..1F323 ; Extended_Pictographic# E0.0 [2] (🌢..🌣) BLACK DROPLET..WHITE SUN
1F324..1F32C ; Extended_Pictographic# E0.7 [9] (🌤️..🌬️) sun behind small cloud..wind face
1F32D..1F32F ; Extended_Pictographic# E1.0 [3] (🌭..🌯) hot dog..burrito
1F330..1F331 ; Extended_Pictographic# E0.6 [2] (🌰..🌱) chestnut..seedling
@ -1026,11 +995,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F37D ; Extended_Pictographic# E0.7 [1] (🍽️) fork and knife with plate
1F37E..1F37F ; Extended_Pictographic# E1.0 [2] (🍾..🍿) bottle with popping cork..popcorn
1F380..1F393 ; Extended_Pictographic# E0.6 [20] (🎀..🎓) ribbon..graduation cap
1F394..1F395 ; Extended_Pictographic# E0.0 [2] (🎔..🎕) HEART WITH TIP ON THE LEFT..BOUQUET OF FLOWERS
1F396..1F397 ; Extended_Pictographic# E0.7 [2] (🎖️..🎗️) military medal..reminder ribbon
1F398 ; Extended_Pictographic# E0.0 [1] (🎘) MUSICAL KEYBOARD WITH JACKS
1F399..1F39B ; Extended_Pictographic# E0.7 [3] (🎙️..🎛️) studio microphone..control knobs
1F39C..1F39D ; Extended_Pictographic# E0.0 [2] (🎜..🎝) BEAMED ASCENDING MUSICAL NOTES..BEAMED DESCENDING MUSICAL NOTES
1F39E..1F39F ; Extended_Pictographic# E0.7 [2] (🎞️..🎟️) film frames..admission tickets
1F3A0..1F3C4 ; Extended_Pictographic# E0.6 [37] (🎠..🏄) carousel horse..person surfing
1F3C5 ; Extended_Pictographic# E1.0 [1] (🏅) sports medal
@ -1045,11 +1011,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F3E0..1F3E3 ; Extended_Pictographic# E0.6 [4] (🏠..🏣) house..Japanese post office
1F3E4 ; Extended_Pictographic# E1.0 [1] (🏤) post office
1F3E5..1F3F0 ; Extended_Pictographic# E0.6 [12] (🏥..🏰) hospital..castle
1F3F1..1F3F2 ; Extended_Pictographic# E0.0 [2] (🏱..🏲) WHITE PENNANT..BLACK PENNANT
1F3F3 ; Extended_Pictographic# E0.7 [1] (🏳️) white flag
1F3F4 ; Extended_Pictographic# E1.0 [1] (🏴) black flag
1F3F5 ; Extended_Pictographic# E0.7 [1] (🏵️) rosette
1F3F6 ; Extended_Pictographic# E0.0 [1] (🏶) BLACK ROSETTE
1F3F7 ; Extended_Pictographic# E0.7 [1] (🏷️) label
1F3F8..1F3FA ; Extended_Pictographic# E1.0 [3] (🏸..🏺) badminton..amphora
1F400..1F407 ; Extended_Pictographic# E1.0 [8] (🐀..🐇) rat..rabbit
@ -1086,7 +1050,6 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F4F8 ; Extended_Pictographic# E1.0 [1] (📸) camera with flash
1F4F9..1F4FC ; Extended_Pictographic# E0.6 [4] (📹..📼) video camera..videocassette
1F4FD ; Extended_Pictographic# E0.7 [1] (📽️) film projector
1F4FE ; Extended_Pictographic# E0.0 [1] (📾) PORTABLE STEREO
1F4FF..1F502 ; Extended_Pictographic# E1.0 [4] (📿..🔂) prayer beads..repeat single button
1F503 ; Extended_Pictographic# E0.6 [1] (🔃) clockwise vertical arrows
1F504..1F507 ; Extended_Pictographic# E1.0 [4] (🔄..🔇) counterclockwise arrows button..muted speaker
@ -1097,51 +1060,30 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F516..1F52B ; Extended_Pictographic# E0.6 [22] (🔖..🔫) bookmark..water pistol
1F52C..1F52D ; Extended_Pictographic# E1.0 [2] (🔬..🔭) microscope..telescope
1F52E..1F53D ; Extended_Pictographic# E0.6 [16] (🔮..🔽) crystal ball..downwards button
1F546..1F548 ; Extended_Pictographic# E0.0 [3] (🕆..🕈) WHITE LATIN CROSS..CELTIC CROSS
1F549..1F54A ; Extended_Pictographic# E0.7 [2] (🕉️..🕊️) om..dove
1F54B..1F54E ; Extended_Pictographic# E1.0 [4] (🕋..🕎) kaaba..menorah
1F54F ; Extended_Pictographic# E0.0 [1] (🕏) BOWL OF HYGIEIA
1F550..1F55B ; Extended_Pictographic# E0.6 [12] (🕐..🕛) one o’clock..twelve o’clock
1F55C..1F567 ; Extended_Pictographic# E0.7 [12] (🕜..🕧) one-thirty..twelve-thirty
1F568..1F56E ; Extended_Pictographic# E0.0 [7] (🕨..🕮) RIGHT SPEAKER..BOOK
1F56F..1F570 ; Extended_Pictographic# E0.7 [2] (🕯️..🕰️) candle..mantelpiece clock
1F571..1F572 ; Extended_Pictographic# E0.0 [2] (🕱..🕲) BLACK SKULL AND CROSSBONES..NO PIRACY
1F573..1F579 ; Extended_Pictographic# E0.7 [7] (🕳️..🕹️) hole..joystick
1F57A ; Extended_Pictographic# E3.0 [1] (🕺) man dancing
1F57B..1F586 ; Extended_Pictographic# E0.0 [12] (🕻..🖆) LEFT HAND TELEPHONE RECEIVER..PEN OVER STAMPED ENVELOPE
1F587 ; Extended_Pictographic# E0.7 [1] (🖇️) linked paperclips
1F588..1F589 ; Extended_Pictographic# E0.0 [2] (🖈..🖉) BLACK PUSHPIN..LOWER LEFT PENCIL
1F58A..1F58D ; Extended_Pictographic# E0.7 [4] (🖊️..🖍️) pen..crayon
1F58E..1F58F ; Extended_Pictographic# E0.0 [2] (🖎..🖏) LEFT WRITING HAND..TURNED OK HAND SIGN
1F590 ; Extended_Pictographic# E0.7 [1] (🖐️) hand with fingers splayed
1F591..1F594 ; Extended_Pictographic# E0.0 [4] (🖑..🖔) REVERSED RAISED HAND WITH FINGERS SPLAYED..REVERSED VICTORY HAND
1F595..1F596 ; Extended_Pictographic# E1.0 [2] (🖕..🖖) middle finger..vulcan salute
1F597..1F5A3 ; Extended_Pictographic# E0.0 [13] (🖗..🖣) WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX
1F5A4 ; Extended_Pictographic# E3.0 [1] (🖤) black heart
1F5A5 ; Extended_Pictographic# E0.7 [1] (🖥️) desktop computer
1F5A6..1F5A7 ; Extended_Pictographic# E0.0 [2] (🖦..🖧) KEYBOARD AND MOUSE..THREE NETWORKED COMPUTERS
1F5A8 ; Extended_Pictographic# E0.7 [1] (🖨️) printer
1F5A9..1F5B0 ; Extended_Pictographic# E0.0 [8] (🖩..🖰) POCKET CALCULATOR..TWO BUTTON MOUSE
1F5B1..1F5B2 ; Extended_Pictographic# E0.7 [2] (🖱️..🖲️) computer mouse..trackball
1F5B3..1F5BB ; Extended_Pictographic# E0.0 [9] (🖳..🖻) OLD PERSONAL COMPUTER..DOCUMENT WITH PICTURE
1F5BC ; Extended_Pictographic# E0.7 [1] (🖼️) framed picture
1F5BD..1F5C1 ; Extended_Pictographic# E0.0 [5] (🖽..🗁) FRAME WITH TILES..OPEN FOLDER
1F5C2..1F5C4 ; Extended_Pictographic# E0.7 [3] (🗂️..🗄️) card index dividers..file cabinet
1F5C5..1F5D0 ; Extended_Pictographic# E0.0 [12] (🗅..🗐) EMPTY NOTE..PAGES
1F5D1..1F5D3 ; Extended_Pictographic# E0.7 [3] (🗑️..🗓️) wastebasket..spiral calendar
1F5D4..1F5DB ; Extended_Pictographic# E0.0 [8] (🗔..🗛) DESKTOP WINDOW..DECREASE FONT SIZE SYMBOL
1F5DC..1F5DE ; Extended_Pictographic# E0.7 [3] (🗜️..🗞️) clamp..rolled-up newspaper
1F5DF..1F5E0 ; Extended_Pictographic# E0.0 [2] (🗟..🗠) PAGE WITH CIRCLED TEXT..STOCK CHART
1F5E1 ; Extended_Pictographic# E0.7 [1] (🗡️) dagger
1F5E2 ; Extended_Pictographic# E0.0 [1] (🗢) LIPS
1F5E3 ; Extended_Pictographic# E0.7 [1] (🗣️) speaking head
1F5E4..1F5E7 ; Extended_Pictographic# E0.0 [4] (🗤..🗧) THREE RAYS ABOVE..THREE RAYS RIGHT
1F5E8 ; Extended_Pictographic# E2.0 [1] (🗨️) left speech bubble
1F5E9..1F5EE ; Extended_Pictographic# E0.0 [6] (🗩..🗮) RIGHT SPEECH BUBBLE..LEFT ANGER BUBBLE
1F5EF ; Extended_Pictographic# E0.7 [1] (🗯️) right anger bubble
1F5F0..1F5F2 ; Extended_Pictographic# E0.0 [3] (🗰..🗲) MOOD BUBBLE..LIGHTNING MOOD
1F5F3 ; Extended_Pictographic# E0.7 [1] (🗳️) ballot box with ballot
1F5F4..1F5F9 ; Extended_Pictographic# E0.0 [6] (🗴..🗹) BALLOT SCRIPT X..BALLOT BOX WITH BOLD CHECK
1F5FA ; Extended_Pictographic# E0.7 [1] (🗺️) world map
1F5FB..1F5FF ; Extended_Pictographic# E0.6 [5] (🗻..🗿) mount fuji..moai
1F600 ; Extended_Pictographic# E1.0 [1] (😀) grinning face
@ -1210,26 +1152,22 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F6BF ; Extended_Pictographic# E1.0 [1] (🚿) shower
1F6C0 ; Extended_Pictographic# E0.6 [1] (🛀) person taking bath
1F6C1..1F6C5 ; Extended_Pictographic# E1.0 [5] (🛁..🛅) bathtub..left luggage
1F6C6..1F6CA ; Extended_Pictographic# E0.0 [5] (🛆..🛊) TRIANGLE WITH ROUNDED CORNERS..GIRLS SYMBOL
1F6CB ; Extended_Pictographic# E0.7 [1] (🛋️) couch and lamp
1F6CC ; Extended_Pictographic# E1.0 [1] (🛌) person in bed
1F6CD..1F6CF ; Extended_Pictographic# E0.7 [3] (🛍️..🛏️) shopping bags..bed
1F6D0 ; Extended_Pictographic# E1.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Extended_Pictographic# E3.0 [2] (🛑..🛒) stop sign..shopping cart
1F6D3..1F6D4 ; Extended_Pictographic# E0.0 [2] (🛓..🛔) STUPA..PAGODA
1F6D5 ; Extended_Pictographic# E12.0 [1] (🛕) hindu temple
1F6D6..1F6D7 ; Extended_Pictographic# E13.0 [2] (🛖..🛗) hut..elevator
1F6D8..1F6DB ; Extended_Pictographic# E0.0 [4] (🛘..🛛) <reserved-1F6D8>..<reserved-1F6DB>
1F6D8 ; Extended_Pictographic# E17.0 [1] (🛘) landslide
1F6D9..1F6DB ; Extended_Pictographic# E0.0 [3] (🛙..🛛) <reserved-1F6D9>..<reserved-1F6DB>
1F6DC ; Extended_Pictographic# E15.0 [1] (🛜) wireless
1F6DD..1F6DF ; Extended_Pictographic# E14.0 [3] (🛝..🛟) playground slide..ring buoy
1F6E0..1F6E5 ; Extended_Pictographic# E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E6..1F6E8 ; Extended_Pictographic# E0.0 [3] (🛦..🛨) UP-POINTING MILITARY AIRPLANE..UP-POINTING SMALL AIRPLANE
1F6E9 ; Extended_Pictographic# E0.7 [1] (🛩️) small airplane
1F6EA ; Extended_Pictographic# E0.0 [1] (🛪) NORTHEAST-POINTING AIRPLANE
1F6EB..1F6EC ; Extended_Pictographic# E1.0 [2] (🛫..🛬) airplane departure..airplane arrival
1F6ED..1F6EF ; Extended_Pictographic# E0.0 [3] (🛭..🛯) <reserved-1F6ED>..<reserved-1F6EF>
1F6F0 ; Extended_Pictographic# E0.7 [1] (🛰️) satellite
1F6F1..1F6F2 ; Extended_Pictographic# E0.0 [2] (🛱..🛲) ONCOMING FIRE ENGINE..DIESEL LOCOMOTIVE
1F6F3 ; Extended_Pictographic# E0.7 [1] (🛳️) passenger ship
1F6F4..1F6F6 ; Extended_Pictographic# E3.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Extended_Pictographic# E5.0 [2] (🛷..🛸) sled..flying saucer
@ -1237,8 +1175,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F6FA ; Extended_Pictographic# E12.0 [1] (🛺) auto rickshaw
1F6FB..1F6FC ; Extended_Pictographic# E13.0 [2] (🛻..🛼) pickup truck..roller skate
1F6FD..1F6FF ; Extended_Pictographic# E0.0 [3] (🛽..🛿) <reserved-1F6FD>..<reserved-1F6FF>
1F774..1F77F ; Extended_Pictographic# E0.0 [12] (🝴..🝿) LOT OF FORTUNE..ORCUS
1F7D5..1F7DF ; Extended_Pictographic# E0.0 [11] (🟕..🟟) CIRCLED TRIANGLE..<reserved-1F7DF>
1F7DA..1F7DF ; Extended_Pictographic# E0.0 [6] (🟚..🟟) <reserved-1F7DA>..<reserved-1F7DF>
1F7E0..1F7EB ; Extended_Pictographic# E12.0 [12] (🟠..🟫) orange circle..brown square
1F7EC..1F7EF ; Extended_Pictographic# E0.0 [4] (🟬..🟯) <reserved-1F7EC>..<reserved-1F7EF>
1F7F0 ; Extended_Pictographic# E14.0 [1] (🟰) heavy equals sign
@ -1247,7 +1184,10 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F848..1F84F ; Extended_Pictographic# E0.0 [8] (🡈..🡏) <reserved-1F848>..<reserved-1F84F>
1F85A..1F85F ; Extended_Pictographic# E0.0 [6] (🡚..🡟) <reserved-1F85A>..<reserved-1F85F>
1F888..1F88F ; Extended_Pictographic# E0.0 [8] (🢈..🢏) <reserved-1F888>..<reserved-1F88F>
1F8AE..1F8FF ; Extended_Pictographic# E0.0 [82] (🢮..🣿) <reserved-1F8AE>..<reserved-1F8FF>
1F8AE..1F8AF ; Extended_Pictographic# E0.0 [2] (🢮..🢯) <reserved-1F8AE>..<reserved-1F8AF>
1F8BC..1F8BF ; Extended_Pictographic# E0.0 [4] (🢼..🢿) <reserved-1F8BC>..<reserved-1F8BF>
1F8C2..1F8CF ; Extended_Pictographic# E0.0 [14] (🣂..🣏) <reserved-1F8C2>..<reserved-1F8CF>
1F8D9..1F8FF ; Extended_Pictographic# E0.0 [39] (🣙..🣿) <reserved-1F8D9>..<reserved-1F8FF>
1F90C ; Extended_Pictographic# E13.0 [1] (🤌) pinched fingers
1F90D..1F90F ; Extended_Pictographic# E12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Extended_Pictographic# E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
@ -1293,7 +1233,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F9CD..1F9CF ; Extended_Pictographic# E12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Extended_Pictographic# E5.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Extended_Pictographic# E11.0 [25] (🧧..🧿) red envelope..nazar amulet
1FA00..1FA6F ; Extended_Pictographic# E0.0 [112] (🨀..🩯) NEUTRAL CHESS KING..<reserved-1FA6F>
1FA58..1FA5F ; Extended_Pictographic# E0.0 [8] (🩘..🩟) <reserved-1FA58>..<reserved-1FA5F>
1FA6E..1FA6F ; Extended_Pictographic# E0.0 [2] (🩮..🩯) <reserved-1FA6E>..<reserved-1FA6F>
1FA70..1FA73 ; Extended_Pictographic# E12.0 [4] (🩰..🩳) ballet shoes..shorts
1FA74 ; Extended_Pictographic# E13.0 [1] (🩴) thong sandal
1FA75..1FA77 ; Extended_Pictographic# E15.0 [3] (🩵..🩷) light blue heart..pink heart
@ -1304,7 +1245,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1FA83..1FA86 ; Extended_Pictographic# E13.0 [4] (🪃..🪆) boomerang..nesting dolls
1FA87..1FA88 ; Extended_Pictographic# E15.0 [2] (🪇..🪈) maracas..flute
1FA89 ; Extended_Pictographic# E16.0 [1] (🪉) harp
1FA8A..1FA8E ; Extended_Pictographic# E0.0 [5] (🪊..🪎) <reserved-1FA8A>..<reserved-1FA8E>
1FA8A ; Extended_Pictographic# E17.0 [1] (🪊) trombone
1FA8B..1FA8D ; Extended_Pictographic# E0.0 [3] (🪋..🪍) <reserved-1FA8B>..<reserved-1FA8D>
1FA8E ; Extended_Pictographic# E17.0 [1] (🪎) treasure chest
1FA8F ; Extended_Pictographic# E16.0 [1] (🪏) shovel
1FA90..1FA95 ; Extended_Pictographic# E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Extended_Pictographic# E13.0 [19] (🪖..🪨) military helmet..rock
@ -1318,7 +1261,10 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1FAC0..1FAC2 ; Extended_Pictographic# E13.0 [3] (🫀..🫂) anatomical heart..people hugging
1FAC3..1FAC5 ; Extended_Pictographic# E14.0 [3] (🫃..🫅) pregnant man..person with crown
1FAC6 ; Extended_Pictographic# E16.0 [1] (🫆) fingerprint
1FAC7..1FACD ; Extended_Pictographic# E0.0 [7] (🫇..🫍) <reserved-1FAC7>..<reserved-1FACD>
1FAC7 ; Extended_Pictographic# E0.0 [1] (🫇) <reserved-1FAC7>
1FAC8 ; Extended_Pictographic# E17.0 [1] (🫈) hairy creature
1FAC9..1FACC ; Extended_Pictographic# E0.0 [4] (🫉..🫌) <reserved-1FAC9>..<reserved-1FACC>
1FACD ; Extended_Pictographic# E17.0 [1] (🫍) orca
1FACE..1FACF ; Extended_Pictographic# E15.0 [2] (🫎..🫏) moose..donkey
1FAD0..1FAD6 ; Extended_Pictographic# E13.0 [7] (🫐..🫖) blueberries..teapot
1FAD7..1FAD9 ; Extended_Pictographic# E14.0 [3] (🫗..🫙) pouring liquid..jar
@ -1329,12 +1275,14 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1FAE0..1FAE7 ; Extended_Pictographic# E14.0 [8] (🫠..🫧) melting face..bubbles
1FAE8 ; Extended_Pictographic# E15.0 [1] (🫨) shaking face
1FAE9 ; Extended_Pictographic# E16.0 [1] (🫩) face with bags under eyes
1FAEA..1FAEF ; Extended_Pictographic# E0.0 [6] (🫪..🫯) <reserved-1FAEA>..<reserved-1FAEF>
1FAEA ; Extended_Pictographic# E17.0 [1] (🫪) distorted face
1FAEB..1FAEE ; Extended_Pictographic# E0.0 [4] (🫫..🫮) <reserved-1FAEB>..<reserved-1FAEE>
1FAEF ; Extended_Pictographic# E17.0 [1] (🫯) fight cloud
1FAF0..1FAF6 ; Extended_Pictographic# E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
1FAF7..1FAF8 ; Extended_Pictographic# E15.0 [2] (🫷..🫸) leftwards pushing hand..rightwards pushing hand
1FAF9..1FAFF ; Extended_Pictographic# E0.0 [7] (🫹..🫿) <reserved-1FAF9>..<reserved-1FAFF>
1FC00..1FFFD ; Extended_Pictographic# E0.0[1022] (🰀..🿽) <reserved-1FC00>..<reserved-1FFFD>
# Total elements: 3537
# Total elements: 2848
#EOF

View file

@ -52,16 +52,14 @@ function __do_upload_to_testflight() {
local FASTLANE_ITC_PROVIDER="$1"
local FASTLANE_API_KEY_ID="$2"
local APP_IPA_DST
local BUILD_NUMBER
export FASTLANE_ITC_TEAM_ID="$3"
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
BUILD_NUMBER=$(cat ../VERSION.md)
APP_IPA_DST="$4-ios-${BUILD_NUMBER}.ipa"
APP_IPA_DST="$4-ios-${KEYMAN_VERSION_FOR_FILENAME}.ipa"
cd "upload/${BUILD_NUMBER}"
cd "upload/${KEYMAN_VERSION}"
command -v fastlane
# shellcheck disable=SC2154
fastlane pilot upload \

View file

@ -33,7 +33,7 @@ function make_source_tarball_action() {
if builder_is_ci_build_level_release; then
builder_echo start "make source tarball" "Make source tarball"
rm -rf dist
make tmpsources
PKG_CONFIG_PATH="${KEYMAN_ROOT}/core/build/arch/release/meson-private" "${KEYMAN_ROOT}/linux/scripts/dist.sh"
builder_echo end "make source tarball" success "Finished making source tarball"
else
builder_echo "Skipping source tarball creation - not release build level"

View file

@ -12,6 +12,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
# shellcheck disable=SC2154
. "${KEYMAN_ROOT}/resources/build/ci/trigger-definitions.inc.sh"
. "${KEYMAN_ROOT}/resources/build/ci/trigger-builds.inc.sh"
. "${KEYMAN_ROOT}/resources/build/ci/trigger-build-bot.inc.sh"
@ -29,15 +30,15 @@ if ! builder_has_option --branch; then
builder_die "--branch parameter is required"
fi
if [[ ! "$PRNUM" =~ ^([0-9]+|master|beta|stable-[0-9]+\.[0-9]+)$ ]]; then
builder_die "Invalid parameter --branch '$PRNUM'; expected 'master', 'beta', 'stable-x.y', or PR number"
if [[ ! "${PRNUM}" =~ ^([0-9]+|master|beta|stable-[0-9]+\.[0-9]+)$ ]]; then
builder_die "Invalid parameter --branch '${PRNUM}'; expected 'master', 'beta', 'stable-x.y', or PR number"
fi
#
# Debug logging
#
function debug_echo() {
if [ ! -z ${DEBUG:-} ]; then
if [[ ! -z ${DEBUG:-} ]]; then
echo "DEBUG: $1"
fi
true
@ -59,44 +60,45 @@ function triggerTestBuilds() {
# Cancel any already running builds for this branch
if builder_has_option --dry-run; then
builder_echo "DRY RUN: cancel current builds for $branch"
builder_echo "DRY RUN: cancel current builds for ${branch}"
else
node "$KEYMAN_ROOT/resources/build/ci/cancel-builds/cancel-test-builds.mjs" "$branch" "$TEAMCITY_TOKEN"
node "${KEYMAN_ROOT}/resources/build/ci/cancel-builds/cancel-test-builds.mjs" "${branch}" "${TEAMCITY_TOKEN}"
fi
for platform in "${!platforms[@]}"; do
local platformBuildLevel=${platforms[$platform]}
if [[ $platformBuildLevel == skip ]]; then
builder_echo heading "$platform: skipping build"
local platformBuildLevel=${platforms[${platform}]}
if [[ ${platformBuildLevel} == skip ]]; then
builder_echo heading "${platform}: skipping build"
continue
fi
builder_echo heading "$platform: checking for git changes"
eval test_builds='(${'bc_test_$platform'[@]})'
builder_echo heading "${platform}: checking for git changes"
eval test_builds='(${'bc_test_${platform}'[@]})'
# shellcheck disable=SC2154
for test_build in "${test_builds[@]}"; do
if [[ $test_build == "" ]]; then continue; fi
if [[ ${test_build} == "" ]]; then continue; fi
found_build=true
if [ "${test_build:(-7)}" == "_GitHub" ]; then
if [[ "${test_build:(-7)}" == "_GitHub" ]]; then
local job=${test_build%_GitHub}
if builder_has_option --dry-run; then
builder_echo "DRY RUN: Triggering GitHub action build $job/$branch, level = $platformBuildLevel"
builder_echo "DRY RUN: Triggering GitHub action build ${job}/${branch}, level = ${platformBuildLevel}"
else
builder_echo "Triggering GitHub action build $job/$branch, level = $platformBuildLevel"
triggerGitHubActionsBuild true $platformBuildLevel "$job" "$branch"
builder_echo "Triggering GitHub action build ${job}/${branch}, level = ${platformBuildLevel}"
triggerGitHubActionsBuild true "${platformBuildLevel}" "${job}" "${branch}" "${KEYMAN_API_CHECK_SKIP}"
fi
else
if builder_has_option --dry-run; then
builder_echo "DRY RUN: Triggering build configuration $test_build on teamcity for $branch, level = $platformBuildLevel"
builder_echo "DRY RUN: Triggering build configuration ${test_build} on teamcity for ${branch}, level = ${platformBuildLevel}"
else
builder_echo "Triggering build configuration $test_build on teamcity for $branch, level = $platformBuildLevel"
triggerTeamCityBuild true $platformBuildLevel "$test_build" "$vcs_test" "$branch"
builder_echo "Triggering build configuration ${test_build} on teamcity for ${branch}, level = ${platformBuildLevel}"
triggerTeamCityBuild true "${platformBuildLevel}" "${test_build}" "${vcs_test}" "${branch}"
fi
fi
done
done
if [[ $found_build == false ]]; then
if [[ ${found_build} == false ]]; then
postSkippedBuildsStatusResult
fi
}
@ -111,7 +113,7 @@ function postSkippedBuildsStatusResult() {
curl --silent --write-out '\n' \
--request POST \
--header "Accept: application/vnd.github+json" \
--header "Authorization: token $GITHUB_TOKEN" \
--header "Authorization: token ${GITHUB_TOKEN}" \
--data '{"state":"success","description":"Skipping since no platform builds necessary","context":"Test Build (Keyman)"}' \
"https://api.github.com/repos/keymanapp/keyman/statuses/${BUILD_VCS_NUMBER}"
fi
@ -122,10 +124,10 @@ function postSkippedBuildsStatusResult() {
# branches as well as pull requests
#
if [[ ! "$PRNUM" =~ ^[[:digit:]]+$ ]]; then
if [[ ! "${PRNUM}" =~ ^[[:digit:]]+$ ]]; then
# branch name is 'master', 'beta', or 'stable-x.y'
builder_echo "Branch $PRNUM needs to pass tests on all platforms."
triggerTestBuilds main_branch_platform_build_levels $PRNUM
builder_echo "Branch ${PRNUM} needs to pass tests on all platforms."
triggerTestBuilds main_branch_platform_build_levels "${PRNUM}"
exit 0
fi
@ -136,20 +138,20 @@ fi
# targeted by the PR other than to ask GitHub, anyway.)
#
builder_echo grey "# Get information about pull request #$PRNUM from GitHub"
prinfo=`curl -s -H "User-Agent: @keymanapp" -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/keymanapp/keyman/pulls/$PRNUM`
prbase=`echo ${prinfo} | "$JQ" -r '.base.ref'`
prhead=`echo ${prinfo} | "$JQ" -r '.head.ref'`
prbaserepo=`echo ${prinfo} | "$JQ" -r '.base.repo.html_url'`
prheadrepo=`echo ${prinfo} | "$JQ" -r '.head.repo.html_url'`
builder_echo grey "# Get information about pull request #${PRNUM} from GitHub"
prinfo=$(curl -s -H "User-Agent: @keymanapp" -H "Authorization: Bearer ${GITHUB_TOKEN}" "https://api.github.com/repos/keymanapp/keyman/pulls/${PRNUM}")
prbase=$(echo "${prinfo}" | "${JQ}" -r '.base.ref')
prhead=$(echo "${prinfo}" | "${JQ}" -r '.head.ref')
prbaserepo=$(echo "${prinfo}" | "${JQ}" -r '.base.repo.html_url')
prheadrepo=$(echo "${prinfo}" | "${JQ}" -r '.head.repo.html_url')
debug_echo "PRNUM: $PRNUM"
debug_echo "BASE: $prbase ($prbaserepo)"
debug_echo "HEAD: $prhead ($prheadrepo)"
debug_echo "PRNUM: ${PRNUM}"
debug_echo "BASE: ${prbase} (${prbaserepo})"
debug_echo "HEAD: ${prhead} (${prheadrepo})"
prremote=origin
if [ "$prbaserepo" != "https://github.com/keymanapp/keyman" ]; then
echo "Unknown base repository $prbaserepo."
if [[ "${prbaserepo}" != "https://github.com/keymanapp/keyman" ]]; then
echo "Unknown base repository ${prbaserepo}."
exit 1
fi
@ -160,7 +162,7 @@ fi
# (Ensure we are within the repository before running git calls)
builder_echo grey "# Ensure our local branch is up to date"
pushd "$KEYMAN_ROOT" > /dev/null
pushd "${KEYMAN_ROOT}" > /dev/null
git fetch origin > /dev/null
#
@ -169,10 +171,10 @@ git fetch origin > /dev/null
# on untrusted forks.
#
if [ "$prheadrepo" != "https://github.com/keymanapp/keyman" ]; then
if [[ "${prheadrepo}" != "https://github.com/keymanapp/keyman" ]]; then
prremote=external-rrtb
git remote add "$prremote" "$prheadrepo"
git fetch "$prremote" > /dev/null
git remote add "${prremote}" "${prheadrepo}"
git fetch "${prremote}" > /dev/null
fi
#
@ -181,18 +183,18 @@ fi
#
builder_echo grey "# Get list of changed files in the pull request"
prfiles=`git diff --name-only "origin/$prbase"..."$prremote/$prhead" || ( if [ $? == 128 ]; then echo abort; else exit $?; fi )`
if [ "$prfiles" == "abort" ]; then
prfiles=$(git diff --name-only "origin/${prbase}...${prremote}/${prhead}" || ( if [[ $? == 128 ]]; then echo abort; else exit $?; fi ))
if [[ "${prfiles}" == "abort" ]]; then
# Don't trigger any builds, exit with success
if [ "$prremote" != "origin" ]; then
git remote remove "$prremote"
if [[ "${prremote}" != "origin" ]]; then
git remote remove "${prremote}"
fi
echo "Remote branch origin/$prhead has gone away; probably an automatic pull request. Skipping build."
echo "Remote branch origin/${prhead} has gone away; probably an automatic pull request. Skipping build."
exit 0
fi
if [ "$prremote" != "origin" ]; then
git remote remove "$prremote"
if [[ "${prremote}" != "origin" ]]; then
git remote remove "${prremote}"
fi
debug_echo "Files found: ${prfiles[*]}"
@ -212,23 +214,23 @@ function find_platform_changes() {
while IFS= read -r line; do
# for each platform
for platform in "${available_platforms[@]}"; do
if [[ ! " ${!build_platforms[@]} " =~ " $platform " ]]; then
if [[ ! " ${!build_platforms[*]} " =~ " ${platform} " ]]; then
# Which platform are we watching?
eval watch='$'watch_$platform
eval watch="'$'watch_${platform}"
# Add common patterns to the watch list
watch="^(${platform}|(oem/[^/]+/${platform})|resources/((?!teamcity)|teamcity/(${platform}|includes))|${watch})"
watch="^(${platform}|(oem/[^/]+/${platform})|resources/((?!teamcity|docker-images)|teamcity/(${platform}|includes)|docker-images/(${platform}|base))|${watch})"
# Since bash doesn't support negative look-aheads we use perl to test
# (grep has a --perl-regexp option, but not the version on macOS)
if perl -e 'exit($ARGV[0] =~ /$ARGV[1]/ ? 0 : 1)' "${line}" "${watch}"; then
# By default, we'll build a 'release' test build for touched platforms
build_platforms[$platform]=$build_level_release
build_platforms[${platform}]=${build_level_release}
fi
fi
done
done <<< "$prfiles"
done <<< "${prfiles}"
builder_echo blue "Default build platforms: ${!build_platforms[@]}"
builder_echo blue "Default build platforms: ${!build_platforms[*]}"
}
find_platform_changes
@ -238,7 +240,9 @@ find_platform_changes
# This will modify the build_platforms array
#
if [ "$prremote" == "origin" ]; then
KEYMAN_API_CHECK_SKIP="false"
if [[ "${prremote}" == "origin" ]]; then
# We only accept Build-bot commands on trusted local origin PRs
cd "${KEYMAN_ROOT}/resources/build/ci/github"
npm ci
@ -249,9 +253,10 @@ if [ "$prremote" == "origin" ]; then
NODE_WRAPPER=node
fi
# Note: we may move all of this into javascript at some point!
prcommits=$(${NODE_WRAPPER} api-pull-commits.mjs $GITHUB_TOKEN $PRNUM)
test_bot_check_pr_body "$PRNUM" "$prinfo"
build_bot_check_messages $PRNUM "$prinfo" "$prcommits"
prcommits=$(${NODE_WRAPPER} api-pull-commits.mjs "${GITHUB_TOKEN}" "${PRNUM}")
test_bot_check_pr_body "${PRNUM}" "${prinfo}"
build_bot_check_messages "${PRNUM}" "${prinfo}" "${prcommits}"
KEYMAN_API_CHECK_SKIP="$(is_skip_keyman_api_check "${PRNUM}" "${prinfo}")"
fi
#
@ -260,7 +265,7 @@ fi
if [[ ! -z "${build_platforms[*]:-}" ]] && (( ${#build_platforms[@]} > 0)); then
builder_echo heading "Start test builds"
triggerTestBuilds build_platforms $PRNUM
triggerTestBuilds build_platforms "${PRNUM}"
else
builder_echo heading "No builds to start"
postSkippedBuildsStatusResult

View file

@ -20,7 +20,7 @@ computer browsers.
<!-- -->
[What's New](whats-new)
[What's New](whatsnew)
: What's new in Keyman Engine for Web 19.0
<!-- -->

View file

@ -1,3 +0,0 @@
---
title: What's New in KeymanWeb 19.0
---

View file

@ -0,0 +1,7 @@
---
title: What's New in KeymanWeb 19.0
---
## See also
* [Keyman Engine for Web Documentation](index)

View file

@ -797,6 +797,7 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
if (this.usesFixedWidthScaling) {
let baseWidth = this.width;
baseWidth -= this._borderWidth * 2;
baseWidth = (isNaN(baseWidth) || baseWidth < 0) ? 0 : baseWidth;
return ParsedLengthStyle.inPixels(baseWidth);
} else {
return ParsedLengthStyle.forScalar(1);
@ -821,7 +822,9 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
// Touch OSKs may apply internal padding to prevent row cropping at the edges.
// ... why not precompute both, rather than recalculate each time?
// - appears to contribute to layout reflow costs on layer swaps!
return ParsedLengthStyle.inPixels(this.layoutHeight.val - this._borderWidth * 2 - this.layerGroup.verticalPadding);
let baseHeight = this.layoutHeight.val - this._borderWidth * 2 - this.layerGroup.verticalPadding;
baseHeight = (isNaN(baseHeight) || baseHeight < 0) ? 0 : baseHeight;
return ParsedLengthStyle.inPixels(baseHeight);
} else {
return ParsedLengthStyle.forScalar(1);
}

View file

@ -555,7 +555,6 @@ export class ClassicalDistanceCalculation<
return trimmedInstance;
}
// Inputs add an extra row / first index entry.
// Inputs add an extra row / first index entry.
addInputChar(token: TUnit): this {
const returnBuffer = new ClassicalDistanceCalculation<TUnit, TOpSet>(this);
@ -583,7 +582,7 @@ export class ClassicalDistanceCalculation<
}
addMatchChar(token: TUnit): this {
let returnBuffer = new ClassicalDistanceCalculation<TUnit, TOpSet>(this);
const returnBuffer = new ClassicalDistanceCalculation<TUnit, TOpSet>(this);
returnBuffer._addMatchChar(token);
return returnBuffer as this;
}

View file

@ -0,0 +1,241 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { assert } from 'chai';
import sinon from 'sinon';
// @ts-ignore // Type info unavailable; doing a npm-install for it breaks things much worse in other ways.
import { JSDOM } from 'jsdom';
import { VisualKeyboard } from 'keyman/engine/osk';
import { ActiveLayout, Keyboard, KeyboardProperties } from 'keyman/engine/keyboard';
import { StylesheetManager } from 'keyman/engine/dom-utils';
import { DeviceSpec } from "@keymanapp/web-utils";
describe('VisualKeyboard', () => {
let dom: JSDOM;
let mockConfig: any;
beforeEach(() => {
dom = new JSDOM('<!DOCTYPE html><p>Hello world</p>');
global.document = dom.window.document;
global.getComputedStyle = dom.window.getComputedStyle;
const mockDevice = { browser: DeviceSpec.Browser.Chrome, formFactor: DeviceSpec.FormFactor.Desktop, OS: DeviceSpec.OperatingSystem.Windows, touchable: false };
const mockLayout = sinon.createStubInstance(ActiveLayout) as any;
const keyboard = sinon.createStubInstance(Keyboard) as any;
keyboard.layout.returns(mockLayout);
sinon.stub(keyboard, 'isRTL').get(() => false);
sinon.stub(keyboard, 'id').get(() => 'test');
sinon.stub(keyboard, 'name').get(() => 'Test Keyboard');
mockConfig = {
device: mockDevice,
hostDevice: mockDevice,
pathConfig: { fonts: '', resources: '' },
keyboard: keyboard,
keyboardMetadata: sinon.createStubInstance(KeyboardProperties),
topContainer: document.createElement('div'),
styleSheetManager: sinon.createStubInstance(StylesheetManager),
};
});
function createVisualKeyboard(fixedScaling: boolean = true): VisualKeyboard {
const vk = new VisualKeyboard(mockConfig);
vk.usesFixedHeightScaling = fixedScaling;
vk.usesFixedWidthScaling = fixedScaling;
vk['_borderWidth'] = 3;
return vk;
}
describe('LayoutHeight', () => {
it('calculates LayoutHeight correctly for number', () => {
const vk = createVisualKeyboard();
vk['_height'] = 100;
assert.equal(vk.layoutHeight.val, 94);
});
it('calculates LayoutHeight correctly for number string', () => {
const vk = createVisualKeyboard();
vk['_height'] = "100";
assert.equal(vk.layoutHeight.val, 94);
});
it('accepts small heights for LayoutHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = "5";
assert.equal(vk.layoutHeight.val, 0);
});
it('ignores non-number string for LayoutHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = "100px";
assert.equal(vk.layoutHeight.val, 0);
});
it('ignores invalid string for LayoutHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = "invalid";
assert.equal(vk.layoutHeight.val, 0);
});
it('ignores undefined value for LayoutHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = undefined;
assert.equal(vk.layoutHeight.val, 0);
});
it('ignores null value for LayoutHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = null;
assert.equal(vk.layoutHeight.val, 0);
});
it('calculates LayoutHeight correctly with fixed scaling', () => {
const vk = createVisualKeyboard(true);
vk['_height'] = 100;
assert.equal(vk.layoutHeight.val, 94);
});
it('calculates LayoutHeight correctly with fixed scaling and string', () => {
const vk = createVisualKeyboard(true);
vk['_height'] = "100px";
assert.equal(vk.layoutHeight.val, 0);
});
it('ignores invalid borderWidth for LayoutHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = 100;
vk['_borderWidth'] = "3px";
assert.equal(vk.layoutHeight.val, 0);
});
});
describe('LayoutWidth', () => {
it('calculates LayoutWidth correctly for number', () => {
const vk = createVisualKeyboard();
vk['_width'] = 100;
assert.equal(vk.layoutWidth.val, 94);
});
it('calculates LayoutWidth correctly for number string', () => {
const vk = createVisualKeyboard();
vk['_width'] = "100";
assert.equal(vk.layoutWidth.val, 94);
});
it('accepts small heights for LayoutWidth', () => {
const vk = createVisualKeyboard();
vk['_width'] = "5";
assert.equal(vk.layoutWidth.val, 0);
});
it('ignores non-number string for LayoutWidth', () => {
const vk = createVisualKeyboard();
vk['_width'] = "100px";
assert.equal(vk.layoutWidth.val, 0);
});
it('ignores invalid string for LayoutWidth', () => {
const vk = createVisualKeyboard();
vk['_width'] = "invalid";
assert.equal(vk.layoutWidth.val, 0);
});
it('ignores undefined value for LayoutWidth', () => {
const vk = createVisualKeyboard();
vk['_width'] = undefined;
assert.equal(vk.layoutWidth.val, 0);
});
it('ignores null value for LayoutWidth', () => {
const vk = createVisualKeyboard();
vk['_width'] = null;
assert.equal(vk.layoutWidth.val, 0);
});
it('calculates LayoutWidth correctly with fixed scaling', () => {
const vk = createVisualKeyboard(true);
vk['_width'] = 100;
assert.equal(vk.layoutWidth.val, 94);
});
it('calculates LayoutWidth correctly with fixed scaling and string', () => {
const vk = createVisualKeyboard(true);
vk['_width'] = "100px";
assert.equal(vk.layoutWidth.val, 0);
});
it('ignores invalid borderWidth for LayoutWidth', () => {
const vk = createVisualKeyboard();
vk['_width'] = 100;
vk['_borderWidth'] = "3px";
assert.equal(vk.layoutWidth.val, 0);
});
});
describe('InternalHeight', () => {
it('calculates InternalHeight correctly for number', () => {
const vk = createVisualKeyboard();
vk['_height'] = 100;
assert.equal(vk.internalHeight.val, 88);
});
it('calculates InternalHeight correctly for number string', () => {
const vk = createVisualKeyboard();
vk['_height'] = "100";
assert.equal(vk.internalHeight.val, 88);
});
it('accepts small heights for InternalHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = "5";
assert.equal(vk.internalHeight.val, 0);
});
it('ignores non-number string for InternalHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = "100px";
assert.equal(vk.internalHeight.val, 0);
});
it('ignores invalid string for InternalHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = "invalid";
assert.equal(vk.internalHeight.val, 0);
});
it('ignores undefined value for InternalHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = undefined;
assert.equal(vk.internalHeight.val, 0);
});
it('ignores null value for InternalHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = null;
assert.equal(vk.internalHeight.val, 0);
});
it('calculates InternalHeight correctly with fixed scaling', () => {
const vk = createVisualKeyboard(true);
vk['_height'] = 100;
assert.equal(vk.internalHeight.val, 88);
});
it('calculates InternalHeight correctly with fixed scaling and string', () => {
const vk = createVisualKeyboard(true);
vk['_height'] = "100px";
assert.equal(vk.internalHeight.val, 0);
});
it('ignores invalid borderWidth for InternalHeight', () => {
const vk = createVisualKeyboard();
vk['_height'] = 100;
vk['_borderWidth'] = "3px";
assert.equal(vk.internalHeight.val, 0);
});
});
});

View file

@ -188,8 +188,8 @@ describe('computeAlignment', () => {
canAlign: true,
editPath: [
{op: 'substitute', input: 0, match: 0},
{op: 'insert', match: 1},
{op: 'insert', match: 2}
{op: 'insert', match: 1},
{op: 'insert', match: 2}
],
leadTokenShift: 0,
leadEditLength: 0,
@ -347,7 +347,7 @@ describe('computeAlignment', () => {
assert.deepEqual(computedAlignment, {
canAlign: true,
editPath: [
{op: 'insert', match: 0},
{op: 'insert', match: 0},
{op: 'match', input: 0, match: 1},
{op: 'match', input: 1, match: 2},
{op: 'match', input: 2, match: 3},
@ -399,7 +399,7 @@ describe('computeAlignment', () => {
assert.deepEqual(computedAlignment, {
canAlign: true,
editPath: [
{op: 'insert', match: 0},
{op: 'insert', match: 0},
{op: 'substitute', input: 0, match: 1},
{op: 'match', input: 1, match: 2},
{op: 'match', input: 2, match: 3},
@ -430,7 +430,7 @@ describe('computeAlignment', () => {
{op: 'match', input: 2, match: 1},
{op: 'match', input: 3, match: 2},
{op: 'match', input: 4, match: 3},
{op: 'insert', match: 4}
{op: 'insert', match: 4}
],
leadTokenShift: -1,
leadEditLength: 0,
@ -483,7 +483,7 @@ describe('computeAlignment', () => {
{op: 'match', input: 2, match: 2},
{op: 'match', input: 3, match: 3},
{op: 'substitute', input: 4, match: 4},
{op: 'insert', match: 5}
{op: 'insert', match: 5}
],
leadTokenShift: 0,
leadEditLength: 1,
@ -505,7 +505,7 @@ describe('computeAlignment', () => {
assert.deepEqual(computedAlignment, {
canAlign: true,
editPath: [
{op: 'insert', match: 0},
{op: 'insert', match: 0},
{op: 'match', input: 0, match: 1},
{op: 'match', input: 1, match: 2},
{op: 'match', input: 2, match: 3},
@ -532,7 +532,7 @@ describe('computeAlignment', () => {
assert.deepEqual(computedAlignment, {
canAlign: true,
editPath: [
{op: 'insert', match: 0},
{op: 'insert', match: 0},
{op: 'match', input: 0, match: 1},
{op: 'match', input: 1, match: 2},
{op: 'match', input: 2, match: 3},
@ -559,7 +559,7 @@ describe('computeAlignment', () => {
assert.deepEqual(computedAlignment, {
canAlign: true,
editPath: [
{op: 'insert', match: 0},
{op: 'insert', match: 0},
{op: 'match', input: 0, match: 1},
{op: 'match', input: 1, match: 2},
{op: 'match', input: 2, match: 3},
@ -608,7 +608,7 @@ describe('computeAlignment', () => {
canAlign: false,
editPath: [
{op: 'match', input: 0, match: 0},
{op: 'insert', match: 1},
{op: 'insert', match: 1},
{op: 'match', input: 1, match: 2},
{op: 'match', input: 2, match: 3},
{op: 'match', input: 3, match: 4}
@ -653,7 +653,7 @@ describe('computeAlignment', () => {
{op: 'match', input: 1, match: 1},
{op: 'match', input: 2, match: 2},
{op: 'match', input: 3, match: 3},
{op: 'insert', match: 4},
{op: 'insert', match: 4},
{op: 'match', input: 4, match: 5}
]
});
@ -715,8 +715,8 @@ describe('computeAlignment', () => {
{op: 'match', input: 8, match: 8},
{op: 'match', input: 9, match: 9},
{op: 'substitute', input: 10, match: 10},
{op: 'insert', match: 11},
{op: 'insert', match: 12}
{op: 'insert', match: 11},
{op: 'insert', match: 12}
],
leadTokenShift: 0,
leadEditLength: 0,

View file

@ -138,8 +138,8 @@ describe('ContextTokenization', function() {
{op: 'match', input: 4, match: 4},
{op: 'match', input: 5, match: 5},
{op: 'match', input: 6, match: 6},
{op: 'insert', match: 7},
{op: 'insert', match: 8}
{op: 'insert', match: 7},
{op: 'insert', match: 8}
],
leadTokenShift: 0,
leadEditLength: 0,
@ -436,8 +436,8 @@ describe('ContextTokenization', function() {
targetTokens, {
canAlign: true,
editPath: [
{op: 'insert', match: 0},
{op: 'insert', match: 1},
{op: 'insert', match: 0},
{op: 'insert', match: 1},
{op: 'match', input: 0, match: 2},
{op: 'match', input: 1, match: 3},
{op: 'match', input: 2, match: 4},
@ -614,8 +614,8 @@ describe('ContextTokenization', function() {
{op: 'match', input: 10, match: 9},
{op: 'match', input: 11, match: 10},
{op: 'match', input: 12, match: 11},
{op: 'insert', match: 12},
{op: 'insert', match: 13}
{op: 'insert', match: 12},
{op: 'insert', match: 13}
],
leadTokenShift: 1, // "'",
leadEditLength: 1, // "n't" / "t"

View file

@ -592,7 +592,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() {
{ op: 'match', input: 1 /* 'c' */, match: 1 /* 'c' */},
{ op: 'match', input: 2 /* 'c' */, match: 2 /* 'c' */},
{ op: 'match', input: 3 /* 'o' */, match: 3 /* 'o' */},
{ op: 'insert', match: 4 /* 'm' */}, // insert is earlier
{ op: 'insert', match: 4 /* 'm' */}, // insert is earlier
{ op: 'match', input: 4 /* 'm' */, match: 5 /* 'm' */}, // longer contiguous match sequence
{ op: 'match', input: 5 /* 'o' */, match: 6 /* 'o' */},
{ op: 'match', input: 6 /* 'd' */, match: 7 /* 'd' */},
@ -605,7 +605,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() {
{ op: 'match', input: 2 /* 'c' */, match: 2 /* 'c' */},
{ op: 'match', input: 3 /* 'o' */, match: 3 /* 'o' */},
{ op: 'match', input: 4 /* 'm' */, match: 4 /* 'm' */},
{ op: 'insert', match: 5 /* 'm' */}, // now inserting later.
{ op: 'insert', match: 5 /* 'm' */}, // now inserting later.
{ op: 'match', input: 5 /* 'o' */, match: 6 /* 'o' */},
{ op: 'match', input: 6 /* 'd' */, match: 7 /* 'd' */},
{ op: 'match', input: 7 /* 'a' */, match: 8 /* 'a' */},
@ -635,7 +635,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() {
{ op: 'delete', input: 3},
{ op: 'match', input: 4, match: 3},
{ op: 'match', input: 5, match: 4},
{ op: 'insert', match: 5}
{ op: 'insert', match: 5}
];
const viablePaths = buffer.editPath();
@ -687,14 +687,14 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() {
{ op: 'match', input: 1, match: 1}, // best sequence: continguous matches
{ op: 'match', input: 2, match: 2}, // + adjacent substitutes
{ op: 'substitute', input: 3, match: 3},
{ op: 'insert', match: 4},
{ op: 'insert', match: 4},
];
let altSequence: EditTuple<string>[] = [
{ op: 'match', input: 0, match: 0},
{ op: 'match', input: 1, match: 1},
{ op: 'match', input: 2, match: 2},
{ op: 'insert', match: 3},
{ op: 'insert', match: 3},
{ op: 'substitute', input: 3, match: 4},
];
@ -717,7 +717,7 @@ describe('Classical Damerau-Levenshtein edit-distance calculation', function() {
{ op: 'match', input: 5, match: 3},
{ op: 'match', input: 6, match: 4},
{ op: 'transpose-start', input: 7, match: 5},
{ op: 'transpose-insert', match: 6},
{ op: 'transpose-insert', match: 6},
{ op: 'transpose-end', input: 8, match: 7}
];

View file

@ -147,7 +147,6 @@ describe('Split/merge aware edit-distance calculation', () => {
assert.equal(calc.getFinalCost(), 4);
const editPaths = calc.editPath();
assert.isAbove(editPaths.length, 3);
assert.sameDeepOrderedMembers(editPaths[0], [
{ op: 'split', input: 0, match: 0 },
{ op: 'split', input: 0, match: 1 },
@ -156,6 +155,26 @@ describe('Split/merge aware edit-distance calculation', () => {
{ op: 'merge', input: 3, match: 4 },
{ op: 'merge', input: 4, match: 4 }
]);
// ('insert', 'substitute') can be reordered
// ('substitute', 'delete') can be reordered
// There also exist cases where one pair or the other, but not both,
// are considered for the edit path.
assert.includeDeepMembers(editPaths, [[
{ op: 'insert', match: 0 }, // -> 'b'
{ op: 'substitute', input: 0, match: 1 }, // 'abc' -> 'c'
{ op: 'match', input: 1, match: 2 }, // 'd' == 'd'
{ op: 'match', input: 2, match: 3 }, // 'e' == 'e'
{ op: 'substitute', input: 3, match: 4 }, // 'f' -> 'fgh'
{ op: 'delete', input: 4 } // 'g' ->
]]);
// both split and merge: 1 variation
// split, but not merge: 2 variations
// merge, but not split: 2 variations
// neither: 2x2 = 4 variations
// sum: 9
assert.equal(editPaths.length, 9);
});
it("abcd,e,f,g -> b,c,d,efgh = 4", () => {
@ -180,10 +199,10 @@ describe('Split/merge aware edit-distance calculation', () => {
]);
assert.sameDeepOrderedMembers(editPaths[1], [
{ op: 'substitute', input: 0, match: 0 },
{ op: 'substitute', input: 1, match: 1 },
{ op: 'substitute', input: 2, match: 2 },
{ op: 'substitute', input: 3, match: 3 }
{ op: 'substitute', input: 0, match: 0 }, // 'abcd' -> 'b'
{ op: 'substitute', input: 1, match: 1 }, // 'e' -> 'c'
{ op: 'substitute', input: 2, match: 2 }, // 'f' -> 'd'
{ op: 'substitute', input: 3, match: 3 } // 'g' -> 'efgh'
]);
});
});

View file

@ -3,5 +3,5 @@ title: Does Keyman support Unicode?
---
Yes, since Keyman 5, Keyman fully supports the Unicode Standard, up to
and including Unicode 13.0. We also update Keyman with each new release
and including Unicode 17.0. We also update Keyman with each new release
of the Unicode Standard.

View file

@ -15,7 +15,7 @@ builder_describe "Installation files for Keyman for Windows" \
# after all other builds complete
builder_if_release_build_level builder_describe_outputs \
publish /windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION}.exe
publish /windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION_FOR_FILENAME}.exe
builder_parse "$@"
@ -104,7 +104,7 @@ function copy-installer() {
mkdir -p "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}"
cp keymandesktop.msi "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keymandesktop.msi"
cp keymandesktop.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION}.exe"
cp keymandesktop.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION_FOR_FILENAME}.exe"
cp "$WINDOWS_PROGRAM_APP/setup.exe" "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/setup.exe"
verify-installer-signatures

View file

@ -88,14 +88,19 @@ type
procedure HandleFirstRun;
function CurrentStateName: string;
(**
* Checks if Keyman is the WaitingRestartState and that
* Keyman has not run in this Windows session.
* The sole purpose is for the calling code then produce
* a UI to confirm the user wants to continue install.
* Checks if Keyman is the WaitingRestartState, the cache is valid
* and that Keyman has not run in this Windows session. It also confirms
* that automatic updates are still enabled #14295.
* The purpose is for the calling code can choose to produce
* a UI to confirm the user wants to install an update,
* if everything is ready to start a update.
*
* NOTE: If it finds the cache is invalid it will abort causing
* the State Machine to change state, returning to idle.
*
* @returns True if the Keyman is ready to install.
*)
function ReadyToInstall: Boolean;
function ValidateReadyToInstall: Boolean;
function IsInstallingState: Boolean;
function GetAutomaticUpdates: Boolean;
@ -558,14 +563,27 @@ begin
Result := CurrentState.ClassName;
end;
function TUpdateStateMachine.ReadyToInstall: Boolean;
function TUpdateStateMachine.ValidateReadyToInstall: Boolean;
begin
if not IsCurrentStateAssigned then
Exit(False);
if (CurrentState is WaitingRestartState) and not HasKeymanRun then
Result := True
else
Result := False;
if not (CurrentState is WaitingRestartState) then
Exit(False);
// Verify conditions since entering WaitingRestartState:
// If the cache is not valid or automatic updates have
// been disabled clear the cache and return to the idle state.
if not TUpdateCheckStorage.CheckMetaDataForUpdate or not Self.GetAutomaticUpdates then
begin
HandleAbort;
Exit(False);
end;
if not HasKeymanRun then
Exit(True);
Result := False;
end;
function TUpdateStateMachine.IsInstallingState: Boolean;
@ -955,7 +973,7 @@ begin
// Return to Idle state and check for Updates state
ChangeState(IdleState);
bucStateContext.CurrentState.HandleCheck;
Result := kmShellExit;
Result := kmShellContinue;
end
else
begin

View file

@ -668,20 +668,11 @@ function ShouldSendToBUpdateSM(FSilent: Boolean; BUpdateSM: TUpdateStateMachine;
// UI elements from the state machine we have bring some of logic here.
var
frmStartInstall: TfrmStartInstall;
ReadyToInstall: Boolean;
ValidateReadyToInstall: Boolean;
begin
ReadyToInstall := BUpdateSM.ReadyToInstall;
// The setting for automatic updates may have been set to disable
// after updates were checked and downloaded. The user should
// not be prompted for an update in this case and the update should
// abort.
if not BUpdateSM.GetAutomaticUpdates and ReadyToInstall then
begin
BUpdateSM.HandleAbort;
Exit(False);
end;
ValidateReadyToInstall := BUpdateSM.ValidateReadyToInstall;
if not FSilent and ReadyToInstall and
if not FSilent and ValidateReadyToInstall and
(FMode in [fmStart, fmSplash, fmMain, fmAbout,
fmHelp, fmShowHelp, fmSettings, fmBoot]) then
begin
@ -692,7 +683,7 @@ begin
frmStartInstall.Free;
end;
end
else if FSilent and ReadyToInstall then
else if FSilent and ValidateReadyToInstall then
Result := False
else
Result := True;