///|
priv struct PdfUnicodeDataLookup {
code_value : String
general_category : String
character_name : String
}
///|
let pdf_unicode_data_cache : Ref[Array[@unicodedata.PdfUnicodeDataEntry]?] = {
val: None,
}
///|
let pdf_unicode_data_lookup_cache : Ref[
@hashmap.HashMap[Int, PdfUnicodeDataLookup]?,
] = { val: None, }
///|
fn pdf_unicode_data_field(
source : BytesView,
start : Int,
end : Int,
) -> String raise @core.PdfError {
@ascii.decode(source[start:end]) catch {
_ => raise SoftError("malformed UnicodeData.txt row")
}
}
///|
fn pdf_unicode_data_parse_row(
source : BytesView,
start : Int,
end : Int,
) -> @unicodedata.PdfUnicodeDataEntry raise @core.PdfError {
let fields : Array[String] = []
let mut field_start = start
let mut index = start
while index < end {
if source[index].to_int() == 59 {
fields.push(pdf_unicode_data_field(source, field_start, index))
field_start = index + 1
}
index += 1
}
fields.push(pdf_unicode_data_field(source, field_start, end))
guard fields.length() == 15 else {
raise SoftError("malformed UnicodeData.txt row")
}
{
code_value: fields[0],
character_name: fields[1],
general_category: fields[2],
canonical_combining_classes: fields[3],
bidirectional_category: fields[4],
character_decomposition_mapping: fields[5],
decimal_digit_value: fields[6],
digit_value: fields[7],
numeric_value: fields[8],
mirrored: fields[9],
unicode_10_name: fields[10],
iso_10646_comment_field: fields[11],
uppercase_mapping: fields[12],
lowercase_mapping: fields[13],
titlecase_mapping: fields[14],
}
}
///|
/// Parse UnicodeData.txt bytes into cpdf-compatible rows.
pub fn pdf_parse_unicode_data(
source : BytesView,
) -> Array[@unicodedata.PdfUnicodeDataEntry] raise @core.PdfError {
let entries : Array[@unicodedata.PdfUnicodeDataEntry] = []
let mut index = 0
while index < source.length() {
let row_start = index
while index < source.length() && source[index].to_int() != 10 {
index += 1
}
let mut row_end = index
if row_end > row_start && source[row_end - 1].to_int() == 13 {
row_end -= 1
}
entries.push(pdf_unicode_data_parse_row(source, row_start, row_end))
if index < source.length() {
index += 1
}
}
entries
}
///|
/// Decode cpdf's flate-compressed UnicodeData source bytes, then parse rows.
pub fn pdf_parse_flate_unicode_data(
source : BytesView,
) -> Array[@unicodedata.PdfUnicodeDataEntry] raise @core.PdfError {
pdf_parse_unicode_data(@flate.pdf_flate_decode_view(source))
}
///|
/// Return cpdf's parsed UnicodeData table, decoded and parsed on demand.
pub fn pdf_unicode_data() -> Array[@unicodedata.PdfUnicodeDataEntry] raise @core.PdfError {
match pdf_unicode_data_cache.val {
Some(entries) => entries
None => {
let entries = pdf_parse_flate_unicode_data(pdf_unicode_data_source())
pdf_unicode_data_cache.val = Some(entries)
entries
}
}
}
///|
/// Source-spelled compatibility wrapper for cpdfunicodedata's `unicodedata`.
pub fn pdf_unicodedata() -> Array[@unicodedata.PdfUnicodeDataEntry] raise @core.PdfError {
pdf_unicode_data()
}
///|
fn pdf_unicode_data_hex_digit(value : Byte) -> Int? {
match value.to_int() {
digit if digit >= 0x30 && digit <= 0x39 => Some(digit - 0x30)
upper if upper >= 0x41 && upper <= 0x46 => Some(upper - 0x41 + 10)
lower if lower >= 0x61 && lower <= 0x66 => Some(lower - 0x61 + 10)
_ => None
}
}
///|
fn pdf_unicode_data_codepoint(text : String) -> Int? {
let bytes = @ascii.encode(text)
guard bytes.length() > 0 else { return None }
let mut value = 0
for byte in bytes {
match pdf_unicode_data_hex_digit(byte) {
Some(digit) => value = value * 16 + digit
None => return None
}
}
Some(value)
}
///|
fn pdf_unicode_data_lookup_table() -> @hashmap.HashMap[
Int,
PdfUnicodeDataLookup,
] raise @core.PdfError {
match pdf_unicode_data_lookup_cache.val {
Some(table) => table
None => {
let table : @hashmap.HashMap[Int, PdfUnicodeDataLookup] = HashMap([])
for entry in pdf_unicode_data() {
match pdf_unicode_data_codepoint(entry.code_value) {
Some(codepoint) =>
table[codepoint] = {
code_value: entry.code_value,
general_category: entry.general_category,
character_name: entry.character_name,
}
None => raise SoftError("malformed UnicodeData code value")
}
}
pdf_unicode_data_lookup_cache.val = Some(table)
table
}
}
}
///|
fn pdf_unicode_data_lookup(
codepoint : Int,
) -> PdfUnicodeDataLookup? raise @core.PdfError {
pdf_unicode_data_lookup_table().get(codepoint)
}