tectonic_bridge_harfbuzz 0.3.3

Expose the Harfbuzz C/C++ APIs to Rust/Cargo.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
// Copyright 2020-2023 the Tectonic Project
// Licensed under the MIT License.

//! This crate exists to export the Harfbuzz *C/C++* API into the Cargo framework, as well as
//! provide bindings to other tectonic crates.

use std::ffi::CStr;
use std::marker::PhantomData;
use std::ops::Deref;
use std::ptr::NonNull;
use std::{ptr, slice};
use tectonic_bridge_graphite2 as gr;

mod font_funcs;
pub mod ot;
#[doc(hidden)]
pub mod sys;
#[cfg(test)]
mod test_util;

pub use font_funcs::{FontFuncs, FontFuncsMut, FontFuncsRef, ImmutFontFuncs};
pub use sys::hb_buffer_content_type_t as BufferContentType;
pub use sys::hb_codepoint_t as Codepoint;
pub use sys::hb_direction_t as Direction;
pub use sys::hb_feature_t as Feature;
pub use sys::hb_glyph_extents_t as GlyphExtents;
pub use sys::hb_glyph_info_t as GlyphInfo;
pub use sys::hb_glyph_position_t as GlyphPosition;
pub use sys::hb_memory_mode_t as MemoryMode;
pub use sys::hb_ot_name_id_t as OtNameId;
pub use sys::hb_position_t as Position;
pub use sys::hb_segment_properties_t as SegmentProperties;

/// Import something from our bridge crates so that we ensure that we actually
/// link with them, to pull in the symbols defined in the C APIs.
mod linkage {
    #[allow(unused_imports)]
    use tectonic_bridge_freetype2 as clippyrenamehack1;
    #[allow(unused_imports)]
    #[cfg(target_os = "macos")]
    use tectonic_mac_core as clippyrenamehack2;
}

unsafe extern "C" fn dealloc<T: 'static>(user_data: *mut ()) {
    // SAFETY: Soundness precondition - this is only called on pointers created by `Box::into_raw`
    let _ = unsafe { Box::from_raw(user_data.cast::<T>()) };
}

/// Data type representing different script values
#[repr(transparent)]
pub struct Script(sys::hb_script_t);

impl Script {
    /// The invalid script
    pub const INVALID: Script = Script(sys::HB_SCRIPT_INVALID);

    /// Get the text layout direction of this script
    pub fn get_horizontal_direction(&self) -> Direction {
        // SAFETY: This is always safe to call
        unsafe { sys::hb_script_get_horizontal_direction(self.0) }
    }
}

/// Tag identifiers - used to identify tables, scripts, etc.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
pub struct Tag(sys::hb_tag_t);

impl Tag {
    /// Create a new tag from a raw 4-byte integer
    pub fn new(val: u32) -> Tag {
        Tag(val)
    }

    /// Create a new tag from a string. Strings shorter than 4 characters will be padded with
    /// spaces, those longer than 4 characters will be truncated.
    #[allow(clippy::should_implement_trait)]
    pub fn from_str(val: &str) -> Tag {
        // SAFETY: The provided string is not used past this call, and not read past len
        Tag(unsafe { sys::hb_tag_from_string(val.as_ptr().cast(), val.len() as libc::c_int) })
    }

    /// Same as [`Self::from_str`], but accepts a C-string
    pub fn from_cstr(val: &CStr) -> Tag {
        // SAFETY: The provided string is not used past this call, and not read past the terminating
        //         null
        Tag(unsafe { sys::hb_tag_from_string(val.as_ptr(), -1) })
    }

    /// Convert this tag into a 4-byte integer
    pub fn to_raw(self) -> u32 {
        self.0
    }

    /// Convert this tag into a [`Script`]
    pub fn to_script(self) -> Script {
        // SAFETY: This is always safe to call
        Script(unsafe { sys::hb_ot_tag_to_script(self.0) })
    }

    /// Convert this tag into a [`Language`]
    pub fn to_language(self) -> Language {
        // SAFETY: This is always safe to call
        Language(unsafe { sys::hb_ot_tag_to_language(self.0) })
    }
}

/// Data type representing BCP 47 language tags
#[derive(Copy, Clone)]
pub struct Language(*mut sys::hb_language_impl_t);

