// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Script tag (ISO 15924), stored as a Tag.
pub struct Script {
tag : Tag
} derive(Eq, Show, ToJson)
///|
fn canonicalize_script_tag(tag : Tag) -> Tag {
let (b1, b2, b3, b4) = tag.bytes()
let u1 = b1.to_uint()
let u2 = b2.to_uint()
let u3 = b3.to_uint()
let u4 = b4.to_uint()
Tag::from_bytes(
(u1 & 0xdfU).to_byte(),
((u2 & 0xdfU) | 0x20U).to_byte(),
((u3 & 0xdfU) | 0x20U).to_byte(),
((u4 & 0xdfU) | 0x20U).to_byte(),
)
}
///|
fn byte_is_ascii_upper(b : Byte) -> Bool {
let v = b.to_int()
v >= 0x41 && v <= 0x5a
}
///|
fn byte_is_ascii_lower(b : Byte) -> Bool {
let v = b.to_int()
v >= 0x61 && v <= 0x7a
}
///|
fn looks_like_script_tag(tag : Tag) -> Bool {
let (b1, b2, b3, b4) = tag.bytes()
byte_is_ascii_upper(b1) &&
byte_is_ascii_lower(b2) &&
byte_is_ascii_lower(b3) &&
byte_is_ascii_lower(b4)
}
///|
/// Convert an ISO 15924 tag to a script.
pub fn Script::from_iso15924_tag(tag : Tag) -> Script {
if tag == tag_none {
return script_invalid
}
let canonical = canonicalize_script_tag(tag)
if canonical == Tag::from_chars('Q', 'a', 'a', 'i') {
return script_inherited
}
if canonical == Tag::from_chars('Q', 'a', 'a', 'c') {
return script_coptic
}
if canonical == Tag::from_chars('A', 'r', 'a', 'n') {
return script_arabic
}
if canonical == Tag::from_chars('C', 'y', 'r', 's') {
return script_cyrillic
}
if canonical == Tag::from_chars('G', 'e', 'o', 'k') {
return script_georgian
}
if canonical == Tag::from_chars('H', 'a', 'n', 's') {
return script_han
}
if canonical == Tag::from_chars('H', 'a', 'n', 't') {
return script_han
}
if canonical == Tag::from_chars('J', 'a', 'm', 'o') {
return script_hangul
}
if canonical == Tag::from_chars('L', 'a', 't', 'f') {
return script_latin
}
if canonical == Tag::from_chars('L', 'a', 't', 'g') {
return script_latin
}
if canonical == Tag::from_chars('S', 'y', 'r', 'e') {
return script_syriac
}
if canonical == Tag::from_chars('S', 'y', 'r', 'j') {
return script_syriac
}
if canonical == Tag::from_chars('S', 'y', 'r', 'n') {
return script_syriac
}
if looks_like_script_tag(canonical) {
Script::{ tag: canonical }
} else {
script_unknown
}
}
///|
/// Convert an ISO 15924 string to a script.
pub fn Script::from_string(s : String, len? : Int = -1) -> Script {
Script::from_iso15924_tag(Tag::from_string(s, len~))
}
///|
/// Convert a script to its ISO 15924 tag.
pub fn Script::to_iso15924_tag(self : Script) -> Tag {
self.tag
}
///|
fn is_rtl_tag(tag : Tag) -> Bool {
tag == script_arabic.tag ||
tag == script_hebrew.tag ||
tag == script_syriac.tag ||
tag == script_thaana.tag ||
tag == script_cypriot.tag ||
tag == script_kharoshthi.tag ||
tag == script_phoenician.tag ||
tag == script_nko.tag ||
tag == script_lydian.tag ||
tag == script_avestan.tag ||
tag == script_imperial_aramaic.tag ||
tag == script_inscriptional_pahlavi.tag ||
tag == script_inscriptional_parthian.tag ||
tag == script_old_south_arabian.tag ||
tag == script_old_turkic.tag ||
tag == script_samaritan.tag ||
tag == script_mandaic.tag ||
tag == script_meroitic_cursive.tag ||
tag == script_meroitic_hieroglyphs.tag ||
tag == script_manichaean.tag ||
tag == script_mende_kikakui.tag ||
tag == script_nabataean.tag ||
tag == script_old_north_arabian.tag ||
tag == script_palmyrene.tag ||
tag == script_psalter_pahlavi.tag ||
tag == script_hatran.tag ||
tag == script_adlam.tag ||
tag == script_hanifi_rohingya.tag ||
tag == script_old_sogdian.tag ||
tag == script_sogdian.tag ||
tag == script_elymaic.tag ||
tag == script_chorasmian.tag ||
tag == script_yezidi.tag ||
tag == script_old_uyghur.tag ||
tag == script_garay.tag ||
tag == script_sidetic.tag
}
///|
fn is_horizontal_ambiguous_tag(tag : Tag) -> Bool {
tag == script_old_hungarian.tag ||
tag == script_old_italic.tag ||
tag == script_runic.tag ||
tag == script_tifinagh.tag
}
///|
/// Fetch the horizontal direction for the script.
pub fn Script::horizontal_direction(self : Script) -> Direction {
let tag = self.tag
if is_rtl_tag(tag) {
direction_rtl
} else if is_horizontal_ambiguous_tag(tag) {
direction_invalid
} else {
direction_ltr
}
}
///|
/// Access the underlying tag.
pub fn Script::to_tag(self : Script) -> Tag {
self.tag
}