@@ -140,6 +140,126 @@ impl CGFont {
140140 None
141141 }
142142 }
143+
144+ /// Will construct a Vec<u8> containing a font that has the tables and table contents of the
145+ /// CGFont. This will not necessarily be the exact same bytes as the original font but should
146+ /// be functionally equivalent.
147+ ///
148+ /// Manually reconstructing a font is necessary because CoreGraphics does not provide a method
149+ /// to retrieve the actual underlying data.
150+ pub fn construct_font_data ( & self ) -> Vec < u8 > {
151+ construct_font_data ( self )
152+ }
153+ }
154+
155+ fn calc_table_checksum ( table : & [ u8 ] , skip_checksum_adjust : bool ) -> u32 {
156+ use std:: convert:: TryInto ;
157+ let mut sum = std:: num:: Wrapping ( 0 ) ;
158+ let mut i = 0 ;
159+ let mut chunks = table. chunks_exact ( 4 ) ;
160+ for chunk in & mut chunks {
161+ if skip_checksum_adjust && i == 2 {
162+
163+ } else {
164+ let val = u32:: from_be_bytes ( chunk. try_into ( ) . unwrap ( ) ) ;
165+ sum += std:: num:: Wrapping ( val)
166+ }
167+ i += 1 ;
168+ }
169+
170+ // The table will be zero padded to be 4 byte aligned when written out
171+ // so compute the checksum as if that were the case.
172+ let mut val = [ 0 ; 4 ] ;
173+ val[ 0 ..chunks. remainder ( ) . len ( ) ] . copy_from_slice ( chunks. remainder ( ) ) ;
174+ let val = u32:: from_be_bytes ( val) ;
175+ sum += std:: num:: Wrapping ( val) ;
176+
177+ sum. 0
178+ }
179+
180+ fn max_pow2_less_than_equal ( a : i32 ) -> i32 {
181+ let x = 1 ;
182+ let mut shift = 0 ;
183+ while ( x << ( shift + 1 ) ) <= a {
184+ shift+=1 ;
185+ }
186+ shift
187+ }
188+
189+ // This code is inspired by the code in mozilla-central/gfx/2d/ScaledFontMac.cpp
190+ fn construct_font_data ( font : & CGFont ) -> Vec < u8 > {
191+ struct TableRecord {
192+ tag : u32 ,
193+ checksum : u32 ,
194+ offset : u32 ,
195+ length : u32 ,
196+ data : CFData ,
197+ }
198+
199+ let tags = font. copy_table_tags ( ) ;
200+ let count = tags. len ( ) ;
201+ let mut records = Vec :: with_capacity ( tags. len ( ) as usize ) ;
202+ let mut offset: u32 = 0 ;
203+ offset += std:: mem:: size_of :: < u32 > ( ) as u32 * 3 ;
204+ offset += std:: mem:: size_of :: < u32 > ( ) as u32 * 4 * count as u32 ;
205+ let mut cff = false ;
206+ for tag in tags. iter ( ) {
207+ let data = font. copy_table_for_tag ( * tag) . unwrap ( ) ;
208+ let skip_checksum_adjust = * tag == 0x68656164 ; // 'head'
209+
210+ if * tag == 0x43464620 { // 'CFF '
211+ cff = true ;
212+ }
213+ let checksum = calc_table_checksum ( data. bytes ( ) , skip_checksum_adjust) ;
214+ records. push ( TableRecord { tag : * tag, offset, length : data. len ( ) as u32 , data : data. clone ( ) , checksum} ) ;
215+ offset += data. len ( ) as u32 ;
216+ // 32 bit align the tables
217+ offset = ( offset + 3 ) & !3 ;
218+ }
219+
220+ let mut buf: Vec < u8 > = Vec :: new ( ) ;
221+ if cff {
222+ buf. extend_from_slice ( & 0x4f54544fu32 . to_be_bytes ( ) ) ;
223+ } else {
224+ buf. extend_from_slice ( & 0x00010000u32 . to_be_bytes ( ) ) ;
225+ }
226+
227+ buf. extend_from_slice ( & ( count as u16 ) . to_be_bytes ( ) ) ;
228+ let max_pow2_count = max_pow2_less_than_equal ( count as i32 ) ;
229+ buf. extend_from_slice ( & ( ( 1u16 << max_pow2_count) * 16 ) . to_be_bytes ( ) ) ;
230+ buf. extend_from_slice ( & ( max_pow2_count as u16 ) . to_be_bytes ( ) ) ;
231+ buf. extend_from_slice ( & ( ( count as u16 - ( 1 << max_pow2_count) ) * 16 ) . to_be_bytes ( ) ) ;
232+
233+ // write table record entries
234+ for rec in & records {
235+ buf. extend_from_slice ( & rec. tag . to_be_bytes ( ) ) ;
236+ buf. extend_from_slice ( & rec. checksum . to_be_bytes ( ) ) ;
237+ buf. extend_from_slice ( & rec. offset . to_be_bytes ( ) ) ;
238+ buf. extend_from_slice ( & rec. length . to_be_bytes ( ) ) ;
239+ }
240+
241+ // write tables
242+ let mut checksum_adjustment_offset = 0 ;
243+ for rec in & records {
244+ if rec. tag == 0x68656164 { // 'head'
245+ checksum_adjustment_offset = buf. len ( ) + 2 * 4 ;
246+ }
247+ assert ! ( buf. len( ) == rec. offset as usize ) ;
248+ buf. extend_from_slice ( rec. data . bytes ( ) ) ;
249+ // align
250+ let extra = ( ( buf. len ( ) + 3 ) & !3 ) - buf. len ( ) ;
251+ buf. extend_from_slice ( & [ 0 ; 4 ] [ 0 ..extra] ) ;
252+ }
253+
254+ // clear the checksumAdjust field before checksumming the whole font
255+ for b in & mut buf[ checksum_adjustment_offset..checksum_adjustment_offset+4 ] {
256+ * b = 0 ;
257+ }
258+ let font_check_sum = ( 0xb1b0afba_u32 . wrapping_sub (
259+ calc_table_checksum ( & buf, false ) ) ) . to_be_bytes ( ) ;
260+ ( & mut buf[ checksum_adjustment_offset..checksum_adjustment_offset+4 ] ) . copy_from_slice ( & font_check_sum) ;
261+
262+ buf
143263}
144264
145265#[ link( name = "CoreGraphics" , kind = "framework" ) ]
@@ -174,3 +294,19 @@ extern {
174294 fn CGFontCopyVariations ( font : :: sys:: CGFontRef ) -> CFDictionaryRef ;
175295 fn CGFontCopyVariationAxes ( font : :: sys:: CGFontRef ) -> CFArrayRef ;
176296}
297+
298+ #[ cfg( test) ]
299+ mod test {
300+ use core_foundation:: string:: CFString ;
301+ use crate :: font:: * ;
302+ #[ test]
303+ fn construct_font_data ( ) {
304+ use std:: sync:: Arc ;
305+
306+ let font = CGFont :: from_name ( & CFString :: from_static_string ( "Helvetica" ) ) . unwrap ( ) ;
307+ let data = font. construct_font_data ( ) ;
308+ let data_provider = crate :: data_provider:: CGDataProvider :: from_buffer ( Arc :: new ( data) ) ;
309+ let font = CGFont :: from_data_provider ( data_provider) . unwrap ( ) ;
310+ assert_eq ! ( font. postscript_name( ) , "Helvetica" ) ;
311+ }
312+ }
0 commit comments