impl Language {
    /// Convert the provided string into a language tag
    pub fn from_string(str: &str) -> Language {
        // SAFETY: The provided string is not used past this call, and not read past len
        Language(unsafe {
            sys::hb_language_from_string(str.as_ptr().cast(), str.len() as libc::c_int)
        })
    }

    /// Same as [`Self::from_string`], but accepts a C-string
    pub fn from_cstr(str: &CStr) -> Language {
        // SAFETY: The provided string is not used past this call, and not read past the terminating
        //         null
        Language(unsafe { sys::hb_language_from_string(str.as_ptr(), -1) })
    }

    /// Convert this tag into its string representation
    pub fn to_string(&self) -> Option<&CStr> {
        // SAFETY: Internal pointer guaranteed valid
        let ptr = unsafe { sys::hb_language_to_string(self.0) };
        // ptr may be null if we have HB_LANGUAGE_INVALID
        if ptr.is_null() {
            None
        } else {
            // SAFETY: Pointer returned by hb_language_to_string is a valid C-string if non-null
            Some(unsafe { CStr::from_ptr(ptr) })
        }
    }
}

impl Default for Language {
    fn default() -> Language {
        // This gets HB_LANGUAGE_INVALID
        // SAFETY: This function is safe to call with null and will return a marker value
        Language(unsafe { sys::hb_language_from_string(ptr::null(), -1) })
    }
}

/// A borrowed reference to a [`Buffer`]
#[derive(Copy, Clone)]
pub struct BufferRef<'a>(NonNull<sys::hb_buffer_t>, PhantomData<&'a sys::hb_buffer_t>);

impl<'a> BufferRef<'a> {
    fn as_ptr(self) -> *mut sys::hb_buffer_t {
        self.0.as_ptr()
    }

    /// Get the number of items in this buffer
    pub fn len(self) -> usize {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_get_length(self.as_ptr()) as usize }
    }

    /// Whether this buffer is empty
    pub fn is_empty(self) -> bool {
        self.len() == 0
    }

    /// Get a slice of glyph information. Will return `None` if no information as present, such
    /// as before this buffer is shaped via a call to [`ShapePlanMut::execute`].
    pub fn glyph_info(self) -> Option<&'a [GlyphInfo]> {
        let mut len = 0;
        // SAFETY: Internal pointer guaranteed valid
        let ptr = unsafe { sys::hb_buffer_get_glyph_infos(self.as_ptr(), &mut len) };
        // FIXME(CraftSpider): This isn't fully sound unless we never allow `hb_buffer_reference` -
        //       currently it's fine, but we may need to either bite the cost of cloning, force
        //       refcounting to be on the Rust side, or do something... weird.
        if ptr.is_null() {
            None
        } else {
            // SAFETY: Returned pointer is to an array of length `len`, and is valid as long as contents
            //         of the buffer aren't modified
            Some(unsafe { slice::from_raw_parts(ptr, len as usize) })
        }
    }

    /// Get a slice of glyph positions. Will return `None` if no information as present, such
    /// as before this buffer is shaped via a call to [`ShapePlanMut::execute`].
    pub fn glyph_positions(self) -> Option<&'a [GlyphPosition]> {
        let mut len = 0;
        // SAFETY: Internal pointer guaranteed valid
        let ptr = unsafe { sys::hb_buffer_get_glyph_positions(self.as_ptr(), &mut len) };
        // FIXME(CraftSpider): See get_glyph_info
        if ptr.is_null() {
            None
        } else {
            // SAFETY: Returned pointer is to an array of length `len`, and is valid as long as contents
            //         of the buffer aren't modified
            Some(unsafe { slice::from_raw_parts(ptr, len as usize) })
        }
    }

    /// Get the script of this buffer
    pub fn get_script(self) -> Script {
        // SAFETY: Internal pointer guaranteed valid
        Script(unsafe { sys::hb_buffer_get_script(self.as_ptr()) })
    }

    /// Get the segment properties of this buffer
    pub fn get_segment_properties(self) -> SegmentProperties {
        let mut props = SegmentProperties::default();
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_get_segment_properties(self.as_ptr(), &mut props) };
        props
    }
}

/// A borrowed mutable reference to a [`Buffer`]
pub struct BufferMut<'a>(BufferRef<'a>, PhantomData<&'a mut sys::hb_buffer_t>);

