///|
fn bytes_array_contains(values : Array[Bytes], value : Bytes) -> Bool {
let mut found = false
for item in values {
if item == value {
found = true
}
}
found
}
///|
fn copy_non_empty_unique_bytes(
values : Array[Bytes],
skip? : Bytes = b"",
) -> Array[Bytes] {
let result : Array[Bytes] = Array::new(capacity=values.length())
for value in values {
if !value.is_empty() &&
value != skip &&
!bytes_array_contains(result, value) {
result.push(value)
}
}
result
}
///|
/// Ordered family list used before asking a native font manager for fallback.
pub(all) struct FontFallbackChain {
primary_family : Bytes
fallback_families : Array[Bytes]
} derive(Debug, Eq)
///|
pub fn FontFallbackChain::new(
primary_family? : Bytes = b"",
fallback_families? : Array[Bytes] = [],
) -> FontFallbackChain {
{
primary_family,
fallback_families: copy_non_empty_unique_bytes(
fallback_families,
skip=primary_family,
),
}
}
///|
pub fn FontFallbackChain::length(self : FontFallbackChain) -> Int {
let primary_count = if self.primary_family.is_empty() { 0 } else { 1 }
primary_count + self.fallback_families.length()
}
///|
pub fn FontFallbackChain::is_empty(self : FontFallbackChain) -> Bool {
self.length() == 0
}
///|
pub fn FontFallbackChain::family_at(
self : FontFallbackChain,
index : Int,
) -> Bytes? {
if index < 0 {
None
} else if self.primary_family.is_empty() {
self.fallback_families.get(index)
} else if index == 0 {
Some(self.primary_family)
} else {
self.fallback_families.get(index - 1)
}
}
///|
pub fn FontFallbackChain::contains_family(
self : FontFallbackChain,
family : Bytes,
) -> Bool {
!family.is_empty() &&
(
self.primary_family == family ||
bytes_array_contains(self.fallback_families, family)
)
}
///|
fn FontFallbackChain::index_of_family(
self : FontFallbackChain,
family : Bytes,
) -> Int {
if family.is_empty() {
-1
} else if self.primary_family == family {
0
} else {
let mut index = -1
for fallback_index, fallback_family in self.fallback_families {
if index < 0 && fallback_family == family {
index = if self.primary_family.is_empty() {
fallback_index
} else {
fallback_index + 1
}
}
}
index
}
}
///|
pub fn FontFallbackChain::with_family(
self : FontFallbackChain,
family : Bytes,
) -> FontFallbackChain {
if family.is_empty() || self.contains_family(family) {
self
} else {
let fallbacks : Array[Bytes] = Array::new(
capacity=self.fallback_families.length() + 1,
)
for value in self.fallback_families {
fallbacks.push(value)
}
fallbacks.push(family)
{ ..self, fallback_families: fallbacks }
}
}
///|
/// Font fallback query for one character or run.
pub(all) struct FontFallbackRequest {
families : FontFallbackChain
bcp47 : Array[Bytes]
character : Int
style : FontStyleRequest
} derive(Debug, Eq)
///|
pub fn FontFallbackRequest::new(
families? : FontFallbackChain = FontFallbackChain::new(),
bcp47? : Array[Bytes] = [],
character? : Int = 0,
style? : FontStyleRequest = FontStyleRequest::new(),
) -> FontFallbackRequest {
{
families,
bcp47: copy_non_empty_unique_bytes(bcp47),
character: Int::max(0, character),
style,
}
}
///|
pub fn FontFallbackRequest::language_count(self : FontFallbackRequest) -> Int {
self.bcp47.length()
}
///|
pub fn FontFallbackRequest::language_at(
self : FontFallbackRequest,
index : Int,
) -> Bytes? {
if index < 0 {
None
} else {
self.bcp47.get(index)
}
}
///|
pub fn FontFallbackRequest::has_character(self : FontFallbackRequest) -> Bool {
self.character > 0
}
///|
pub fn FontFallbackRequest::is_empty(self : FontFallbackRequest) -> Bool {
self.families.is_empty() && self.bcp47.is_empty() && !self.has_character()
}
///|
pub fn FontFallbackRequest::with_character(
self : FontFallbackRequest,
character : Int,
) -> FontFallbackRequest {
{ ..self, character: Int::max(0, character) }
}
///|
pub fn FontFallbackRequest::with_style(
self : FontFallbackRequest,
style : FontStyleRequest,
) -> FontFallbackRequest {
{ ..self, style, }
}
///|
/// Value-layer metadata for a native font fallback/typeface match result.
pub(all) struct FontFallbackMatchDescriptor {
request : FontFallbackRequest
matched_family : Bytes
fallback_index : Int
synthetic : Bool
} derive(Debug, Eq)
///|
fn normalized_font_fallback_index(
request : FontFallbackRequest,
matched_family : Bytes,
fallback_index : Int,
) -> Int {
if fallback_index >= 0 && fallback_index < request.families.length() {
fallback_index
} else {
request.families.index_of_family(matched_family)
}
}
///|
pub fn FontFallbackMatchDescriptor::new(
request? : FontFallbackRequest = FontFallbackRequest::new(),
matched_family? : Bytes = b"",
fallback_index? : Int = -1,
synthetic? : Bool = false,
) -> FontFallbackMatchDescriptor {
{
request,
matched_family,
fallback_index: normalized_font_fallback_index(
request, matched_family, fallback_index,
),
synthetic,
}
}
///|
pub fn FontFallbackRequest::match_descriptor(
self : FontFallbackRequest,
matched_family : Bytes,
fallback_index? : Int = -1,
synthetic? : Bool = false,
) -> FontFallbackMatchDescriptor {
FontFallbackMatchDescriptor::new(
request=self,
matched_family~,
fallback_index~,
synthetic~,
)
}
///|
pub fn FontFallbackMatchDescriptor::is_valid(
self : FontFallbackMatchDescriptor,
) -> Bool {
!self.matched_family.is_empty()
}
///|
pub fn FontFallbackMatchDescriptor::source_family(
self : FontFallbackMatchDescriptor,
) -> Bytes? {
if self.fallback_index < 0 {
None
} else {
self.request.families.family_at(self.fallback_index)
}
}
///|
pub fn FontFallbackMatchDescriptor::matched_in_request(
self : FontFallbackMatchDescriptor,
) -> Bool {
self.request.families.contains_family(self.matched_family)
}
///|
fn FontFallbackChain::text_run_cache_id(self : FontFallbackChain) -> Bytes {
let mut id = text_run_cache_bytes(self.primary_family) +
text_run_cache_u64(self.fallback_families.length())
for family in self.fallback_families {
id = id + text_run_cache_bytes(family)
}
id
}
///|
fn FontFallbackRequest::text_run_cache_id(self : FontFallbackRequest) -> Bytes {
let mut id = self.families.text_run_cache_id() +
text_run_cache_u64(self.bcp47.length())
for language in self.bcp47 {
id = id + text_run_cache_bytes(language)
}
id + text_run_cache_u64(self.character) + self.style.text_run_cache_id()
}
///|
fn FontFallbackRequest::resource_id(self : FontFallbackRequest) -> Bytes {
if self.is_empty() {
b""
} else {
b"moui_skia:font-fallback:v1" + self.text_run_cache_id()
}
}
///|
fn FontFallbackMatchDescriptor::text_run_cache_id(
self : FontFallbackMatchDescriptor,
) -> Bytes {
self.request.text_run_cache_id() +
text_run_cache_bytes(self.matched_family) +
text_run_cache_bool(self.fallback_index >= 0) +
text_run_cache_u64(self.fallback_index) +
text_run_cache_bool(self.synthetic)
}
///|
fn FontFallbackMatchDescriptor::resource_id(
self : FontFallbackMatchDescriptor,
) -> Bytes {
if !self.is_valid() {
b""
} else {
b"moui_skia:font-fallback-match:v1" + self.text_run_cache_id()
}
}
///|
/// Deterministic renderer cache key for native font fallback/typeface matching.
pub fn FontFallbackRequest::resource_key(
self : FontFallbackRequest,
) -> RendererResourceKey {
RendererResourceKey::typeface(self.resource_id())
}
///|
/// Cache descriptor for a native font fallback/typeface match result.
pub fn FontFallbackRequest::resource_descriptor(
self : FontFallbackRequest,
byte_size? : Int64 = -1L,
) -> RendererResourceDescriptor {
let id = self.resource_id()
let measured_byte_size = if byte_size < 0L {
id.length().to_int64()
} else {
byte_size
}
RendererResourceDescriptor::typeface(id, byte_size=measured_byte_size)
}
///|
/// Font descriptor derived from a native fallback/typeface match result.
pub fn FontFallbackRequest::font_descriptor(
self : FontFallbackRequest,
size? : Float = 12.0,
scale_x? : Float = 1.0,
skew_x? : Float = 0.0,
) -> FontDescriptor {
FontDescriptor::new(
typeface_key=self.resource_key(),
size~,
scale_x~,
skew_x~,
)
}
///|
/// Combined resource plan for matching a fallback typeface and creating a font.
pub fn FontFallbackRequest::resource_plan(
self : FontFallbackRequest,
size? : Float = 12.0,
scale_x? : Float = 1.0,
skew_x? : Float = 0.0,
fallback_byte_size? : Int64 = -1L,
font_byte_size? : Int64 = -1L,
) -> RendererResourcePlan {
if self.is_empty() {
RendererResourcePlan::new([
self.resource_descriptor(byte_size=fallback_byte_size),
])
} else {
RendererResourcePlan::new([
self.resource_descriptor(byte_size=fallback_byte_size),
self
.font_descriptor(size~, scale_x~, skew_x~)
.resource_descriptor(byte_size=font_byte_size),
])
}
}
///|
/// Deterministic renderer cache key for a resolved native fallback match.
pub fn FontFallbackMatchDescriptor::resource_key(
self : FontFallbackMatchDescriptor,
) -> RendererResourceKey {
RendererResourceKey::typeface(self.resource_id())
}
///|
/// Cache descriptor for resolved fallback/typeface match metadata.
pub fn FontFallbackMatchDescriptor::resource_descriptor(
self : FontFallbackMatchDescriptor,
byte_size? : Int64 = -1L,
) -> RendererResourceDescriptor {
let id = self.resource_id()
let measured_byte_size = if byte_size < 0L {
id.length().to_int64()
} else {
byte_size
}
RendererResourceDescriptor::typeface(id, byte_size=measured_byte_size)
}
///|
/// Font descriptor derived from a resolved fallback/typeface match.
pub fn FontFallbackMatchDescriptor::font_descriptor(
self : FontFallbackMatchDescriptor,
size? : Float = 12.0,
scale_x? : Float = 1.0,
skew_x? : Float = 0.0,
) -> FontDescriptor {
FontDescriptor::new(
typeface_key=self.resource_key(),
size~,
scale_x~,
skew_x~,
)
}
///|
/// Combined resource plan for retaining a fallback match and derived font.
pub fn FontFallbackMatchDescriptor::resource_plan(
self : FontFallbackMatchDescriptor,
size? : Float = 12.0,
scale_x? : Float = 1.0,
skew_x? : Float = 0.0,
match_byte_size? : Int64 = -1L,
font_byte_size? : Int64 = -1L,
) -> RendererResourcePlan {
if self.is_valid() {
RendererResourcePlan::new([
self.resource_descriptor(byte_size=match_byte_size),
self
.font_descriptor(size~, scale_x~, skew_x~)
.resource_descriptor(byte_size=font_byte_size),
])
} else {
RendererResourcePlan::new([
self.resource_descriptor(byte_size=match_byte_size),
])
}
}
///|
/// Value-layer metadata for a completed font fallback resolution.
pub(all) struct FontFallbackResolutionDescriptor {
request : FontFallbackRequest
resolved_match : FontFallbackMatchDescriptor
} derive(Debug, Eq)
///|
fn font_fallback_resolution_normalized_match(
request : FontFallbackRequest,
resolved_match : FontFallbackMatchDescriptor,
) -> FontFallbackMatchDescriptor {
if resolved_match.request == request {
resolved_match
} else {
request.match_descriptor(
resolved_match.matched_family,
fallback_index=resolved_match.fallback_index,
synthetic=resolved_match.synthetic,
)
}
}
///|
pub fn FontFallbackResolutionDescriptor::new(
request? : FontFallbackRequest = FontFallbackRequest::new(),
resolved_match? : FontFallbackMatchDescriptor = FontFallbackMatchDescriptor::new(),
) -> FontFallbackResolutionDescriptor {
{
request,
resolved_match: font_fallback_resolution_normalized_match(
request, resolved_match,
),
}
}
///|
pub fn FontFallbackRequest::resolution_descriptor(
self : FontFallbackRequest,
matched_family : Bytes,
fallback_index? : Int = -1,
synthetic? : Bool = false,
) -> FontFallbackResolutionDescriptor {
FontFallbackResolutionDescriptor::new(
request=self,
resolved_match=self.match_descriptor(
matched_family,
fallback_index~,
synthetic~,
),
)
}
///|
pub fn FontFallbackResolutionDescriptor::is_valid(
self : FontFallbackResolutionDescriptor,
) -> Bool {
self.resolved_match.request == self.request && self.resolved_match.is_valid()
}
///|
pub fn FontFallbackResolutionDescriptor::matched_in_request(
self : FontFallbackResolutionDescriptor,
) -> Bool {
self.resolved_match.matched_in_request()
}
///|
pub fn FontFallbackResolutionDescriptor::source_family(
self : FontFallbackResolutionDescriptor,
) -> Bytes? {
self.resolved_match.source_family()
}
///|
fn FontFallbackResolutionDescriptor::resource_id(
self : FontFallbackResolutionDescriptor,
) -> Bytes {
if !self.is_valid() {
b""
} else {
b"moui_skia:font-fallback-resolution:v1" +
text_run_cache_bytes(self.request.resource_key().id) +
text_run_cache_bytes(self.resolved_match.resource_key().id)
}
}
///|
pub fn FontFallbackResolutionDescriptor::resource_key(
self : FontFallbackResolutionDescriptor,
) -> RendererResourceKey {
RendererResourceKey::typeface(self.resource_id())
}
///|
pub fn FontFallbackResolutionDescriptor::resource_descriptor(
self : FontFallbackResolutionDescriptor,
byte_size? : Int64 = -1L,
) -> RendererResourceDescriptor {
let id = self.resource_id()
let measured_byte_size = if byte_size < 0L {
id.length().to_int64()
} else {
byte_size
}
RendererResourceDescriptor::typeface(id, byte_size=measured_byte_size)
}
///|
pub fn FontFallbackResolutionDescriptor::font_descriptor(
self : FontFallbackResolutionDescriptor,
size? : Float = 12.0,
scale_x? : Float = 1.0,
skew_x? : Float = 0.0,
) -> FontDescriptor {
FontDescriptor::new(
typeface_key=self.resource_key(),
size~,
scale_x~,
skew_x~,
)
}
///|
pub fn FontFallbackResolutionDescriptor::resource_plan(
self : FontFallbackResolutionDescriptor,
size? : Float = 12.0,
scale_x? : Float = 1.0,
skew_x? : Float = 0.0,
request_byte_size? : Int64 = -1L,
resolution_byte_size? : Int64 = -1L,
match_byte_size? : Int64 = -1L,
font_byte_size? : Int64 = -1L,
) -> RendererResourcePlan {
if self.is_valid() {
RendererResourcePlan::new([
self.request.resource_descriptor(byte_size=request_byte_size),
self.resource_descriptor(byte_size=resolution_byte_size),
self.resolved_match.resource_descriptor(byte_size=match_byte_size),
self
.font_descriptor(size~, scale_x~, skew_x~)
.resource_descriptor(byte_size=font_byte_size),
])
} else {
RendererResourcePlan::new([
self.resource_descriptor(byte_size=resolution_byte_size),
])
}
}