@@ -140,6 +140,126 @@ impl CGFont {
140
140
None
141
141
}
142
142
}
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
143
263
}
144
264
145
265
#[ link( name = "CoreGraphics" , kind = "framework" ) ]
@@ -174,3 +294,19 @@ extern {
174
294
fn CGFontCopyVariations ( font : :: sys:: CGFontRef ) -> CFDictionaryRef ;
175
295
fn CGFontCopyVariationAxes ( font : :: sys:: CGFontRef ) -> CFArrayRef ;
176
296
}
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