impl BufferMut<'_> {
    fn as_ptr_mut(&mut self) -> *mut sys::hb_buffer_t {
        self.0.as_ptr()
    }

    /// Set the content type of this buffer. This should rarely need called, as most methods that
    /// mutate a buffer transition the content type themselves.
    pub fn set_content_type(&mut self, content: BufferContentType) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_set_content_type(self.as_ptr_mut(), content) }
    }

    /// Set the text direction for this buffer.
    pub fn set_direction(&mut self, direction: Direction) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_set_direction(self.as_ptr_mut(), direction) }
    }

    /// Set the language for this buffer.
    pub fn set_language(&mut self, lang: Language) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_set_language(self.as_ptr_mut(), lang.0) }
    }

    /// Set the script for this buffer.
    pub fn set_script(&mut self, script: Script) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_set_script(self.as_ptr_mut(), script.0) }
    }

    /// Add UTF-16 codepoints to the contents of the buffer.
    pub fn add_utf16(&mut self, text: &[u16]) {
        // SAFETY: Internal pointer guaranteed valid. Provided text isn't held past this call.
        unsafe {
            sys::hb_buffer_add_utf16(
                self.as_ptr_mut(),
                text.as_ptr(),
                text.len() as libc::c_int,
                0,
                text.len() as libc::c_int,
            )
        }
    }

    /// Set properties that haven't been set manually based on the current contents of the buffer.
    pub fn guess_segment_properties(&mut self) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_guess_segment_properties(self.as_ptr_mut()) }
    }

    /// Reset the buffer to a freshly created status. More efficient than calling [`Buffer::new`]
    /// repeatedly.
    pub fn reset(&mut self) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_buffer_reset(self.as_ptr_mut()) }
    }
}

impl<'a> Deref for BufferMut<'a> {
    type Target = BufferRef<'a>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// Container for text and associated properties before shaping, then glyphs and associated
/// information after shaping.
pub struct Buffer(NonNull<sys::hb_buffer_t>);

impl Buffer {
    /// Create a new buffer to use when shaping text
    pub fn new() -> Buffer {
        // SAFETY: This is always safe to call
        let ptr = unsafe { sys::hb_buffer_create() };
        Buffer(NonNull::new(ptr).unwrap())
    }

    /// Convert into a shared reference
    pub fn as_ref(&self) -> BufferRef<'_> {
        BufferRef(self.0, PhantomData)
    }

    /// Convert into a mutable reference
    pub fn as_mut(&mut self) -> BufferMut<'_> {
        BufferMut(self.as_ref(), PhantomData)
    }
}

impl Default for Buffer {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for Buffer {
    fn drop(&mut self) {
        // SAFETY: Internal pointer guaranteed valid, we own the pointer.
        unsafe { sys::hb_buffer_destroy(self.0.as_ptr()) }
    }
}

/// Blob of binary data. Facilitates interaction of program blobs (such as static memory or [`Vec`])
/// with the Harfbuzz library's lifecycle management.
pub struct Blob(NonNull<sys::hb_blob_t>);

impl Blob {
    /// Create a new blob from a [`Vec`] of bytes
    pub fn new(data: Vec<u8>) -> Blob {
        unsafe extern "C" fn blob_dealloc(ptr: *mut ()) {
            let slice = Box::from_raw(ptr.cast::<(*mut (), usize)>());
            let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(slice.0.cast::<u8>(), slice.1));
        }

        let len = data.len();
        let data = Box::into_raw(data.into_boxed_slice());
        let slice_data = Box::into_raw(Box::new((data.cast::<u8>(), len)));
        // SAFETY: The provided pointer is never referenced after being creating from Box::into_raw.
        //         The pointer will live as long as the blob, and be deallocated by blob_dealloc
        //         once the blob is destroyed.
        let raw = unsafe {
            sys::hb_blob_create(
                data.cast(),
                len as libc::c_uint,
                MemoryMode::Writable,
                slice_data.cast(),
                Some(blob_dealloc),
            )
        };

        Blob(NonNull::new(raw).unwrap())
    }
}

/// Valid tags for use in OpenType layout-related methods
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum GTag {
    /// GPOS table, Glyph Positioning
    GPos,
    /// GSUB table, Glyph Substitutions
    GSub,
}

