fortplot_tt_binary.f90 Source File


Source Code

module fortplot_tt_binary
    !! Big-endian binary readers for TrueType font parsing.
    !! Fortran equivalents of stb_truetype.h macros: ttBYTE, ttUSHORT, ttSHORT, ttULONG.
    !! All offsets are 0-based to match the TTF specification directly.
    use, intrinsic :: iso_fortran_env, only: int8
    implicit none

    private
    public :: tt_byte, tt_ushort, tt_short, tt_ulong
    public :: tt_tag_match, tt_load_file

contains

    pure function tt_byte(data, offset) result(val)
        !! Read one unsigned byte (0..255) at a 0-based offset.
        integer(int8), intent(in) :: data(:)
        integer, intent(in) :: offset
        integer :: val
        val = iand(int(data(offset + 1)), 255)
    end function tt_byte

    pure function tt_ushort(data, offset) result(val)
        !! Read big-endian unsigned 16-bit integer (0..65535).
        integer(int8), intent(in) :: data(:)
        integer, intent(in) :: offset
        integer :: val
        val = ishft(tt_byte(data, offset), 8) + tt_byte(data, offset + 1)
    end function tt_ushort

    pure function tt_short(data, offset) result(val)
        !! Read big-endian signed 16-bit integer (-32768..32767).
        integer(int8), intent(in) :: data(:)
        integer, intent(in) :: offset
        integer :: val
        val = tt_ushort(data, offset)
        if (val >= 32768) val = val - 65536
    end function tt_short

    pure function tt_ulong(data, offset) result(val)
        !! Read big-endian unsigned 32-bit integer via bit operations.
        integer(int8), intent(in) :: data(:)
        integer, intent(in) :: offset
        integer :: val
        val = ior(ishft(tt_byte(data, offset), 24), &
                  ior(ishft(tt_byte(data, offset + 1), 16), &
                      ior(ishft(tt_byte(data, offset + 2), 8), &
                          tt_byte(data, offset + 3))))
    end function tt_ulong

    pure function tt_tag_match(data, offset, tag) result(match)
        !! Compare 4 bytes at offset to a character tag (e.g. "cmap", "head").
        integer(int8), intent(in) :: data(:)
        integer, intent(in) :: offset
        character(len=4), intent(in) :: tag
        logical :: match
        match = tt_byte(data, offset) == iachar(tag(1:1)) .and. &
                tt_byte(data, offset + 1) == iachar(tag(2:2)) .and. &
                tt_byte(data, offset + 2) == iachar(tag(3:3)) .and. &
                tt_byte(data, offset + 3) == iachar(tag(4:4))
    end function tt_tag_match

    function tt_load_file(filename, data, file_size) result(success)
        !! Load entire file into a byte array via stream I/O.
        character(len=*), intent(in) :: filename
        integer(int8), allocatable, intent(out) :: data(:)
        integer, intent(out) :: file_size
        logical :: success
        integer :: unit_num, ios
        logical :: exists

        success = .false.
        file_size = 0

        inquire(file=filename, exist=exists, size=file_size)
        if (.not. exists .or. file_size <= 0) return

        allocate(data(file_size))
      open(newunit=unit_num, file=filename, access='stream', &
              form='unformatted', status='old', iostat=ios)
        if (ios /= 0) then
            file_size = 0
            return
        end if

        read(unit_num, iostat=ios) data
        close(unit_num)

        if (ios /= 0) then
            file_size = 0
            return
        end if

        success = .true.
    end function tt_load_file

end module fortplot_tt_binary