Skip to content

Commit

Permalink
Add an api to construct font data
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmuizel committed Feb 10, 2021
1 parent 987123d commit b28a9ae
Showing 1 changed file with 136 additions and 0 deletions.
136 changes: 136 additions & 0 deletions core-graphics/src/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,126 @@ impl CGFont {
None
}
}

/// Will construct a Vec<u8> containing a font that has the tables and table contents of the
/// CGFont. This will not necessarily be the exact same bytes as the original font but should
/// be functionally equivalent.
///
/// Manually reconstructing a font is necessary because CoreGraphics does not provide a method
/// to retrieve the actual underlying data.
pub fn construct_font_data(&self) -> Vec<u8> {
construct_font_data(self)
}
}

fn calc_table_checksum(table: &[u8], skip_checksum_adjust: bool) -> u32 {
use std::convert::TryInto;
let mut sum = std::num::Wrapping(0);
let mut i = 0;
let mut chunks = table.chunks_exact(4);
for chunk in &mut chunks {
if skip_checksum_adjust && i == 2 {

} else {
let val = u32::from_be_bytes(chunk.try_into().unwrap());
sum += std::num::Wrapping(val)
}
i += 1;
}

// The table will be zero padded to be 4 byte aligned when written out
// so compute the checksum as if that were the case.
let mut val = [0; 4];
val[0..chunks.remainder().len()].copy_from_slice(chunks.remainder());
let val = u32::from_be_bytes(val);
sum += std::num::Wrapping(val);

sum.0
}

fn max_pow2_less_than_equal(a: i32) -> i32 {
let x = 1;
let mut shift = 0;
while (x << (shift + 1)) <= a {
shift+=1;
}
shift
}

// This code is inspired by the code in mozilla-central/gfx/2d/ScaledFontMac.cpp
fn construct_font_data(font: &CGFont) -> Vec<u8> {
struct TableRecord {
tag: u32,
checksum: u32,
offset: u32,
length: u32,
data: CFData,
}

let tags = font.copy_table_tags();
let count = tags.len();
let mut records = Vec::with_capacity(tags.len() as usize);
let mut offset: u32 = 0;
offset += std::mem::size_of::<u32>() as u32 * 3;
offset += std::mem::size_of::<u32>() as u32 * 4 * count as u32;
let mut cff = false;
for tag in tags.iter() {
let data = font.copy_table_for_tag(*tag).unwrap();
let skip_checksum_adjust = *tag == 0x68656164; // 'head'

if *tag == 0x43464620 { // 'CFF '
cff = true;
}
let checksum = calc_table_checksum(data.bytes(), skip_checksum_adjust);
records.push(TableRecord { tag: *tag, offset, length: data.len() as u32, data: data.clone(), checksum});
offset += data.len() as u32;
// 32 bit align the tables
offset = (offset + 3) & !3;
}

let mut buf: Vec<u8> = Vec::new();
if cff {
buf.extend_from_slice(&0x4f54544fu32.to_be_bytes());
} else {
buf.extend_from_slice(&0x00010000u32.to_be_bytes());
}

buf.extend_from_slice(&(count as u16).to_be_bytes());
let max_pow2_count = max_pow2_less_than_equal(count as i32);
buf.extend_from_slice(&((1u16 << max_pow2_count) * 16).to_be_bytes());
buf.extend_from_slice(&(max_pow2_count as u16).to_be_bytes());
buf.extend_from_slice(&((count as u16 - (1 << max_pow2_count)) * 16).to_be_bytes());

// write table record entries
for rec in &records {
buf.extend_from_slice(&rec.tag.to_be_bytes());
buf.extend_from_slice(&rec.checksum.to_be_bytes());
buf.extend_from_slice(&rec.offset.to_be_bytes());
buf.extend_from_slice(&rec.length.to_be_bytes());
}

// write tables
let mut checksum_adjustment_offset = 0;
for rec in &records {
if rec.tag == 0x68656164 { // 'head'
checksum_adjustment_offset = buf.len() + 2 * 4;
}
assert!(buf.len() == rec.offset as usize);
buf.extend_from_slice(rec.data.bytes());
// align
let extra = ((buf.len() + 3) & !3) - buf.len();
buf.extend_from_slice(&[0;4][0..extra]);
}

// clear the checksumAdjust field before checksumming the whole font
for b in &mut buf[checksum_adjustment_offset..checksum_adjustment_offset+4] {
*b = 0;
}
let font_check_sum = (0xb1b0afba_u32.wrapping_sub(
calc_table_checksum(&buf, false))).to_be_bytes();
(&mut buf[checksum_adjustment_offset..checksum_adjustment_offset+4]).copy_from_slice(&font_check_sum);

buf
}

#[link(name = "CoreGraphics", kind = "framework")]
Expand Down Expand Up @@ -174,3 +294,19 @@ extern {
fn CGFontCopyVariations(font: ::sys::CGFontRef) -> CFDictionaryRef;
fn CGFontCopyVariationAxes(font: ::sys::CGFontRef) -> CFArrayRef;
}

#[cfg(test)]
mod test {
use core_foundation::string::CFString;
use crate::font::*;
#[test]
fn construct_font_data() {
use std::sync::Arc;

let font = CGFont::from_name(&CFString::from_static_string("Helvetica")).unwrap();
let data = font.construct_font_data();
let data_provider = crate::data_provider::CGDataProvider::from_buffer(Arc::new(data));
let font = CGFont::from_data_provider(data_provider).unwrap();
assert_eq!(font.postscript_name(), "Helvetica");
}
}

0 comments on commit b28a9ae

Please sign in to comment.