impl GTag {
    fn as_tag(self) -> Tag {
        Tag::new(match self {
            GTag::GPos => sys::HB_OT_TAG_GPOS,
            GTag::GSub => sys::HB_OT_TAG_GSUB,
        })
    }
}

// TODO: OTLayout/OTTable/OTScript/OTLanguage

/// A borrowed reference to a [`Face`]
#[derive(Copy, Clone)]
pub struct FaceRef<'a>(NonNull<sys::hb_face_t>, PhantomData<&'a sys::hb_face_t>);

impl<'a> FaceRef<'a> {
    unsafe fn from_raw(ptr: NonNull<sys::hb_face_t>) -> FaceRef<'a> {
        FaceRef(ptr, PhantomData)
    }

    fn as_ptr(self) -> *mut sys::hb_face_t {
        self.0.as_ptr()
    }

    /// Check whether this font has an OpenType math table
    pub fn has_ot_math_data(self) -> bool {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_ot_math_has_data(self.as_ptr()) != 0 }
    }

    /// Helper for retrieving OpenType layout information for this face
    pub fn ot_layout(self) -> ot::Layout<'a> {
        ot::Layout(self)
    }

    /// Get the graphite2 face for this font
    pub fn gr_face(self) -> Option<gr::FaceRef<'a>> {
        // SAFETY: Internal pointer guaranteed valid
        let ptr = unsafe { sys::hb_graphite2_face_get_gr_face(self.as_ptr()) };
        // SAFETY: If non-null, returned pointer is a valid (non-retained) graphite face reference.
        NonNull::new(ptr).map(|ptr| unsafe { gr::FaceRef::from_raw(ptr) })
    }
}

/// A borrowed mutable reference to a [`Face`]
pub struct FaceMut<'a>(FaceRef<'a>, PhantomData<&'a mut sys::hb_face_t>);

impl FaceMut<'_> {
    fn as_ptr_mut(&mut self) -> *mut sys::hb_face_t {
        self.0.as_ptr()
    }

    /// Set the index of this face. This has no effect on the face directly.
    pub fn set_index(&mut self, index: u32) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_face_set_index(self.as_ptr_mut(), index as libc::c_uint) }
    }

    /// Set the units-per-em for this face
    pub fn set_upem(&mut self, upem: u32) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_face_set_upem(self.as_ptr_mut(), upem as libc::c_uint) }
    }
}

/// A typographic face - a typeface combined with a style, loaded from a binary blob. Not yet
/// associated with a size or variation. See [`Font`].
pub struct Face(NonNull<sys::hb_face_t>);

impl Face {
    /// Create a new [`Face`], where it is easier to provide individual tables rather than a blob
    /// of all font data. Retrieving all table won't work for faces created this way by default.
    pub fn new_tables<T: Fn(FaceRef<'_>, Tag) -> Option<Blob> + 'static>(f: T) -> Face {
        unsafe extern "C" fn get_table<T: Fn(FaceRef<'_>, Tag) -> Option<Blob> + 'static>(
            face: *mut sys::hb_face_t,
            tag: sys::hb_tag_t,
            user_data: *mut (),
        ) -> *mut sys::hb_blob_t {
            // SAFETY: Precondition of this function - it is only called with a T user-data
            let f = unsafe { &*user_data.cast::<T>() };
            let face = NonNull::new(face).unwrap();
            // SAFETY: Harfbuzz guarantees to provide a valid non-retained face reference when this
            //         is called.
            let face = unsafe { FaceRef::from_raw(face) };
            match f(face, Tag(tag)) {
                Some(blob) => blob.0.as_ptr(),
                None => ptr::null_mut(),
            }
        }

        // SAFETY: The created face will only call get_table in valid ways. The passed closure will
        //         be deallocated as its same type by `dealloc` when the face is destroyed, the
        //         static bound ensures it may live however long that is.
        let face = unsafe {
            sys::hb_face_create_for_tables(
                get_table::<T>,
                Box::into_raw(Box::new(f)).cast(),
                Some(dealloc::<T>),
            )
        };

        Face(NonNull::new(face).unwrap())
    }

    /// Convert into a shared reference
    pub fn as_ref(&self) -> FaceRef<'_> {
        FaceRef(self.0, PhantomData)
    }

    /// Convert into a mutable reference
    pub fn as_mut(&mut self) -> FaceMut<'_> {
        FaceMut(self.as_ref(), PhantomData)
    }
}

