/// Returns a `cmap` format 4 subtable representing the given segments.
///
/// Note that this function does not automatically append a sentinel segment at the end of the segments vector.
/// If such a sentinel segment is desired it must be provided as the last element of `segments`.
///
/// # Example
///
/// ```
/// # use informa::ctx::Context;
/// # use informa::data::CharacterMap;
/// # use informa::sfnt::tables::cmap::format4::compile_segments;
/// # use informa::sfnt::tables::cmap::format4::encoding_segments;
/// # use informa::sfnt::tables::cmap::format4::Segment;
/// let ctx = Context::default();
/// let mut map = CharacterMap::new();
/// // ... insert entries into character map ...
/// # map.insert('A', 1);
/// # map.insert('B', 2);
/// # map.insert('C', 3);
/// let mut segments = encoding_segments(&map, &ctx);
/// segments.push(Segment::sentinel());
///
/// let subtable = compile_segments(&map, &segments);
/// ```
Returns a `cmap` format 4 subtable representing the given segments.
Note that this function does not automatically append a sentinel segment at the end of the segments vector.
If such a sentinel segment is desired it must be provided as the last element of `segments`.
Returns a ` cmap ` format 4 subtable representing the given segments . Note that this function does not automatically append a sentinel segment at the end of the segments vector . If such a sentinel segment is desired it must be provided as the last element of ` segments ` .
pub fn compile_segments(map: &CharacterMap, segments: &[Segment]) -> Bytes {
    let length = CONSTANT_SIZE + segments.iter().map(Segment::size).sum::<usize>();
    let mut buf = BytesMut::with_capacity(length);
    let format = 4;
    buf.put_u16(format);
    buf.put_u16(length as u16);
    let language = 0;
    buf.put_u16(language);
    let seg_count = segments.len() as u16;
    let seg_count_x2 = seg_count * 2;
    buf.put_u16(seg_count_x2);
    let search_range: u16 = 2 * 2u16.pow((seg_count as f64).log2().floor() as u32);
    buf.put_u16(search_range);
    let entry_selector = ((search_range as f64) / 2f64).log2().floor() as u16;
    buf.put_u16(entry_selector);
    let range_shift = seg_count_x2 - search_range;
    buf.put_u16(range_shift);
    // endCode
    for segment in segments {
        buf.put_u16(segment.end as u16);
    }
    let reserved_pad = 0;
    buf.put_u16(reserved_pad);
    // startCode
    for segment in segments {
        buf.put_u16(segment.start as u16);
    }
    // idDelta
    for segment in segments {
        match segment.strategy {
            SegmentEncodingStrategy::Shift => {
                let start_char = segment.start;
                // Map to glyph id `0` (`.notdef`) if no mapping is defined for the character.
                // This is required to correctly map U+FFFF, which is used as a sentinel segment but does not have any mapping defined in `map`.
                // See also [`Segment::sentinel`].
                let start_gid = map.get(&start_char).map_or(0, |&x| x) as i32;
                let start_code = start_char as i32;
                let id_delta = start_gid.wrapping_sub(start_code) as i16;
                buf.put_i16(id_delta);
            }
            SegmentEncodingStrategy::Mix => {
                buf.put_i16(0);
            }
        }
    }
    let mut glyph_id_array_offset: usize = GLYPH_ID_ARRAY_FIELD_SIZE;
    // idRangeOffset
    for (index, segment) in segments.iter().enumerate() {
        match segment.strategy {
            SegmentEncodingStrategy::Shift => {
                buf.put_u16(0);
            }
            SegmentEncodingStrategy::Mix => {
                // buf:
                //
                //      index                                   id_range_offset
                //        ↓                                            ↓
                // ...[P][S][ ][ ][ ][ ][ ][ ][A][A][A][A][A][A][A][A][ ][ ]...
                // \_________________________/\_________________________/
                //     idRangeOffset block       glyph_id_array_offset
                //          \________________/\_______________________________/
                //      remaining_id_range_offset    glyphIdArray block
                //
                //     P = preceding `segment`
                //     S = current `segment`
                //     A = glyphIdArray field reserved by a preceding segment
                //         (just reserved, set in `glyphIdArray` loop below)
                let remaining_id_range_offset =
                    (segments.len() - index - 1) * ID_RANGE_OFFSET_FIELD_SIZE;
                let id_range_offset = (glyph_id_array_offset + remaining_id_range_offset) as u16;
                buf.put_u16(id_range_offset);
                glyph_id_array_offset += segment.len() * GLYPH_ID_ARRAY_FIELD_SIZE;
            }
        }
    }
    // glyphIdArray
    for segment in segments
        .iter()
        .filter(|x| x.strategy == SegmentEncodingStrategy::Mix)
    {
        for code in segment.start..=segment.end {
            // `0` is used as a default value for the same reason as outlined in the `idDelta` loop above
            let gid = map.get(&code).map_or(0, |x| *x) as u16;
            buf.put_u16(gid);
        }
    }
    assert_eq!(length, buf.len());
    buf.freeze()
}
pub fn compile_segments ( map : & CharacterMap , segments : & [ Segment ] ) -> Bytes { let length = CONSTANT_SIZE + segments . iter ( ) . map ( Segment :: size ) . sum :: < usize > ( ) ; let mut buf = BytesMut :: with_capacity ( length ) ; let format = 4 ; buf . put_u16 ( format ) ; buf . put_u16 ( length as u16 ) ; let language = 0 ; buf . put_u16 ( language ) ; let seg_count = segments . len ( ) as u16 ; let seg_count_x2 = seg_count * 2 ; buf . put_u16 ( seg_count_x2 ) ; let search_range : u16 = 2 * 2u16 . pow ( ( seg_count as f64 ) . log2 ( ) . floor ( ) as u32 ) ; buf . put_u16 ( search_range ) ; let entry_selector = ( ( search_range as f64 ) / 2f64 ) . log2 ( ) . floor ( ) as u16 ; buf . put_u16 ( entry_selector ) ; let range_shift = seg_count_x2 - search_range ; buf . put_u16 ( range_shift ) ; // endCode for segment in segments { buf . put_u16 ( segment . end as u16 ) ; } let reserved_pad = 0 ; buf . put_u16 ( reserved_pad ) ; // startCode for segment in segments { buf . put_u16 ( segment . start as u16 ) ; } // idDelta for segment in segments { match segment . strategy { SegmentEncodingStrategy :: Shift => { let start_char = segment . start ; // Map to glyph id `0` (`.notdef`) if no mapping is defined for the character. // This is required to correctly map U+FFFF, which is used as a sentinel segment but does not have any mapping defined in `map`. // See also [`Segment::sentinel`]. let start_gid = map . get ( & start_char ) . map_or ( 0 , | & x | x ) as i32 ; let start_code = start_char as i32 ; let id_delta = start_gid . wrapping_sub ( start_code ) as i16 ; buf . put_i16 ( id_delta ) ; } SegmentEncodingStrategy :: Mix => { buf . put_i16 ( 0 ) ; } } } let mut glyph_id_array_offset : usize = GLYPH_ID_ARRAY_FIELD_SIZE ; // idRangeOffset for ( index , segment ) in segments . iter ( ) . enumerate ( ) { match segment . strategy { SegmentEncodingStrategy :: Shift => { buf . put_u16 ( 0 ) ; } SegmentEncodingStrategy :: Mix => { // buf: // //      index                                   id_range_offset //        ↓                                            ↓ // ...[P][S][ ][ ][ ][ ][ ][ ][A][A][A][A][A][A][A][A][ ][ ]... // \_________________________/\_________________________/ //     idRangeOffset block       glyph_id_array_offset //          \________________/\_______________________________/ //      remaining_id_range_offset    glyphIdArray block // //     P = preceding `segment` //     S = current `segment` //     A = glyphIdArray field reserved by a preceding segment //         (just reserved, set in `glyphIdArray` loop below) let remaining_id_range_offset = ( segments . len ( ) - index - 1 ) * ID_RANGE_OFFSET_FIELD_SIZE ; let id_range_offset = ( glyph_id_array_offset + remaining_id_range_offset ) as u16 ; buf . put_u16 ( id_range_offset ) ; glyph_id_array_offset += segment . len ( ) * GLYPH_ID_ARRAY_FIELD_SIZE ; } } } // glyphIdArray for segment in segments . iter ( ) . filter ( | x | x . strategy == SegmentEncodingStrategy :: Mix ) { for code in segment . start ..= segment . end { // `0` is used as a default value for the same reason as outlined in the `idDelta` loop above let gid = map . get ( & code ) . map_or ( 0 , | x | * x ) as u16 ; buf . put_u16 ( gid ) ; } } assert_eq ! ( length buf len ( ) ) ; buf . freeze ( ) }
Returns a `cmap` format 4 subtable representing the given segments.
Returns a ` cmap ` format 4 subtable representing the given segments .
map
&CharacterMap
segments
&[Segment]
example
None
use informa::ctx::context;
None
use informa::data::charactermap;
None
use informa::sfnt::tables::cmap::format4::compile_segments;
None
use informa::sfnt::tables::cmap::format4::encoding_segments;
None
use informa::sfnt::tables::cmap::format4::segment;
let ctx = Context::default();
let mut map = CharacterMap::new();
insert entries into character map
let ctx = Context :: default () ; let mut map = CharacterMap :: new () ; insert entries into character map
map.insert('a', 1);
None
map.insert('b', 2);
None
map.insert('c', 3);
let mut segments = encoding_segments(↦, &ctx);
segments.push(Segment::sentinel()).

let subtable = compile_segments(↦, &segments);
```
let mut segments = encoding_segments ( ↦ &ctx ) ; segments . push ( Segment :: sentinel () ) . let subtable = compile_segments ( ↦ &segments ) ; ` ` `