// 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.
///|
pub struct GvarTable {
axis_count : Int
shared_tuple_count : Int
shared_tuples_offset : Int
glyph_count : Int
flags : Int
data_offset : Int
offsets : Array[Int]
data : Bytes
} derive(Show, ToJson)
fn parse_offsets(
data : BytesView,
offset : Int,
glyph_count : Int,
long_offsets : Bool,
) -> Result[Array[Int], VarError] {
let count = glyph_count + 1
let offsets : Array[Int] = []
if long_offsets {
let total = count * 4
if offset < 0 || offset + total > data.length() {
return Err(UnexpectedEof)
}
let mut pos = offset
for _ in 0.. return Err(err)
Ok(v) => offsets.push(v)
}
pos = pos + 4
}
} else {
let total = count * 2
if offset < 0 || offset + total > data.length() {
return Err(UnexpectedEof)
}
let mut pos = offset
for _ in 0.. return Err(err)
Ok(v) => offsets.push(v * 2)
}
pos = pos + 2
}
}
Ok(offsets)
}
///|
pub fn GvarTable::parse(data : BytesView) -> Result[GvarTable, VarError] {
let version = read_u32(data, 0)
let axis_count = read_u16_int(data, 4)
let shared_tuple_count = read_u16_int(data, 6)
let shared_tuples_offset = read_u32_int(data, 8)
let glyph_count = read_u16_int(data, 12)
let flags = read_u16_int(data, 14)
let data_offset = read_u32_int(data, 16)
match (
version,
axis_count,
shared_tuple_count,
shared_tuples_offset,
glyph_count,
flags,
data_offset,
) {
(Err(err), _, _, _, _, _, _) => Err(err)
(_, Err(err), _, _, _, _, _) => Err(err)
(_, _, Err(err), _, _, _, _) => Err(err)
(_, _, _, Err(err), _, _, _) => Err(err)
(_, _, _, _, Err(err), _, _) => Err(err)
(_, _, _, _, _, Err(err), _) => Err(err)
(_, _, _, _, _, _, Err(err)) => Err(err)
(Ok(version), Ok(axis_count), Ok(shared_tuple_count), Ok(shared_tuples_offset), Ok(glyph_count), Ok(flags), Ok(data_offset)) => {
let major = (version >> 16) & 0xFFFFU
if major != 1U {
return Err(InvalidFormat)
}
if glyph_count < 0 {
return Err(InvalidFormat)
}
let long_offsets = (flags & 1) != 0
let offsets = match parse_offsets(data, 20, glyph_count, long_offsets) {
Err(err) => return Err(err)
Ok(value) => value
}
Ok(GvarTable::{
axis_count,
shared_tuple_count,
shared_tuples_offset,
glyph_count,
flags,
data_offset,
offsets,
data: data.to_bytes(),
})
}
}
}
///|
pub fn GvarTable::glyph_variation_data(self : GvarTable, glyph : Int) -> Bytes? {
if glyph < 0 || glyph >= self.glyph_count {
return None
}
let start = self.offsets[glyph]
let end = self.offsets[glyph + 1]
if end < start {
return None
}
let base = self.data_offset
let length = end - start
if length == 0 {
return None
}
if base < 0 || base + end > self.data.length() {
return None
}
Some(self.data[base + start:base + start + length].to_bytes())
}
///|
pub fn GvarTable::shared_tuples(self : GvarTable) -> Result[Array[Int], VarError] {
if self.shared_tuple_count <= 0 {
return Ok([])
}
if self.shared_tuples_offset <= 0 || self.shared_tuples_offset >= self.data.length() {
return Err(UnexpectedEof)
}
let total = self.shared_tuple_count * self.axis_count
let tuples : Array[Int] = []
let mut pos = self.shared_tuples_offset
for _ in 0.. return Err(err)
Ok(v) => tuples.push(v)
}
pos = pos + 2
}
Ok(tuples)
}
///|
pub fn GvarTable::glyph_tuple_variations(
self : GvarTable,
glyph : Int,
) -> Result[TupleVariationData?, VarError] {
let data = self.glyph_variation_data(glyph)
match data {
None => Ok(None)
Some(bytes) => {
let shared = match self.shared_tuples() {
Err(err) => return Err(err)
Ok(value) => value
}
let parsed = TupleVariationData::parse(bytes[:], self.axis_count, shared)
match parsed {
Err(err) => Err(err)
Ok(value) => Ok(Some(value))
}
}
}
}
///|
pub fn GvarTable::glyph_deltas(
self : GvarTable,
glyph : Int,
coords : Array[Int],
point_count : Int,
) -> Result[(Array[Double], Array[Double]?), VarError] {
let tuple_data = match self.glyph_tuple_variations(glyph) {
Err(err) => return Err(err)
Ok(value) => value
}
match tuple_data {
Some(value) => value.apply(coords, point_count, true)
None => {
let deltas_x : Array[Double] = []
let deltas_y : Array[Double] = []
for _ in 0..