impl Drop for Face {
    fn drop(&mut self) {
        // SAFETY: Internal pointer guaranteed valid, we own the pointer.
        unsafe { sys::hb_face_destroy(self.0.as_ptr()) }
    }
}

/// A borrowed reference to a [`Font`]
#[derive(Copy, Clone)]
pub struct FontRef<'a>(NonNull<sys::hb_font_t>, PhantomData<&'a sys::hb_font_t>);

impl<'a> FontRef<'a> {
    unsafe fn from_raw(ptr: NonNull<sys::hb_font_t>) -> FontRef<'a> {
        FontRef(ptr, PhantomData)
    }

    #[doc(hidden)]
    pub fn as_ptr(self) -> *mut sys::hb_font_t {
        self.0.as_ptr()
    }

    /// Get the [`Face`] for this font.
    pub fn face(self) -> FaceRef<'a> {
        // SAFETY: Internal pointer guaranteed valid
        let ptr = unsafe { sys::hb_font_get_face(self.as_ptr()) };
        FaceRef(NonNull::new(ptr).unwrap(), PhantomData)
    }

    /// Get the point size of this font
    pub fn ptem(self) -> f32 {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_font_get_ptem(self.as_ptr()) }
    }
}

/// A borrowed mutable reference to a [`Font`]
pub struct FontMut<'a>(FontRef<'a>, PhantomData<&'a mut sys::hb_font_t>);

impl FontMut<'_> {
    fn as_ptr_mut(&mut self) -> *mut sys::hb_font_t {
        self.0.as_ptr()
    }

    /// Set the horizontal and vertical scale of this font
    pub fn set_scale(&mut self, x: i32, y: i32) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_font_set_scale(self.as_ptr_mut(), x as libc::c_int, y as libc::c_int) }
    }

    /// Set the pixels-per-em of this font
    pub fn set_ppem(&mut self, x: u32, y: u32) {
        // SAFETY: Internal pointer guaranteed valid
        unsafe { sys::hb_font_set_ppem(self.as_ptr_mut(), x as libc::c_uint, y as libc::c_uint) }
    }

    /// Set the font functions associated with this font
    pub fn set_funcs<T>(&mut self, funcs: FontFuncsRef<'static, T>, data: T)
    where
        T: 'static,
    {
        let funcs = funcs.as_ptr();
        // SAFETY: Internal pointer guaranteed valid. FontFuncs implementation upholds the relevant
        //         invariants for ensuring all functions is contains will be valid. Data will be
        //         deallocated as its same type by `dealloc` when the font is destroyed, or when a
        //         new set of functions is set. The static bound ensures it may live that long.
        unsafe {
            sys::hb_font_set_funcs(
                self.as_ptr_mut(),
                funcs,
                Box::into_raw(Box::new(data)).cast(),
                Some(dealloc::<T>),
            )
        }
    }
}

/// A [`Face`] that has been associated with a specific size and, possibly, variation. Ready for
/// use in shaping.
pub struct Font(NonNull<sys::hb_font_t>);

impl Font {
    /// Create a new font from the given [`Face`].
    pub fn new(face: FaceRef<'_>) -> Font {
        // SAFETY: The pointer from FaceRef is guaranteed valid
        let ptr = unsafe { sys::hb_font_create(face.as_ptr()) };
        Font(NonNull::new(ptr).unwrap())
    }

    /// Convert into a shared reference
    pub fn as_ref(&self) -> FontRef<'_> {
        FontRef(self.0, PhantomData)
    }

    /// Convert into a mutable reference
    pub fn as_mut(&mut self) -> FontMut<'_> {
        FontMut(self.as_ref(), PhantomData)
    }
}

impl Drop for Font {
    fn drop(&mut self) {
        // SAFETY: Internal pointer guaranteed valid, we own the pointer.
        unsafe { sys::hb_font_destroy(self.0.as_ptr()) }
    }
}

/// A borrowed reference to a [`ShapePlan`]
pub struct ShapePlanRef<'a>(
    NonNull<sys::hb_shape_plan_t>,
    PhantomData<&'a sys::hb_shape_plan_t>,
);

