github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/font_properties.go (about)

     1  package etxt
     2  
     3  import "golang.org/x/image/font/sfnt"
     4  import "sync/atomic"
     5  import "errors"
     6  
     7  var ErrNotFound = errors.New("font property not found or empty")
     8  
     9  // We allocate one sfnt.Buffer so it can be used in FontProperty() calls.
    10  // These buffers can't be used concurrently though, so sfntBuffer will only
    11  // be used if no one else is using it at the moment. We don't bother creating
    12  // a pool because that would be waaay overkill, and this simple trick already
    13  // makes calls to FontProperty() fast in 99% use-cases for a reasonably small
    14  // price.
    15  var sfntBuffer sfnt.Buffer
    16  var usingSfntBuffer uint32 = 0
    17  
    18  func getSfntBuffer() *sfnt.Buffer {
    19  	if !atomic.CompareAndSwapUint32(&usingSfntBuffer, 0, 1) {
    20  		return nil
    21  	}
    22  	return &sfntBuffer
    23  }
    24  func releaseSfntBuffer(buffer *sfnt.Buffer) {
    25  	if buffer != nil {
    26  		atomic.StoreUint32(&usingSfntBuffer, 0)
    27  	}
    28  }
    29  
    30  // Returns the requested font property for the given font.
    31  // The returned property string might be empty even when error is nil.
    32  func FontProperty(font *Font, property sfnt.NameID) (string, error) {
    33  	buffer := getSfntBuffer()
    34  	str, err := font.Name(buffer, property)
    35  	releaseSfntBuffer(buffer)
    36  	if err != nil {
    37  		return "", err
    38  	}
    39  	return str, nil
    40  }
    41  
    42  // Returns the family name of the given font. If the information is
    43  // missing, [ErrNotFound] will be returned. Other errors are also
    44  // possible (e.g., if the font naming table is invalid).
    45  func FontFamily(font *Font) (string, error) {
    46  	value, err := FontProperty(font, sfnt.NameIDFamily)
    47  	if err == sfnt.ErrNotFound || value == "" {
    48  		err = ErrNotFound
    49  	}
    50  	return value, err
    51  }
    52  
    53  // Returns the subfamily name of the given font. If the information
    54  // is missing, [ErrNotFound] will be returned. Other errors are also
    55  // possible (e.g., if the font naming table is invalid).
    56  //
    57  // In most cases, the subfamily value will be one of:
    58  //   - Regular, Italic, Bold, Bold Italic
    59  func FontSubfamily(font *Font) (string, error) {
    60  	value, err := FontProperty(font, sfnt.NameIDSubfamily)
    61  	if err == sfnt.ErrNotFound || value == "" {
    62  		err = ErrNotFound
    63  	}
    64  	return value, err
    65  }
    66  
    67  // Returns the name of the given font. If the information is missing,
    68  // [ErrNotFound] will be returned. Other errors are also possible (e.g.,
    69  // if the font naming table is invalid).
    70  func FontName(font *Font) (string, error) {
    71  	value, err := FontProperty(font, sfnt.NameIDFull)
    72  	if err == sfnt.ErrNotFound || value == "" {
    73  		err = ErrNotFound
    74  	}
    75  	return value, err
    76  }
    77  
    78  // Returns the identifier of the given font. If the information is missing,
    79  // [ErrNotFound] will be returned. Other errors are also possible (e.g.,
    80  // if the font naming table is invalid).
    81  func FontIdentifier(font *Font) (string, error) {
    82  	value, err := FontProperty(font, sfnt.NameIDUniqueIdentifier)
    83  	if err == sfnt.ErrNotFound || value == "" {
    84  		err = ErrNotFound
    85  	}
    86  	return value, err
    87  }
    88  
    89  // Returns the runes in the given text that can't be represented by the
    90  // font. If runes are repeated in the input text, the returned slice may
    91  // contain them multiple times too.
    92  //
    93  // If you load fonts dynamically, it is good practice to use this function
    94  // to make sure that the fonts include all the glyphs that you require.
    95  func GetMissingRunes(font *Font, text string) ([]rune, error) {
    96  	buffer := getSfntBuffer()
    97  	defer releaseSfntBuffer(buffer)
    98  
    99  	missing := make([]rune, 0)
   100  	for _, codePoint := range text {
   101  		index, err := font.GlyphIndex(buffer, codePoint)
   102  		if err != nil {
   103  			return missing, err
   104  		}
   105  		if index == 0 {
   106  			missing = append(missing, codePoint)
   107  		}
   108  	}
   109  	return missing, nil
   110  }