// Copyright 2025 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.
///|
/// Glyph cluster modeling-- output from the shaper.
///
/// Ported from `swash/src/shape/cluster.rs` (swash is dual-licensed Apache-2.0 OR MIT).
///|
/// Information for a glyph.
pub struct GlyphInfo {
bits : UInt16
}
///|
///|
pub fn GlyphInfo::default() -> GlyphInfo {
{ bits: 0, }
}
///|
pub fn GlyphInfo::GlyphInfo(bits : UInt16) -> GlyphInfo {
{ bits, }
}
///|
/// Returns true if the glyph is an attached mark.
pub fn GlyphInfo::is_mark(self : GlyphInfo) -> Bool {
(self.bits & MARK_ATTACH) != 0
}
///|
/// Glyph identifier and positioning information as a result of shaping.
pub struct Glyph {
/// Glyph identifier.
id : GlyphId
/// Glyph flags.
info : GlyphInfo
/// Horizontal offset.
x : Double
/// Vertical offset.
y : Double
/// Advance width or height.
advance : Double
/// Arbitrary user data.
data : UserData
}
///|
pub fn Glyph::default() -> Glyph {
{
id: (0).to_uint16(),
info: GlyphInfo::default(),
x: 0.0,
y: 0.0,
advance: 0.0,
data: 0U,
}
}
///|
pub fn Glyph::id(self : Glyph) -> GlyphId {
self.id
}
///|
pub fn Glyph::info(self : Glyph) -> GlyphInfo {
self.info
}
///|
pub fn Glyph::x(self : Glyph) -> Double {
self.x
}
///|
pub fn Glyph::y(self : Glyph) -> Double {
self.y
}
///|
pub fn Glyph::advance(self : Glyph) -> Double {
self.advance
}
///|
pub fn Glyph::data(self : Glyph) -> UserData {
self.data
}
///|
/// Collection of glyphs and associated metadata corresponding to one or
/// more source clusters.
pub struct GlyphCluster {
/// Full source range of the cluster in original units supplied to the shaper.
source : SourceRange
/// Information about the textual content of the cluster.
info : ClusterInfo
/// Sequence of glyphs for the cluster. May be empty for clusters whose
/// source consisted entirely of control characters.
glyphs : ArrayView[Glyph]
/// If the cluster is a ligature, this contains the source range
/// of each ligature component. Empty otherwise.
components : ArrayView[SourceRange]
/// Arbitrary user data-- taken from the initial character of the cluster.
data : UserData
}
///|
pub fn GlyphCluster::source(self : GlyphCluster) -> SourceRange {
self.source
}
///|
pub fn GlyphCluster::info(self : GlyphCluster) -> ClusterInfo {
self.info
}
///|
pub fn GlyphCluster::glyphs(self : GlyphCluster) -> ArrayView[Glyph] {
self.glyphs
}
///|
pub fn GlyphCluster::components(self : GlyphCluster) -> ArrayView[SourceRange] {
self.components
}
///|
pub fn GlyphCluster::data(self : GlyphCluster) -> UserData {
self.data
}
///|
/// Returns true if the cluster is empty. Empty clusters still represent
/// characters in the source text, but contain no glyphs. This will be
/// true, for example, with newline sequences (\n or \r\n) as well as other
/// control characters.
pub fn GlyphCluster::is_empty(self : GlyphCluster) -> Bool {
self.glyphs.length() == 0
}
///|
/// Returns true if the cluster contains a single glyph. Note that a simple
/// cluster can also be a ligature.
pub fn GlyphCluster::is_simple(self : GlyphCluster) -> Bool {
self.glyphs.length() == 1
}
///|
/// Returns true if the cluster corresponds to multiple source clusters.
/// Note that a ligature cluster can also be complex.
pub fn GlyphCluster::is_ligature(self : GlyphCluster) -> Bool {
self.components.length() != 0
}
///|
/// Returns true if the cluster is complex-- that is if it contains more
/// than one glyph. This will be true for clusters containing marks and is
/// also commonly true for syllabic languages such as those in the Indic
/// family.
pub fn GlyphCluster::is_complex(self : GlyphCluster) -> Bool {
self.glyphs.length() > 1
}
///|
/// Computes the full advance width or height of the cluster.
pub fn GlyphCluster::advance(self : GlyphCluster) -> Double {
let mut advance = 0.0
for g in self.glyphs {
advance = advance + g.advance()
}
advance
}