impl ShapePlanRef<'_> {
    fn as_ptr(&self) -> *mut sys::hb_shape_plan_t {
        self.0.as_ptr()
    }

    /// Get the shaper that will be used for this [`ShapePlan`]. May be `None` if no shaper was
    /// provided and no default could be found, in which case attempting to shape text will likely
    /// fail.
    pub fn get_shaper(&self) -> Option<&CStr> {
        // SAFETY: Internal pointer guaranteed valid
        let ptr = unsafe { sys::hb_shape_plan_get_shaper(self.as_ptr()) };
        // FIXME(CraftSpider): See BufferRef::get_glyph_info
        if ptr.is_null() {
            None
        } else {
            // SAFETY: The returned pointer is guaranteed valid for as long as ShapePlan isn't updated.
            Some(unsafe { CStr::from_ptr(ptr) })
        }
    }
}

/// A borrowed mutable reference to a [`ShapePlan`]
pub struct ShapePlanMut<'a>(ShapePlanRef<'a>, PhantomData<&'a mut sys::hb_shape_plan_t>);

impl ShapePlanMut<'_> {
    fn as_ptr_mut(&mut self) -> *mut sys::hb_shape_plan_t {
        self.0.as_ptr()
    }

    /// Shape a buffer using the provided [`Font`] and [`Feature`]s
    pub fn execute(
        &mut self,
        font: FontRef<'_>,
        mut buffer: BufferMut<'_>,
        features: &[Feature],
    ) -> bool {
        // SAFETY: Internal pointer guaranteed valid. The pointers from font and buffer are
        //         similarly guaranteed valid. Features will not be read past len and will not be
        //         used after this call.
        unsafe {
            sys::hb_shape_plan_execute(
                self.as_ptr_mut(),
                font.as_ptr(),
                buffer.as_ptr_mut(),
                features.as_ptr(),
                features.len() as libc::c_uint,
            ) != 0
        }
    }
}

impl<'a> Deref for ShapePlanMut<'a> {
    type Target = ShapePlanRef<'a>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// A shaping plan, information about how Harfbuzz will shape a text segment based on the segment's
/// properties.
pub struct ShapePlan(NonNull<sys::hb_shape_plan_t>);

impl ShapePlan {
    /// Create a new shaping plan for a given [`Face`], [`Feature`]s, and [`SegmentProperties`].
    ///
    /// The shaper list will be the list of shapers to try. If `None`, then Harfbuzz will attempt
    /// to locate a default shaper to use.
    pub fn new(
        face: FaceRef<'_>,
        props: &SegmentProperties,
        features: &[Feature],
        shaper_list: Option<&[*const libc::c_char]>,
    ) -> ShapePlan {
        if let Some(list) = shaper_list {
            assert!(list.last().unwrap().is_null());
        }
        // SAFETY: Face and features pointers are guaranteed valid. The features will not be read
        //         past their provided length. shaper_list is guaranteed to end with a null if Some
        //         by above assert.
        let ptr = unsafe {
            sys::hb_shape_plan_create(
                face.as_ptr(),
                props,
                features.as_ptr(),
                features.len() as libc::c_uint,
                shaper_list.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()),
            )
        };
        ShapePlan(NonNull::new(ptr).unwrap())
    }

    /// Create a new shape plan for a given [`Face`], [`Feature`]s, and [`SegmentProperties`]. This
    /// shape plan is cached by Harfbuzz, which can make creation faster if the same arguments
    /// are expected to be used frequently.
    pub fn new_cached(
        face: FaceRef<'_>,
        props: &SegmentProperties,
        features: &[Feature],
        shaper_list: Option<&[*const libc::c_char]>,
    ) -> ShapePlan {
        if let Some(list) = shaper_list {
            assert!(list.last().unwrap().is_null());
        }
        // SAFETY: Face and features pointers are guaranteed valid. The features will not be read
        //         past their provided length. shaper_list is guaranteed to end with a null if Some
        //         by above assert.
        let ptr = unsafe {
            sys::hb_shape_plan_create_cached(
                face.as_ptr(),
                props,
                features.as_ptr(),
                features.len() as libc::c_uint,
                shaper_list.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()),
            )
        };
        ShapePlan(NonNull::new(ptr).unwrap())
    }

    /// Convert into a shared reference
    pub fn as_ref(&self) -> ShapePlanRef<'_> {
        ShapePlanRef(self.0, PhantomData)
    }

    /// Convert into a mutable reference
    pub fn as_mut(&mut self) -> ShapePlanMut<'_> {
        ShapePlanMut(self.as_ref(), PhantomData)
    }
}

impl Drop for ShapePlan {
    fn drop(&mut self) {
        // SAFETY: Internal pointer guaranteed valid, we own the pointer.
        unsafe { sys::hb_shape_plan_destroy(self.0.as_ptr()) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_util::{get_font_funcs, test_faces};

    #[test]
    fn test_tag() {
        let raw = u32::from_be_bytes(*b"test");

        assert_eq!(Tag::new(raw).to_raw(), raw);
        assert_eq!(Tag::from_str("test"), Tag::new(raw));
        assert_eq!(Tag::from_cstr(c"test"), Tag::new(raw));
    }

    #[test]
    fn test_language() {
        assert_eq!(Language::from_string("en-US").to_string(), Some(c"en-us"));
        assert_eq!(Language::from_cstr(c"en-gb").to_string(), Some(c"en-gb"));
        assert!(Language::default().to_string().is_none());
    }

    #[test]
    fn test_face_ot_math_data() {
        for (_, face) in test_faces() {
            assert!(!face.as_ref().has_ot_math_data())
        }
    }

    #[test]
    fn test_face_ot_layout() {
        for (_, face) in test_faces() {
            let layout = face.as_ref().ot_layout();

            let size = layout.size_params().unwrap();
            assert_eq!(size.design_size, 120);
            assert_eq!(size.start, 110);
            assert_eq!(size.end, 140);
            assert_eq!(size.subfamily_id, 1);
            assert_eq!(size.subfamily_name_id, 256);
        }
    }

    #[test]
    fn test_font() {
        for (_, face) in test_faces() {
            let font = Font::new(face.as_ref());

            assert_eq!(font.as_ref().ptem(), 0.0);
            assert_eq!(font.as_ref().face().0, face.0);
        }
    }

    #[test]
    fn test_shape() {
        for (ft_face, face) in test_faces() {
            let mut font = Font::new(face.as_ref());
            font.as_mut().set_funcs(get_font_funcs(), ft_face);

            let mut buffer = Buffer::new();
            buffer
                .as_mut()
                .add_utf16(&"Hello World!".encode_utf16().collect::<Vec<_>>());
            buffer.as_mut().guess_segment_properties();

            let plan1 = ShapePlan::new(
                face.as_ref(),
                &buffer.as_ref().get_segment_properties(),
                &[],
                None,
            );
            let plan2 = ShapePlan::new_cached(
                face.as_ref(),
                &buffer.as_ref().get_segment_properties(),
                &[],
                None,
            );

            for mut plan in [plan1, plan2] {
                assert_eq!(plan.as_ref().get_shaper(), Some(c"ot"));

                assert!(plan.as_mut().execute(font.as_ref(), buffer.as_mut(), &[]));

                assert_eq!(
                    buffer.as_ref().get_script().get_horizontal_direction(),
                    Direction::Ltr
                );

                let glyph_info = buffer.as_ref().glyph_info().unwrap();
                assert_eq!(glyph_info.len(), 12);
                assert_eq!(glyph_info[0].cluster, 0);
                assert_eq!(glyph_info[0].codepoint, 62);

                assert_eq!(glyph_info[1].cluster, 1);
                assert_eq!(glyph_info[1].codepoint, 50);

                let glyph_pos = buffer.as_ref().glyph_positions().unwrap();
                assert_eq!(glyph_pos.len(), 12);
                assert_eq!(glyph_pos[0].x_advance, 734);
                assert_eq!(glyph_pos[0].y_advance, 0);
                assert_eq!(glyph_pos[0].x_offset, 0);
                assert_eq!(glyph_pos[0].y_offset, 0);

                assert_eq!(glyph_pos[1].x_advance, 435);
                assert_eq!(glyph_pos[1].y_advance, 0);
                assert_eq!(glyph_pos[1].x_offset, 0);
                assert_eq!(glyph_pos[1].y_offset, 0);

                buffer.as_mut().reset();
                buffer
                    .as_mut()
                    .add_utf16(&"Hello World!".encode_utf16().collect::<Vec<_>>());
                buffer.as_mut().guess_segment_properties();
            }
        }
    }
}