diff --git a/lib/Font/FreeType.rakumod b/lib/Font/FreeType.rakumod index 525422a..50568a3 100644 --- a/lib/Font/FreeType.rakumod +++ b/lib/Font/FreeType.rakumod @@ -1,91 +1,90 @@ -use v6; - -class Font::FreeType:ver<0.5.9> { - use NativeCall; - use Font::FreeType::Face; - use Font::FreeType::Error; - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - use Method::Also; - - has FT_Library $.raw; - our $lock = Lock.new; - - submethod BUILD { - my $p = Pointer[FT_Library].new; - ft-try { FT_Init_FreeType( $p ); }; - $!raw = $p.deref; - } - method native is also is DEPRECATED("Please use the 'raw' method") { $!raw } - - submethod DESTROY { - $lock.protect: { - with $!raw { - ft-try { .FT_Done_FreeType }; - } - } - } - - multi method face(::?CLASS:U \class: |c --> Font::FreeType::Face:D) is hidden-from-backtrace { - class.new.face(|c); - } +unit class Font::FreeType:ver<0.5.9>; + +use NativeCall; +use Font::FreeType::Face; +use Font::FreeType::Error; +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; +use Method::Also; + +has FT_Library $.raw; +our $lock = Lock.new; + +submethod BUILD { + my $p = Pointer[FT_Library].new; + ft-try { FT_Init_FreeType( $p ); }; + $!raw = $p.deref; +} +method native is also is DEPRECATED("Please use the 'raw' method") { $!raw } - multi method face(::?CLASS:D $ft-lib: - IO:D() $file-io, - Int :$index = 0, - |c --> Font::FreeType::Face:D - ) is hidden-from-backtrace { - my $p = Pointer[FT_Face].new; - $lock.protect: sub () is hidden-from-backtrace { - CATCH { - when Font::FreeType::Error { - .details = "loading file '{$file-io.path}'"; - .rethrow; - } - } - ft-try { $!raw.FT_New_Face($file-io.path, $index, $p); }; +submethod DESTROY { + $lock.protect: { + with $!raw { + ft-try { .FT_Done_FreeType }; } - my FT_Face:D $raw = $p.deref; - Font::FreeType::Face.new: :$raw, :$ft-lib, |c; } +} - multi method face(::?CLASS:D $ft-lib: - Blob:D $buf, - Int :$size = $buf.bytes, - Int :$index = 0, - |c --> Font::FreeType::Face:D - ) is hidden-from-backtrace { - my $p = Pointer[FT_Face].new; - ft-try(sub () is hidden-from-backtrace { - $!raw.FT_New_Memory_Face($buf, $size, $index, $p); - }); - my FT_Face:D $raw = $p.deref; - Font::FreeType::Face.new: :$raw, :$ft-lib, |c; - } +multi method face(::?CLASS:U \class: |c --> Font::FreeType::Face:D) is hidden-from-backtrace { + class.new.face(|c); +} - multi method face(::?CLASS:D: - IO::Handle:D $fh, - |c --> Font::FreeType::Face:D - ) is hidden-from-backtrace { +multi method face(::?CLASS:D $ft-lib: + IO:D() $file-io, + Int :$index = 0, + |c --> Font::FreeType::Face:D + ) is hidden-from-backtrace { + my $p = Pointer[FT_Face].new; + $lock.protect: sub () is hidden-from-backtrace { CATCH { when Font::FreeType::Error { - .details = "loading IO handle '{$fh.path}'"; + .details = "loading file '{$file-io.path}'"; .rethrow; } } - $fh.seek(0, SeekFromBeginning); - $.face($fh.slurp(:bin), |c); + ft-try { $!raw.FT_New_Face($file-io.path, $index, $p); }; } + my FT_Face:D $raw = $p.deref; + Font::FreeType::Face.new: :$raw, :$ft-lib, |c; +} - multi method version(::?CLASS:U:) { - self.new.version; - } - multi method version(::?CLASS:D:) returns Version:D { - $!raw.FT_Library_Version(my FT_Int $major, my FT_Int $minor, my FT_Int $patch); - Version.new: "{$major}.{$minor}.{$patch}"; +multi method face(::?CLASS:D $ft-lib: + Blob:D $buf, + Int :$size = $buf.bytes, + Int :$index = 0, + |c --> Font::FreeType::Face:D + ) is hidden-from-backtrace { + my $p = Pointer[FT_Face].new; + ft-try(sub () is hidden-from-backtrace { + $!raw.FT_New_Memory_Face($buf, $size, $index, $p); + }); + my FT_Face:D $raw = $p.deref; + Font::FreeType::Face.new: :$raw, :$ft-lib, |c; +} + +multi method face(::?CLASS:D: + IO::Handle:D $fh, + |c --> Font::FreeType::Face:D + ) is hidden-from-backtrace { + CATCH { + when Font::FreeType::Error { + .details = "loading IO handle '{$fh.path}'"; + .rethrow; + } } + $fh.seek(0, SeekFromBeginning); + $.face($fh.slurp(:bin), |c); +} + +multi method version(::?CLASS:U:) { + self.new.version; +} +multi method version(::?CLASS:D:) returns Version:D { + $!raw.FT_Library_Version(my FT_Int $major, my FT_Int $minor, my FT_Int $patch); + Version.new: "{$major}.{$minor}.{$patch}"; } + =begin pod =head1 class Font::FreeType - Raku FreeType2 Library Instance diff --git a/lib/Font/FreeType/BBox.rakumod b/lib/Font/FreeType/BBox.rakumod index 2ca1519..0e5a375 100644 --- a/lib/Font/FreeType/BBox.rakumod +++ b/lib/Font/FreeType/BBox.rakumod @@ -1,42 +1,41 @@ #| A generic font or glyph bounding box -class Font::FreeType::BBox - is Array { - - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - - constant Dot6 = Font::FreeType::Raw::Defs::Dot6; - has Numeric $.x-scale = 1 / Dot6; - has Numeric $.y-scale = 1 / Dot6; - - method x-min is rw { self[0] } - method y-min is rw { self[1] } - method x-max is rw { self[2] } - method y-max is rw { self[3] } - method width { self[2] - self[0] } - method height { self[3] - self[1] } - - multi method new(FT_BBox:D :bbox($_)!, |c) { - my \bbox = self.bless: | c; - bbox[0] = .x-min * bbox.x-scale; - bbox[1] = .y-min * bbox.y-scale; - bbox[2] = .x-max * bbox.x-scale; - bbox[3] = .y-max * bbox.y-scale; - bbox; - } - - multi method new(Array:D :bbox($_)! where .elems == 4, |c) { - my \bbox = self.bless: | c; - bbox[0] = .[0] * bbox.x-scale; - bbox[1] = .[1] * bbox.y-scale; - bbox[2] = .[2] * bbox.x-scale; - bbox[3] = .[3] * bbox.y-scale; - bbox; - } +unit class Font::FreeType::BBox + is Array; + +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; + +constant Dot6 = Font::FreeType::Raw::Defs::Dot6; +has Numeric $.x-scale = 1 / Dot6; +has Numeric $.y-scale = 1 / Dot6; + +method x-min is rw { self[0] } +method y-min is rw { self[1] } +method x-max is rw { self[2] } +method y-max is rw { self[3] } +method width { self[2] - self[0] } +method height { self[3] - self[1] } + +multi method new(FT_BBox:D :bbox($_)!, |c) { + my \bbox = self.bless: | c; + bbox[0] = .x-min * bbox.x-scale; + bbox[1] = .y-min * bbox.y-scale; + bbox[2] = .x-max * bbox.x-scale; + bbox[3] = .y-max * bbox.y-scale; + bbox; +} - multi method new(|c) { fail } +multi method new(Array:D :bbox($_)! where .elems == 4, |c) { + my \bbox = self.bless: | c; + bbox[0] = .[0] * bbox.x-scale; + bbox[1] = .[1] * bbox.y-scale; + bbox[2] = .[2] * bbox.x-scale; + bbox[3] = .[3] * bbox.y-scale; + bbox; } +multi method new(|c) { fail } + =begin pod =head2 Synopsis diff --git a/lib/Font/FreeType/BitMap.rakumod b/lib/Font/FreeType/BitMap.rakumod index f922ee3..9b1a650 100644 --- a/lib/Font/FreeType/BitMap.rakumod +++ b/lib/Font/FreeType/BitMap.rakumod @@ -1,106 +1,104 @@ #| Bitmaps from rendered glyphs -class Font::FreeType::BitMap { +unit class Font::FreeType::BitMap; - use NativeCall; - use Font::FreeType::Error; - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; +use NativeCall; +use Font::FreeType::Error; +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; - constant Dot6 = Font::FreeType::Raw::Defs::Dot6; - constant Dpi = 72.0; +constant Dot6 = Font::FreeType::Raw::Defs::Dot6; +constant Dpi = 72.0; - has $.face; - has FT_Bitmap $!raw handles ; - has Int $.left is required; - has Int $.top is required; - has FT_ULong $.char-code is required; +has $.face; +has FT_Bitmap $!raw handles ; +has Int $.left is required; +has Int $.top is required; +has FT_ULong $.char-code is required; - submethod TWEAK(FT_Bitmap:D :$!raw!) { - $!top *= 3 - if $!raw.pixel-mode == +FT_PIXEL_MODE_LCD_V; - } +submethod TWEAK(FT_Bitmap:D :$!raw!) { + $!top *= 3 + if $!raw.pixel-mode == +FT_PIXEL_MODE_LCD_V; +} - method !library(--> FT_Library:D) { - $!face.ft-lib.raw; - } +method !library(--> FT_Library:D) { + $!face.ft-lib.raw; +} - method size returns Rat:D { $!raw.size / Dot6 } - multi method x-res(:$ppem! where .so) returns Rat:D { $!raw.x-ppem / Dot6 } - multi method x-res(:$dpi! where .so) returns Rat:D { Dpi/Dot6 * $!raw.x-ppem / self.size } - multi method y-res(:$ppem! where .so) returns Rat:D { $!raw.y-ppem / Dot6 } - multi method y-res(:$dpi! where .so) returns Rat:D { Dpi/Dot6 * $!raw.y-ppem / self.size } +method size returns Rat:D { $!raw.size / Dot6 } +multi method x-res(:$ppem! where .so) returns Rat:D { $!raw.x-ppem / Dot6 } +multi method x-res(:$dpi! where .so) returns Rat:D { Dpi/Dot6 * $!raw.x-ppem / self.size } +multi method y-res(:$ppem! where .so) returns Rat:D { $!raw.y-ppem / Dot6 } +multi method y-res(:$dpi! where .so) returns Rat:D { Dpi/Dot6 * $!raw.y-ppem / self.size } - method convert(UInt :$alignment = 1 --> ::?CLASS:D) { - my FT_Bitmap $target .= new; - ft-try { self!library.FT_Bitmap_Convert($!raw, $target, $alignment); }; - self.new: :$!face, :raw($target), :$!left, :$!top; - } +method convert(UInt :$alignment = 1 --> ::?CLASS:D) { + my FT_Bitmap $target .= new; + ft-try { self!library.FT_Bitmap_Convert($!raw, $target, $alignment); }; + self.new: :$!face, :raw($target), :$!left, :$!top; +} - method depth returns UInt:D { - constant @BitsPerPixel = [Mu, 1, 8, 2, 4, 8, 8, 24]; - with $!raw.pixel-mode { - @BitsPerPixel[$_]; - } +method depth returns UInt:D { + constant @BitsPerPixel = [Mu, 1, 8, 2, 4, 8, 8, 24]; + with $!raw.pixel-mode { + @BitsPerPixel[$_]; } +} - method !get-pixel-buf(Bool :$color = False --> buf8) { - my buf8 $pixels .= allocate($.depth * $.rows * $.width); - ft-try { $!raw.get-pixels($pixels); }; - $pixels; - } +method !get-pixel-buf(Bool :$color = False --> buf8) { + my buf8 $pixels .= allocate($.depth * $.rows * $.width); + ft-try { $!raw.get-pixels($pixels); }; + $pixels; +} - method pixels returns array[uint8] { - my uint8 @pixels[$.rows;$.width] Z= self!get-pixel-buf; - @pixels; - } +method pixels returns array[uint8] { + my uint8 @pixels[$.rows;$.width] Z= self!get-pixel-buf; + @pixels; +} - method Str { - return "\n" x $.rows - unless $.width; - constant on = '#'.ord; - constant off = ' '.ord; - my buf8 $row .= allocate($.width); - my $pixbuf = $.pixels; - my Str @lines; - for ^$.rows -> $y { - for ^$.width -> $x { - $row[$x] = $pixbuf[$y;$x] ?? on !! off; - } - @lines.push: $row.decode("latin-1"); +method Str { + return "\n" x $.rows + unless $.width; + constant on = '#'.ord; + constant off = ' '.ord; + my buf8 $row .= allocate($.width); + my $pixbuf = $.pixels; + my Str @lines; + for ^$.rows -> $y { + for ^$.width -> $x { + $row[$x] = $pixbuf[$y;$x] ?? on !! off; } - @lines.join: "\n"; - } - - # create pgm - Netpbm grayscale image format - method pgm returns Buf { - my $pixels = self.pixels; - my UInt ($ht, $wd) = $pixels.shape.list; - my buf8 $buf .= new: "P5\n$wd $ht\n255\n".encode('latin-1'); - $buf.append: $pixels.list; - $buf; + @lines.push: $row.decode("latin-1"); } + @lines.join: "\n"; +} - method clone returns ::?CLASS:D { - return self unless self.defined; - my $bitmap = $!raw.clone(self!library); - self.new: :$!face, :raw($bitmap), :$!top, :$!left; - } +# create pgm - Netpbm grayscale image format +method pgm returns Buf { + my $pixels = self.pixels; + my UInt ($ht, $wd) = $pixels.shape.list; + my buf8 $buf .= new: "P5\n$wd $ht\n255\n".encode('latin-1'); + $buf.append: $pixels.list; + $buf; +} - method DESTROY { - ft-try { self!library.FT_Bitmap_Done($!raw) }; - $!raw = Nil; - } +method clone returns ::?CLASS:D { + return self unless self.defined; + my $bitmap = $!raw.clone(self!library); + self.new: :$!face, :raw($bitmap), :$!top, :$!left; +} - class Size { - submethod BUILD(:$!raw) { $!raw .= clone; } - has FT_Bitmap_Size $!raw is required handles ; - method size { $!raw.size / Dot6 } - multi method x-res(:$ppem! where .so) { $!raw.x-ppem / Dot6 } - multi method x-res(:$dpi! where .so) { $!raw.x-ppem * Dpi / (self.size * Dot6) } - multi method y-res(:$ppem! where .so) { $!raw.y-ppem / Dot6 } - multi method y-res(:$dpi! where .so) { $!raw.y-ppem* Dpi / (self.size * Dot6) } - } +method DESTROY { + ft-try { self!library.FT_Bitmap_Done($!raw) }; + $!raw = Nil; +} +class Size { + submethod BUILD(:$!raw) { $!raw .= clone; } + has FT_Bitmap_Size $!raw is required handles ; + method size { $!raw.size / Dot6 } + multi method x-res(:$ppem! where .so) { $!raw.x-ppem / Dot6 } + multi method x-res(:$dpi! where .so) { $!raw.x-ppem * Dpi / (self.size * Dot6) } + multi method y-res(:$ppem! where .so) { $!raw.y-ppem / Dot6 } + multi method y-res(:$dpi! where .so) { $!raw.y-ppem* Dpi / (self.size * Dot6) } } =begin pod diff --git a/lib/Font/FreeType/CharMap.rakumod b/lib/Font/FreeType/CharMap.rakumod index 8a383d5..5d682e8 100644 --- a/lib/Font/FreeType/CharMap.rakumod +++ b/lib/Font/FreeType/CharMap.rakumod @@ -1,13 +1,13 @@ #| Character map from font typefaces -class Font::FreeType::CharMap { - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - - has $.face is required; # to prevent GC - has FT_CharMap $!raw handles ; - submethod TWEAK(:$!raw!) { } - method encoding returns UInt:D { FT_ENCODING($!raw.encoding) } -} +unit class Font::FreeType::CharMap; + +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; + +has $.face is required; # to prevent GC +has FT_CharMap $!raw handles ; +submethod TWEAK(:$!raw!) { } +method encoding returns UInt:D { FT_ENCODING($!raw.encoding) } =begin pod diff --git a/lib/Font/FreeType/Error.rakumod b/lib/Font/FreeType/Error.rakumod index ba26e35..3f03549 100644 --- a/lib/Font/FreeType/Error.rakumod +++ b/lib/Font/FreeType/Error.rakumod @@ -1,151 +1,152 @@ -class Font::FreeType::Error is Exception { - use Font::FreeType::Raw::Defs; - - has Int $.error is required; - has Str $.details is rw; - - our @Messages is export(:Messages); - sub error-def(UInt $num, Str $message) { - @Messages[$num] = $message; - $num - } - our enum FT_ERROR is export(:FT_ERROR) ( - - # generic errors - - Ok => error-def(0x00, "no error"), - Cannot_Open_Resource => error-def(0x01, "cannot open resource"), - Unknown_File_Format => error-def(0x02, "unknown file format"), - Invalid_File_Format => error-def(0x03, "invalid file format"), - Invalid_Version => error-def(0x04, "invalid FreeType version"), - Lower_Module_Version=> error-def(0x05, "module version is too low"), - Invalid_Argument => error-def(0x06, "invalid argument"), - Unimplemented_Feature => error-def(0x07, "unimplemented feature"), - Invalid_Table => error-def(0x08, "corrupted table"), - Invalid_Offset => error-def(0x09, "invalid offset within table"), - Array_Too_Large => error-def(0xA, "array allocation size too large"), - Missing_Module => error-def(0xB, "missing module"), - Missing_Property => error-def(0xC, "missing property"), - - # glyph/character errors - - Invalid_Glyph_Index => error-def(0x10, "invalid glyph index"), - Invalid_Character_Code => error-def(0x11, "invalid character code"), - Invalid_Glyph_Format => error-def(0x12, "unsupported glyph image format"), - Cannot_Render_Glyph => error-def(0x13, "cannot render this glyph format"), - Invalid_Outline => error-def(0x14, "invalid outline"), - Invalid_Composite => error-def(0x15, "invalid composite"), - Too_Many_Hints => error-def(0x16, "too many hints"), - Invalid_Pixel_Size => error-def(0x17, "invalid pixel size"), - # handle errors - - Invalid_Handle => error-def(0x20, "invalid object handle" ), - Invalid_Library_Handle => error-def(0x21, "invalid library handle" ), - Invalid_Driver_Handle => error-def(0x22, "invalid module handle" ), - Invalid_Face_Handle => error-def(0x23, "invalid face handle" ), - Invalid_Size_Handle => error-def(0x24, "invalid size handle" ), - Invalid_Slot_Handle => error-def(0x25, "invalid glyph slot handle" ), - Invalid_CharMap_Handle => error-def(0x26, "invalid charmap handle" ), - Invalid_Cache_Handle => error-def(0x27, "invalid cache manager handle" ), - Invalid_Stream_Handle => error-def(0x28, "invalid stream handle" ), - # driver errors - - Too_Many_Drivers => error-def(0x30, "too many modules" ), - Too_Many_Extensions => error-def(0x31, "too many extensions" ), - - # memory errors - - Out_Of_Memory => error-def(0x40, "out of memory" ), - Unlisted_Object => error-def(0x41, "unlisted object" ), - - # stream errors - - Cannot_Open_Stream => error-def(0x51, "cannot open stream" ), - Invalid_Stream_Seek => error-def(0x52, "invalid stream seek" ), - Invalid_Stream_Skip => error-def(0x53, "invalid stream skip" ), - Invalid_Stream_Read => error-def(0x54, "invalid stream read" ), - Invalid_Stream_Operation => error-def(0x55, "invalid stream operation" ), - Invalid_Frame_Operation => error-def(0x56, "invalid frame operation" ), - Nested_Frame_Access => error-def(0x57, "nested frame access" ), - Invalid_Frame_Read => error-def(0x58, "invalid frame read" ), - - # raster errors - - Raster_Uninitialized => error-def(0x60, "raster uninitialized" ), - Raster_Corrupted => error-def(0x61, "raster corrupted" ), - Raster_Overflow => error-def(0x62, "raster overflow" ), - Raster_Negative_Height => error-def(0x63, "negative height while rastering" ), - - # cache errors - - Too_Many_Caches => error-def(0x70, "too many registered caches" ), - - # TrueType and SFNT errors - - Invalid_Opcode => error-def(0x80, "invalid opcode" ), - Too_Few_Arguments => error-def(0x81, "too few arguments" ), - Stack_Overflow => error-def(0x82, "stack overflow" ), - Code_Overflow => error-def(0x83, "code overflow" ), - Bad_Argument => error-def(0x84, "bad argument" ), - Divide_By_Zero => error-def(0x85, "division by zero" ), - Invalid_Reference => error-def(0x86, "invalid reference" ), - Debug_OpCode => error-def(0x87, "found debug opcode" ), - ENDF_In_Exec_Stream => error-def(0x88, "found ENDF opcode in execution stream" ), - Nested_DEFS => error-def(0x89, "nested DEFS" ), - Invalid_CodeRange => error-def(0x8A, "invalid code range" ), - Execution_Too_Long => error-def(0x8B, "execution context too long" ), - Too_Many_Function_Defs => error-def(0x8C, "too many function definitions" ), - Too_Many_Instruction_Defs => error-def(0x8D, "too many instruction definitions" ), - Table_Missing => error-def(0x8E, "SFNT font table missing" ), - Horiz_Header_Missing => error-def(0x8F, "horizontal header (hhea) table missing" ), - Locations_Missing => error-def(0x90, "locations (loca) table missing" ), - Name_Table_Missing => error-def(0x91, "name table missing" ), - CMap_Table_Missing => error-def(0x92, "character map (cmap) table missing" ), - Hmtx_Table_Missing => error-def(0x93, "horizontal metrics (hmtx) table missing" ), - Post_Table_Missing => error-def(0x94, "PostScript (post) table missing" ), - Invalid_Horiz_Metrics => error-def(0x95, "invalid horizontal metrics" ), - Invalid_CharMap_Format => error-def(0x96, "invalid character map (cmap) format" ), - Invalid_PPem => error-def(0x97, "invalid ppem value" ), - Invalid_Vert_Metrics => error-def(0x98, "invalid vertical metrics" ), - Could_Not_Find_Context => error-def(0x99, "could not find context" ), - Invalid_Post_Table_Format => error-def(0x9A, "invalid PostScript (post) table format" ), - Invalid_Post_Table => error-def(0x9B, "invalid PostScript (post) table" ), - - # CFF, CID, and Type 1 errors - - Syntax_Error => error-def(0xA0, "opcode syntax error" ), - Stack_Underflow => error-def(0xA1, "argument stack underflow" ), - Ignore => error-def(0xA2, "ignore" ), - No_Unicode_Glyph_Name => error-def(0xA3, "no Unicode glyph name found" ), - Glyph_Too_Big => error-def(0xA4, "glyph too big for hinting" ), - - # BDF errors - - Missing_Startfont_Field => error-def(0xB0, "`STARTFONT' field missing" ), - Missing_Font_Field => error-def(0xB1, "`FONT' field missing" ), - Missing_Size_Field => error-def(0xB2, "`SIZE' field missing" ), - Missing_Fontboundingbox_Field => error-def(0xB3, "`FONTBOUNDINGBOX' field missing" ), - Missing_Chars_Field => error-def(0xB4, "`CHARS' field missing" ), - Missing_Startchar_Field => error-def(0xB5, "`STARTCHAR' field missing" ), - Missing_Encoding_Field => error-def(0xB6, "`ENCODING' field missing" ), - Missing_Bbx_Field => error-def(0xB7, "`BBX' field missing" ), - Bbx_Too_Big => error-def(0xB8, "`BBX' too big" ), - Corrupted_Font_Header => error-def(0xB9, "Font header corrupted or missing fields" ), - Corrupted_Font_Glyphs => error-def(0xBA, "Font glyphs corrupted or missing fields" ), - - ); - method message { - my $message = @Messages[$!error] - // "unknown error code: {$!error.fmt('0x%02X')}"; - my $detail = do with $!details { ' ' ~ $_ } // ''; - "FreeType Error{$detail}: $message"; - } - - sub ft-try(&sub) is export is hidden-from-backtrace { - my FT_Error $error = &sub(); - Font::FreeType::Error.new(:$error).throw - if $error; - True; - } +unit class Font::FreeType::Error is Exception; + +use Font::FreeType::Raw::Defs; + +has Int $.error is required; +has Str $.details is rw; + +our @Messages is export(:Messages); +sub error-def(UInt $num, Str $message) { + @Messages[$num] = $message; + $num +} +our enum FT_ERROR is export(:FT_ERROR) ( + +# generic errors + + Ok => error-def(0x00, "no error"), + Cannot_Open_Resource => error-def(0x01, "cannot open resource"), + Unknown_File_Format => error-def(0x02, "unknown file format"), + Invalid_File_Format => error-def(0x03, "invalid file format"), + Invalid_Version => error-def(0x04, "invalid FreeType version"), + Lower_Module_Version=> error-def(0x05, "module version is too low"), + Invalid_Argument => error-def(0x06, "invalid argument"), + Unimplemented_Feature => error-def(0x07, "unimplemented feature"), + Invalid_Table => error-def(0x08, "corrupted table"), + Invalid_Offset => error-def(0x09, "invalid offset within table"), + Array_Too_Large => error-def(0xA, "array allocation size too large"), + Missing_Module => error-def(0xB, "missing module"), + Missing_Property => error-def(0xC, "missing property"), + +# glyph/character errors + + Invalid_Glyph_Index => error-def(0x10, "invalid glyph index"), + Invalid_Character_Code => error-def(0x11, "invalid character code"), + Invalid_Glyph_Format => error-def(0x12, "unsupported glyph image format"), + Cannot_Render_Glyph => error-def(0x13, "cannot render this glyph format"), + Invalid_Outline => error-def(0x14, "invalid outline"), + Invalid_Composite => error-def(0x15, "invalid composite"), + Too_Many_Hints => error-def(0x16, "too many hints"), + Invalid_Pixel_Size => error-def(0x17, "invalid pixel size"), +# handle errors + + Invalid_Handle => error-def(0x20, "invalid object handle" ), + Invalid_Library_Handle => error-def(0x21, "invalid library handle" ), + Invalid_Driver_Handle => error-def(0x22, "invalid module handle" ), + Invalid_Face_Handle => error-def(0x23, "invalid face handle" ), + Invalid_Size_Handle => error-def(0x24, "invalid size handle" ), + Invalid_Slot_Handle => error-def(0x25, "invalid glyph slot handle" ), + Invalid_CharMap_Handle => error-def(0x26, "invalid charmap handle" ), + Invalid_Cache_Handle => error-def(0x27, "invalid cache manager handle" ), + Invalid_Stream_Handle => error-def(0x28, "invalid stream handle" ), +# driver errors + + Too_Many_Drivers => error-def(0x30, "too many modules" ), + Too_Many_Extensions => error-def(0x31, "too many extensions" ), + +# memory errors + + Out_Of_Memory => error-def(0x40, "out of memory" ), + Unlisted_Object => error-def(0x41, "unlisted object" ), + +# stream errors + + Cannot_Open_Stream => error-def(0x51, "cannot open stream" ), + Invalid_Stream_Seek => error-def(0x52, "invalid stream seek" ), + Invalid_Stream_Skip => error-def(0x53, "invalid stream skip" ), + Invalid_Stream_Read => error-def(0x54, "invalid stream read" ), + Invalid_Stream_Operation => error-def(0x55, "invalid stream operation" ), + Invalid_Frame_Operation => error-def(0x56, "invalid frame operation" ), + Nested_Frame_Access => error-def(0x57, "nested frame access" ), + Invalid_Frame_Read => error-def(0x58, "invalid frame read" ), + +# raster errors + + Raster_Uninitialized => error-def(0x60, "raster uninitialized" ), + Raster_Corrupted => error-def(0x61, "raster corrupted" ), + Raster_Overflow => error-def(0x62, "raster overflow" ), + Raster_Negative_Height => error-def(0x63, "negative height while rastering" ), + +# cache errors + + Too_Many_Caches => error-def(0x70, "too many registered caches" ), + +# TrueType and SFNT errors + + Invalid_Opcode => error-def(0x80, "invalid opcode" ), + Too_Few_Arguments => error-def(0x81, "too few arguments" ), + Stack_Overflow => error-def(0x82, "stack overflow" ), + Code_Overflow => error-def(0x83, "code overflow" ), + Bad_Argument => error-def(0x84, "bad argument" ), + Divide_By_Zero => error-def(0x85, "division by zero" ), + Invalid_Reference => error-def(0x86, "invalid reference" ), + Debug_OpCode => error-def(0x87, "found debug opcode" ), + ENDF_In_Exec_Stream => error-def(0x88, "found ENDF opcode in execution stream" ), + Nested_DEFS => error-def(0x89, "nested DEFS" ), + Invalid_CodeRange => error-def(0x8A, "invalid code range" ), + Execution_Too_Long => error-def(0x8B, "execution context too long" ), + Too_Many_Function_Defs => error-def(0x8C, "too many function definitions" ), + Too_Many_Instruction_Defs => error-def(0x8D, "too many instruction definitions" ), + Table_Missing => error-def(0x8E, "SFNT font table missing" ), + Horiz_Header_Missing => error-def(0x8F, "horizontal header (hhea) table missing" ), + Locations_Missing => error-def(0x90, "locations (loca) table missing" ), + Name_Table_Missing => error-def(0x91, "name table missing" ), + CMap_Table_Missing => error-def(0x92, "character map (cmap) table missing" ), + Hmtx_Table_Missing => error-def(0x93, "horizontal metrics (hmtx) table missing" ), + Post_Table_Missing => error-def(0x94, "PostScript (post) table missing" ), + Invalid_Horiz_Metrics => error-def(0x95, "invalid horizontal metrics" ), + Invalid_CharMap_Format => error-def(0x96, "invalid character map (cmap) format" ), + Invalid_PPem => error-def(0x97, "invalid ppem value" ), + Invalid_Vert_Metrics => error-def(0x98, "invalid vertical metrics" ), + Could_Not_Find_Context => error-def(0x99, "could not find context" ), + Invalid_Post_Table_Format => error-def(0x9A, "invalid PostScript (post) table format" ), + Invalid_Post_Table => error-def(0x9B, "invalid PostScript (post) table" ), + +# CFF, CID, and Type 1 errors + + Syntax_Error => error-def(0xA0, "opcode syntax error" ), + Stack_Underflow => error-def(0xA1, "argument stack underflow" ), + Ignore => error-def(0xA2, "ignore" ), + No_Unicode_Glyph_Name => error-def(0xA3, "no Unicode glyph name found" ), + Glyph_Too_Big => error-def(0xA4, "glyph too big for hinting" ), + +# BDF errors + + Missing_Startfont_Field => error-def(0xB0, "`STARTFONT' field missing" ), + Missing_Font_Field => error-def(0xB1, "`FONT' field missing" ), + Missing_Size_Field => error-def(0xB2, "`SIZE' field missing" ), + Missing_Fontboundingbox_Field => error-def(0xB3, "`FONTBOUNDINGBOX' field missing" ), + Missing_Chars_Field => error-def(0xB4, "`CHARS' field missing" ), + Missing_Startchar_Field => error-def(0xB5, "`STARTCHAR' field missing" ), + Missing_Encoding_Field => error-def(0xB6, "`ENCODING' field missing" ), + Missing_Bbx_Field => error-def(0xB7, "`BBX' field missing" ), + Bbx_Too_Big => error-def(0xB8, "`BBX' too big" ), + Corrupted_Font_Header => error-def(0xB9, "Font header corrupted or missing fields" ), + Corrupted_Font_Glyphs => error-def(0xBA, "Font glyphs corrupted or missing fields" ), + + ); +method message { + my $message = @Messages[$!error] + // "unknown error code: {$!error.fmt('0x%02X')}"; + my $detail = do with $!details { ' ' ~ $_ } // ''; + "FreeType Error{$detail}: $message"; } +sub ft-try(&sub) is export is hidden-from-backtrace { + my FT_Error $error = &sub(); + Font::FreeType::Error.new(:$error).throw + if $error; + True; +} + + diff --git a/lib/Font/FreeType/Face.rakumod b/lib/Font/FreeType/Face.rakumod index d2dfe95..de011a5 100644 --- a/lib/Font/FreeType/Face.rakumod +++ b/lib/Font/FreeType/Face.rakumod @@ -1,476 +1,475 @@ #| Font typefaces loaded from Font::FreeType -class Font::FreeType::Face { - - use NativeCall; - use Font::FreeType::Error; - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - use Font::FreeType::Raw::TT_Sfnt; - - use Font::FreeType::BitMap; - use Font::FreeType::Glyph; - use Font::FreeType::NamedInfo; - use Font::FreeType::CharMap; - use Font::FreeType::SizeMetrics; - use Font::FreeType::BBox; - use Method::Also; - - constant Dot6 = Font::FreeType::Raw::Defs::Dot6; - - has $.ft-lib is required; # keep a reference to library root object. Just to avoid destroying it - has FT_Face $.raw handles is required; - has UInt $.load-flags is rw = FT_LOAD_NO_SCALE +| FT_LOAD_NO_HINTING; - has Lock $!lock handles .= new; - has $!metrics-delegate handles = $!raw; - - method attach-file(Str:D() $filepath) { - ft-try { self.raw.FT_Attach_File($filepath); } +unit class Font::FreeType::Face; + +use NativeCall; +use Font::FreeType::Error; +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; +use Font::FreeType::Raw::TT_Sfnt; + +use Font::FreeType::BitMap; +use Font::FreeType::Glyph; +use Font::FreeType::NamedInfo; +use Font::FreeType::CharMap; +use Font::FreeType::SizeMetrics; +use Font::FreeType::BBox; +use Method::Also; + +constant Dot6 = Font::FreeType::Raw::Defs::Dot6; + +has $.ft-lib is required; # keep a reference to library root object. Just to avoid destroying it +has FT_Face $.raw handles is required; +has UInt $.load-flags is rw = FT_LOAD_NO_SCALE +| FT_LOAD_NO_HINTING; +has Lock $!lock handles .= new; +has $!metrics-delegate handles = $!raw; + +method attach-file(Str:D() $filepath) { + ft-try { self.raw.FT_Attach_File($filepath); } +} +method bbox is also returns Font::FreeType::BBox { + my Font::FreeType::BBox $bbox; + if $!metrics-delegate === $!raw { + $bbox .= new: :bbox($!raw.bbox), :x-scale(1), :y-scale(1) + if self.is-scalable; } - method bbox is also returns Font::FreeType::BBox { - my Font::FreeType::BBox $bbox; - if $!metrics-delegate === $!raw { - $bbox .= new: :bbox($!raw.bbox), :x-scale(1), :y-scale(1) - if self.is-scalable; - } - else { - $bbox = $!metrics-delegate.bbox; - } - $bbox; + else { + $bbox = $!metrics-delegate.bbox; } + $bbox; +} - class UnscaledMetrics { - method bbox is also { Font::FreeType::BBox } - method FALLBACK(|) { Int } - } +class UnscaledMetrics { + method bbox is also { Font::FreeType::BBox } + method FALLBACK(|) { Int } +} - submethod TWEAK(Str :$attach-file) { - self.attach-file($_) - with $attach-file; - $!metrics-delegate = UnscaledMetrics - unless self.is-scalable; - } +submethod TWEAK(Str :$attach-file) { + self.attach-file($_) + with $attach-file; + $!metrics-delegate = UnscaledMetrics + unless self.is-scalable; +} - subset FontFormat of Str where 'TrueType'|'Type 1'|'BDF'|'PCF'|'Type 42'|'CID Type 1'|'CFF'|'PFR'|'Windows FNT'|'OpenType'; - method font-format(::?CLASS:D $face: --> FontFormat) { - my $type := $!raw.FT_Get_Font_Format; - if $type eq 'CFF' { - # Is this simple CFF or OpenType/CFF font? - TT_Header.load(:$face).defined - ?? 'OpenType' !! 'CFF'; - } - else { - $type; - } +subset FontFormat of Str where 'TrueType'|'Type 1'|'BDF'|'PCF'|'Type 42'|'CID Type 1'|'CFF'|'PFR'|'Windows FNT'|'OpenType'; +method font-format(::?CLASS:D $face: --> FontFormat) { + my $type := $!raw.FT_Get_Font_Format; + if $type eq 'CFF' { + # Is this simple CFF or OpenType/CFF font? + TT_Header.load(:$face).defined + ?? 'OpenType' !! 'CFF'; } - - has Font::FreeType::BitMap::Size @!fixed-sizes; - method fixed-sizes($face:) returns Array { - @!fixed-sizes ||= (^$!raw.num-fixed-sizes).map: { - my FT_Bitmap_Size $raw = $!raw.get-bitmap-size($_); - Font::FreeType::BitMap::Size.new: :$raw, :$face; - } + else { + $type; } +} - has Font::FreeType::SizeMetrics $!scaled-metrics; - method scaled-metrics(::?CLASS:D $face:) handles { - $!scaled-metrics //= Font::FreeType::SizeMetrics.new: :$face, :size($_) - with $!raw.size; +has Font::FreeType::BitMap::Size @!fixed-sizes; +method fixed-sizes($face:) returns Array { + @!fixed-sizes ||= (^$!raw.num-fixed-sizes).map: { + my FT_Bitmap_Size $raw = $!raw.get-bitmap-size($_); + Font::FreeType::BitMap::Size.new: :$raw, :$face; } +} - method charmap($face:) returns Font::FreeType::CharMap { - my Font::FreeType::CharMap $charmap .= new: :$face, :raw($_) - with $!raw.charmap; - $charmap; - } +has Font::FreeType::SizeMetrics $!scaled-metrics; +method scaled-metrics(::?CLASS:D $face:) handles { + $!scaled-metrics //= Font::FreeType::SizeMetrics.new: :$face, :size($_) + with $!raw.size; +} - method set-charmap(UInt $n where 1 <= * <= $!raw.num-charmaps --> Font::FreeType::CharMap) { - my FT_CharMap:D $nth-charmap = $!raw.charmaps[$n - 1]; - ft-try {$!raw.FT_Set_Charmap($nth-charmap); } - @!to-unicode = (); - self.charmap; - } +method charmap($face:) returns Font::FreeType::CharMap { + my Font::FreeType::CharMap $charmap .= new: :$face, :raw($_) + with $!raw.charmap; + $charmap; +} - method charmaps($face:) returns Seq { - my int $n-sizes = self.num-charmaps; - my $ptr = $!raw.charmaps; - (^$n-sizes).map: { - my FT_CharMap $raw = $ptr[$_]; - Font::FreeType::CharMap.new: :$face, :$raw; - } - } +method set-charmap(UInt $n where 1 <= * <= $!raw.num-charmaps --> Font::FreeType::CharMap) { + my FT_CharMap:D $nth-charmap = $!raw.charmaps[$n - 1]; + ft-try {$!raw.FT_Set_Charmap($nth-charmap); } + @!to-unicode = (); + self.charmap; +} - my class Vector { - has FT_Vector $!raw; - has UInt:D $.scale = Dot6; - submethod TWEAK(FT_Vector:D :$!raw!) { } - method x { $!raw.x / $!scale } - method y { $!raw.y / $!scale } - method gist { $.x ~ ' ' ~ $.y }; +method charmaps($face:) returns Seq { + my int $n-sizes = self.num-charmaps; + my $ptr = $!raw.charmaps; + (^$n-sizes).map: { + my FT_CharMap $raw = $ptr[$_]; + Font::FreeType::CharMap.new: :$face, :$raw; } +} - method named-infos { - return Mu unless self.is-scalable; - my int $n-sizes = $!raw.FT_Get_Sfnt_Name_Count; - (^$n-sizes).map: -> $i { - my FT_SfntName $sfnt .= new; - ft-try { $!raw.FT_Get_Sfnt_Name($i, $sfnt); }; - Font::FreeType::NamedInfo.new: :raw($sfnt); - } - } +my class Vector { + has FT_Vector $!raw; + has UInt:D $.scale = Dot6; + submethod TWEAK(FT_Vector:D :$!raw!) { } + method x { $!raw.x / $!scale } + method y { $!raw.y / $!scale } + method gist { $.x ~ ' ' ~ $.y }; +} - method postscript-name returns Str { $!raw.FT_Get_Postscript_Name } - - method !flag-set(FT_FACE_FLAG $f) { ?($!raw.face-flags +& $f) } - method is-scalable { self!flag-set: FT_FACE_FLAG_SCALABLE } - method has-fixed-sizes { self!flag-set: FT_FACE_FLAG_FIXED_SIZES } - method is-fixed-width { self!flag-set: FT_FACE_FLAG_FIXED_WIDTH } - method is-sfnt { self!flag-set: FT_FACE_FLAG_SFNT } - method has-horizontal-metrics { self!flag-set: FT_FACE_FLAG_HORIZONTAL } - method has-vertical-metrics { self!flag-set: FT_FACE_FLAG_VERTICAL } - method has-kerning { self!flag-set: FT_FACE_FLAG_KERNING } - method has-glyph-names { self!flag-set: FT_FACE_FLAG_GLYPH_NAMES } - method has-reliable-glyph-names { self.has-glyph-names && ? $!raw.FT_Has_PS_Glyph_Names } - method is-bold { ?($!raw.style-flags +& FT_STYLE_FLAG_BOLD) } - method is-italic { ?($!raw.style-flags +& FT_STYLE_FLAG_ITALIC) } - - method !get-glyph-name(UInt $glyph-index) { - my buf8 $buf .= allocate(256); - ft-try { $!raw.FT_Get_Glyph_Name($glyph-index, $buf, $buf.bytes); }; - nativecast(Str, $buf); +method named-infos { + return Mu unless self.is-scalable; + my int $n-sizes = $!raw.FT_Get_Sfnt_Name_Count; + (^$n-sizes).map: -> $i { + my FT_SfntName $sfnt .= new; + ft-try { $!raw.FT_Get_Sfnt_Name($i, $sfnt); }; + Font::FreeType::NamedInfo.new: :raw($sfnt); } +} - proto glyph-name($ --> Str) {*} - multi method glyph-name(Str:D $char) { - my FT_UInt $index = $!raw.FT_Get_Char_Index( $char.ord ); - $.glyph-name-from-index($index); - } - multi method glyph-name(UInt:D $char-code) { - my FT_UInt $index = $!raw.FT_Get_Char_Index( $char-code ); - $.glyph-name-from-index($index); - } - proto glyph-index($ --> Int) {*} - multi method glyph-index(Str:D $char) { - $!raw.FT_Get_Char_Index($char.ord); - } - multi method glyph-index(UInt:D $char-code) { - $!raw.FT_Get_Char_Index($char-code); - } +method postscript-name returns Str { $!raw.FT_Get_Postscript_Name } + +method !flag-set(FT_FACE_FLAG $f) { ?($!raw.face-flags +& $f) } +method is-scalable { self!flag-set: FT_FACE_FLAG_SCALABLE } +method has-fixed-sizes { self!flag-set: FT_FACE_FLAG_FIXED_SIZES } +method is-fixed-width { self!flag-set: FT_FACE_FLAG_FIXED_WIDTH } +method is-sfnt { self!flag-set: FT_FACE_FLAG_SFNT } +method has-horizontal-metrics { self!flag-set: FT_FACE_FLAG_HORIZONTAL } +method has-vertical-metrics { self!flag-set: FT_FACE_FLAG_VERTICAL } +method has-kerning { self!flag-set: FT_FACE_FLAG_KERNING } +method has-glyph-names { self!flag-set: FT_FACE_FLAG_GLYPH_NAMES } +method has-reliable-glyph-names { self.has-glyph-names && ? $!raw.FT_Has_PS_Glyph_Names } +method is-bold { ?($!raw.style-flags +& FT_STYLE_FLAG_BOLD) } +method is-italic { ?($!raw.style-flags +& FT_STYLE_FLAG_ITALIC) } + +method !get-glyph-name(UInt $glyph-index) { + my buf8 $buf .= allocate(256); + ft-try { $!raw.FT_Get_Glyph_Name($glyph-index, $buf, $buf.bytes); }; + nativecast(Str, $buf); +} - method glyph-name-from-index(UInt:D $glyph-index --> Str) { - self.has-glyph-names - ?? self!get-glyph-name($glyph-index) - !! Str; - } +proto glyph-name($ --> Str) {*} +multi method glyph-name(Str:D $char) { + my FT_UInt $index = $!raw.FT_Get_Char_Index( $char.ord ); + $.glyph-name-from-index($index); +} +multi method glyph-name(UInt:D $char-code) { + my FT_UInt $index = $!raw.FT_Get_Char_Index( $char-code ); + $.glyph-name-from-index($index); +} +proto glyph-index($ --> Int) {*} +multi method glyph-index(Str:D $char) { + $!raw.FT_Get_Char_Index($char.ord); +} +multi method glyph-index(UInt:D $char-code) { + $!raw.FT_Get_Char_Index($char-code); +} - method index-from-glyph-name(Str:D $glyph-name --> Int) { - self.has-glyph-names - ?? $!raw.FT_Get_Name_Index($glyph-name) - !! Int; - } +method glyph-name-from-index(UInt:D $glyph-index --> Str) { + self.has-glyph-names + ?? self!get-glyph-name($glyph-index) + !! Str; +} - has uint32 @!to-unicode; - multi method index-to-unicode { - $.protect: { - unless @!to-unicode { - my FT_Face $struct := $!raw; - my FT_UInt $glyph-idx; - my FT_ULong $char-code = $struct.FT_Get_First_Char( $glyph-idx); - @!to-unicode[self.num-glyphs] = 0; - while $glyph-idx { - @!to-unicode[ $glyph-idx ] = $char-code; - $char-code = $struct.FT_Get_Next_Char( $char-code, $glyph-idx); - } +method index-from-glyph-name(Str:D $glyph-name --> Int) { + self.has-glyph-names + ?? $!raw.FT_Get_Name_Index($glyph-name) + !! Int; +} + +has uint32 @!to-unicode; +multi method index-to-unicode { + $.protect: { + unless @!to-unicode { + my FT_Face $struct := $!raw; + my FT_UInt $glyph-idx; + my FT_ULong $char-code = $struct.FT_Get_First_Char( $glyph-idx); + @!to-unicode[self.num-glyphs] = 0; + while $glyph-idx { + @!to-unicode[ $glyph-idx ] = $char-code; + $char-code = $struct.FT_Get_Next_Char( $char-code, $glyph-idx); } } - @!to-unicode; - } - multi method index-to-unicode(UInt:D $i) { - self.index-to-unicode unless @!to-unicode; - @!to-unicode[$i]; } + @!to-unicode; +} +multi method index-to-unicode(UInt:D $i) { + self.index-to-unicode unless @!to-unicode; + @!to-unicode[$i]; +} - multi method forall-chars(Str:D $text, &code, |c) is also { - self.forall-chars(&code, $text.ords, |c); - } +multi method forall-chars(Str:D $text, &code, |c) is also { + self.forall-chars(&code, $text.ords, |c); +} - multi method forall-chars(&code, Str:D $text, |c --> Seq) { - self.forall-chars(&code, $text.ords, |c); - } +multi method forall-chars(&code, Str:D $text, |c --> Seq) { + self.forall-chars(&code, $text.ords, |c); +} - method glyph-image(::?CLASS:D $face: UInt:D $char-code, :$flags = $!load-flags --> Font::FreeType::GlyphImage) { - my FT_GlyphSlot:D $glyph = $!raw.glyph; +method glyph-image(::?CLASS:D $face: UInt:D $char-code, :$flags = $!load-flags --> Font::FreeType::GlyphImage) { + my FT_GlyphSlot:D $glyph = $!raw.glyph; - $!lock.protect: { - my $stat = $!raw.FT_Load_Char($char-code, $flags); - Font::FreeType::GlyphImage.new: :$face, :$glyph, :$char-code, :$stat; - } - } + $!lock.protect: { + my $stat = $!raw.FT_Load_Char($char-code, $flags); + Font::FreeType::GlyphImage.new: :$face, :$glyph, :$char-code, :$stat; + } +} - multi method forall-char-images(::?CLASS:D: &code, @ords, :$flags = $!load-flags --> Seq) { - @ords.map: -> UInt:D $char-code { - my $glyph-image = self.glyph-image($char-code, :$flags); - &code($glyph-image); - } +multi method forall-char-images(::?CLASS:D: &code, @ords, :$flags = $!load-flags --> Seq) { + @ords.map: -> UInt:D $char-code { + my $glyph-image = self.glyph-image($char-code, :$flags); + &code($glyph-image); } +} - multi method forall-chars(::?CLASS:D $face: &code, @ords, :$flags = $!load-flags --> Seq) { - my FT_GlyphSlot:D $raw = $!raw.glyph; - my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; +multi method forall-chars(::?CLASS:D $face: &code, @ords, :$flags = $!load-flags --> Seq) { + my FT_GlyphSlot:D $raw = $!raw.glyph; + my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; - @ords.map: -> UInt:D $char-code { - $!lock.protect: { - $glyph.stat = $!raw.FT_Load_Char($char-code, $flags); - $glyph.glyph-index = 0; - $glyph.char-code = $char-code; - &code($glyph); - } + @ords.map: -> UInt:D $char-code { + $!lock.protect: { + $glyph.stat = $!raw.FT_Load_Char($char-code, $flags); + $glyph.glyph-index = 0; + $glyph.char-code = $char-code; + &code($glyph); } } +} - multi method forall-char-images(::?CLASS:D: &code, :$flags = $!load-flags) { - my FT_UInt $glyph-index; - my FT_ULong $char-code = $!raw.FT_Get_First_Char( $glyph-index); +multi method forall-char-images(::?CLASS:D: &code, :$flags = $!load-flags) { + my FT_UInt $glyph-index; + my FT_ULong $char-code = $!raw.FT_Get_First_Char( $glyph-index); - while $glyph-index { - my $glyph-image := self.glyph-image($char-code, :$flags); - &code($glyph-image); - $char-code = $!raw.FT_Get_Next_Char( $char-code, $glyph-index); - } + while $glyph-index { + my $glyph-image := self.glyph-image($char-code, :$flags); + &code($glyph-image); + $char-code = $!raw.FT_Get_Next_Char( $char-code, $glyph-index); } +} - #| iterate all char-mapped glyphs - multi method forall-chars(::?CLASS:D $face: &code, :$flags = $!load-flags) { - my FT_GlyphSlot:D $raw = $!raw.glyph; - my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; - my FT_UInt $glyph-index; - my FT_ULong $char-code = $!raw.FT_Get_First_Char( $glyph-index); - - while $glyph-index { - $!lock.protect: { - $glyph.stat = $!raw.FT_Load_Char($char-code, $flags); - $glyph.glyph-index = $glyph-index; - $glyph.char-code = $char-code; - &code($glyph); - } - $char-code = $!raw.FT_Get_Next_Char( $char-code, $glyph-index); - } - } +#| iterate all char-mapped glyphs +multi method forall-chars(::?CLASS:D $face: &code, :$flags = $!load-flags) { + my FT_GlyphSlot:D $raw = $!raw.glyph; + my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; + my FT_UInt $glyph-index; + my FT_ULong $char-code = $!raw.FT_Get_First_Char( $glyph-index); - multi method forall-glyph-images(::?CLASS:D $face: &code, :$flags = $!load-flags) { - my FT_GlyphSlot:D $glyph = $!raw.glyph; - my $to-unicode := $.index-to-unicode; - (^$!raw.num-glyphs).map: -> $glyph-index { - my Font::FreeType::GlyphImage $glyph-image = $!lock.protect: { - my $stat = $!raw.FT_Load_Glyph($glyph-index, $flags); - my $char-code = $to-unicode[$glyph-index]; - Font::FreeType::GlyphImage.new: :$face, :$glyph, :$char-code, :$glyph-index, :$stat; - } - &code($glyph-image); + while $glyph-index { + $!lock.protect: { + $glyph.stat = $!raw.FT_Load_Char($char-code, $flags); + $glyph.glyph-index = $glyph-index; + $glyph.char-code = $char-code; + &code($glyph); } + $char-code = $!raw.FT_Get_Next_Char( $char-code, $glyph-index); } +} - multi method forall-glyphs(::?CLASS:D $face: &code, :$flags = $!load-flags) { - my FT_GlyphSlot:D $raw = $!raw.glyph; - my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; - my $to-unicode := $.index-to-unicode; - - (^$!raw.num-glyphs).map: -> uint $glyph-index { - $!lock.protect: { - $glyph.stat = $!raw.FT_Load_Glyph($glyph-index, $flags); - $glyph.glyph-index = $glyph-index; - $glyph.char-code = $to-unicode[$glyph-index]; - &code($glyph); - } +multi method forall-glyph-images(::?CLASS:D $face: &code, :$flags = $!load-flags) { + my FT_GlyphSlot:D $glyph = $!raw.glyph; + my $to-unicode := $.index-to-unicode; + (^$!raw.num-glyphs).map: -> $glyph-index { + my Font::FreeType::GlyphImage $glyph-image = $!lock.protect: { + my $stat = $!raw.FT_Load_Glyph($glyph-index, $flags); + my $char-code = $to-unicode[$glyph-index]; + Font::FreeType::GlyphImage.new: :$face, :$glyph, :$char-code, :$glyph-index, :$stat; } + &code($glyph-image); } +} - multi method forall-glyph-images(::?CLASS:D $face: @gids, &code, :$flags = $!load-flags) { - my FT_GlyphSlot:D $glyph = $!raw.glyph; - my $to-unicode := $.index-to-unicode; +multi method forall-glyphs(::?CLASS:D $face: &code, :$flags = $!load-flags) { + my FT_GlyphSlot:D $raw = $!raw.glyph; + my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; + my $to-unicode := $.index-to-unicode; - @gids.map: -> UInt $glyph-index { - my Font::FreeType::GlyphImage $glyph-image = $!lock.protect: { - my $stat = $!raw.FT_Load_Glyph($glyph-index, $flags); - my $char-code = $to-unicode[$glyph-index]; - Font::FreeType::GlyphImage.new: :$face, :$glyph, :$char-code, :$glyph-index, :$stat; - } - &code($glyph-image); + (^$!raw.num-glyphs).map: -> uint $glyph-index { + $!lock.protect: { + $glyph.stat = $!raw.FT_Load_Glyph($glyph-index, $flags); + $glyph.glyph-index = $glyph-index; + $glyph.char-code = $to-unicode[$glyph-index]; + &code($glyph); } } +} - multi method forall-glyphs(::?CLASS:D $face: @gids, &code, :$flags = $!load-flags) { - my FT_GlyphSlot:D $raw = $!raw.glyph; - my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; - my $to-unicode := $.index-to-unicode; - - @gids.map: -> UInt $glyph-index { - $!lock.protect: { - $glyph.stat = $!raw.FT_Load_Glyph($glyph-index, $flags); - $glyph.glyph-index = $glyph-index; - $glyph.char-code = $to-unicode[$glyph-index]; - &code($glyph); - } +multi method forall-glyph-images(::?CLASS:D $face: @gids, &code, :$flags = $!load-flags) { + my FT_GlyphSlot:D $glyph = $!raw.glyph; + my $to-unicode := $.index-to-unicode; + + @gids.map: -> UInt $glyph-index { + my Font::FreeType::GlyphImage $glyph-image = $!lock.protect: { + my $stat = $!raw.FT_Load_Glyph($glyph-index, $flags); + my $char-code = $to-unicode[$glyph-index]; + Font::FreeType::GlyphImage.new: :$face, :$glyph, :$char-code, :$glyph-index, :$stat; } + &code($glyph-image); } +} - method glyph-images(Str $text, Int :$flags = $!load-flags) { - my Font::FreeType::GlyphImage @ = self.forall-char-images({$_}, $text.ords, :$flags); - } +multi method forall-glyphs(::?CLASS:D $face: @gids, &code, :$flags = $!load-flags) { + my FT_GlyphSlot:D $raw = $!raw.glyph; + my Font::FreeType::Glyph $glyph .= new: :$face, :$raw, :$flags; + my $to-unicode := $.index-to-unicode; - method set-char-size(Numeric $width, Numeric $height = $width, UInt $horiz-res = 0, UInt $vert-res = 0) { - $!lock.protect: sub () is hidden-from-backtrace { - my FT_F26Dot6 $w = ($width * Dot6).round; - my FT_F26Dot6 $h = ($height * Dot6).round; - $!load-flags -= FT_LOAD_NO_SCALE - if $!load-flags +& FT_LOAD_NO_SCALE; - if $horiz-res || $vert-res { - $!load-flags -= FT_LOAD_NO_HINTING - if $!load-flags +& FT_LOAD_NO_HINTING; - } - ft-try { $!raw.FT_Set_Char_Size($w, $h, $horiz-res, $vert-res) }; + @gids.map: -> UInt $glyph-index { + $!lock.protect: { + $glyph.stat = $!raw.FT_Load_Glyph($glyph-index, $flags); + $glyph.glyph-index = $glyph-index; + $glyph.char-code = $to-unicode[$glyph-index]; + &code($glyph); } - $!metrics-delegate = $!raw; } +} - method set-font-size(|c) is hidden-from-backtrace { - self.set-char-size(|c); - $!metrics-delegate = self.scaled-metrics; - } +method glyph-images(Str $text, Int :$flags = $!load-flags) { + my Font::FreeType::GlyphImage @ = self.forall-char-images({$_}, $text.ords, :$flags); +} - method set-pixel-sizes(UInt $width, UInt $height, Bool :$scale-font) { - $!lock.protect: sub () is hidden-from-backtrace { - ft-try { $!raw.FT_Set_Pixel_Sizes($width, $height) }; - $!load-flags -= FT_LOAD_NO_SCALE - if $!load-flags +& FT_LOAD_NO_SCALE; +method set-char-size(Numeric $width, Numeric $height = $width, UInt $horiz-res = 0, UInt $vert-res = 0) { + $!lock.protect: sub () is hidden-from-backtrace { + my FT_F26Dot6 $w = ($width * Dot6).round; + my FT_F26Dot6 $h = ($height * Dot6).round; + $!load-flags -= FT_LOAD_NO_SCALE + if $!load-flags +& FT_LOAD_NO_SCALE; + if $horiz-res || $vert-res { $!load-flags -= FT_LOAD_NO_HINTING if $!load-flags +& FT_LOAD_NO_HINTING; } - $!metrics-delegate = $scale-font ?? self.scaled-metrics !! $!raw; + ft-try { $!raw.FT_Set_Char_Size($w, $h, $horiz-res, $vert-res) }; } + $!metrics-delegate = $!raw; +} - method kerning(Str $left, Str $right, UInt :$mode = $!metrics-delegate === $!scaled-metrics ?? FT_KERNING_UNFITTED !! FT_KERNING_UNSCALED) { - my FT_UInt $left-idx = $!raw.FT_Get_Char_Index( $left.ord ); - my FT_UInt $right-idx = $!raw.FT_Get_Char_Index( $right.ord ); - my FT_Vector $vec .= new; - ft-try { $!raw.FT_Get_Kerning($left-idx, $right-idx, $mode, $vec); }; - my $scale := ($mode == FT_KERNING_UNSCALED) ?? 1 !! Dot6; - Vector.new: :raw($vec), :$scale; - } +method set-font-size(|c) is hidden-from-backtrace { + self.set-char-size(|c); + $!metrics-delegate = self.scaled-metrics; +} - method is-internally-keyed-cid returns Bool { - my FT_Bool $is-cid; - $!raw.FT_Get_CID_Is_Internally_CID_Keyed($is-cid); - $is-cid.so; +method set-pixel-sizes(UInt $width, UInt $height, Bool :$scale-font) { + $!lock.protect: sub () is hidden-from-backtrace { + ft-try { $!raw.FT_Set_Pixel_Sizes($width, $height) }; + $!load-flags -= FT_LOAD_NO_SCALE + if $!load-flags +& FT_LOAD_NO_SCALE; + $!load-flags -= FT_LOAD_NO_HINTING + if $!load-flags +& FT_LOAD_NO_HINTING; } + $!metrics-delegate = $scale-font ?? self.scaled-metrics !! $!raw; +} - method Numeric is also { - $!raw.num-glyphs; - } +method kerning(Str $left, Str $right, UInt :$mode = $!metrics-delegate === $!scaled-metrics ?? FT_KERNING_UNFITTED !! FT_KERNING_UNSCALED) { + my FT_UInt $left-idx = $!raw.FT_Get_Char_Index( $left.ord ); + my FT_UInt $right-idx = $!raw.FT_Get_Char_Index( $right.ord ); + my FT_Vector $vec .= new; + ft-try { $!raw.FT_Get_Kerning($left-idx, $right-idx, $mode, $vec); }; + my $scale := ($mode == FT_KERNING_UNSCALED) ?? 1 !! Dot6; + Vector.new: :raw($vec), :$scale; +} - multi method iterate-chars(::?CLASS:D $face: Str:D $text, :$flags = $!load-flags) is DEPRECATED { - class TextIteration does Iterator does Iterable { - has Font::FreeType::Face:D $.face is required; - has Int:D $.flags is required; - has UInt:D @.ords is required; - has Lock:D $.lock is required; - has FT_Face $!raw = $!face.raw; - has Font::FreeType::Glyph $!glyph .= new: :$!face, :raw($!raw.glyph), :$flags; - has UInt:D $!idx = 0; - method pull-one { - if $!idx < @!ords { - $!lock.protect: { - my $char-code := @!ords[$!idx++]; - $!glyph.stat = $!raw.FT_Load_Char($char-code, $!flags); - $!glyph.glyph-index = 0; - $!glyph.char-code = $char-code; - $!glyph; - } - } - else { - IterationEnd; +method is-internally-keyed-cid returns Bool { + my FT_Bool $is-cid; + $!raw.FT_Get_CID_Is_Internally_CID_Keyed($is-cid); + $is-cid.so; +} + +method Numeric is also { + $!raw.num-glyphs; +} + +multi method iterate-chars(::?CLASS:D $face: Str:D $text, :$flags = $!load-flags) is DEPRECATED { + class TextIteration does Iterator does Iterable { + has Font::FreeType::Face:D $.face is required; + has Int:D $.flags is required; + has UInt:D @.ords is required; + has Lock:D $.lock is required; + has FT_Face $!raw = $!face.raw; + has Font::FreeType::Glyph $!glyph .= new: :$!face, :raw($!raw.glyph), :$flags; + has UInt:D $!idx = 0; + method pull-one { + if $!idx < @!ords { + $!lock.protect: { + my $char-code := @!ords[$!idx++]; + $!glyph.stat = $!raw.FT_Load_Char($char-code, $!flags); + $!glyph.glyph-index = 0; + $!glyph.char-code = $char-code; + $!glyph; } } - method iterator { self } + else { + IterationEnd; + } } - my @ords = $text.ords; - TextIteration.new: :$face, :$flags, :@ords, :$!lock; + method iterator { self } } + my @ords = $text.ords; + TextIteration.new: :$face, :$flags, :@ords, :$!lock; +} - # not thread-safe: deprecated - multi method iterate-chars(::?CLASS:D $face: :$flags = $!load-flags, Bool :$load = True) is DEPRECATED { - class AllCharsIteration does Iterator does Iterable { - has Font::FreeType::Face:D $.face is required; - has Int:D $.flags is required; - has Bool $.load is required; - has Lock:D $.lock is required; - has FT_Face $!raw = $!face.raw; - has Font::FreeType::Glyph $!glyph .= new: :$!face, :raw($!raw.glyph), :$flags; - has FT_UInt $!idx = 0; - - method pull-one { - - given $!idx { - $!lock.protect: { - $!glyph.char-code = $_ - ?? $!raw.FT_Get_Next_Char( $!glyph.char-code, $_) - !! $!raw.FT_Get_First_Char($_); - - if $_ { - $!glyph.stat = $!raw.FT_Load_Glyph($_, $!flags ) - if $!load; - $!glyph.glyph-index = $_; - $!glyph; - } - else { - IterationEnd; - } +# not thread-safe: deprecated +multi method iterate-chars(::?CLASS:D $face: :$flags = $!load-flags, Bool :$load = True) is DEPRECATED { + class AllCharsIteration does Iterator does Iterable { + has Font::FreeType::Face:D $.face is required; + has Int:D $.flags is required; + has Bool $.load is required; + has Lock:D $.lock is required; + has FT_Face $!raw = $!face.raw; + has Font::FreeType::Glyph $!glyph .= new: :$!face, :raw($!raw.glyph), :$flags; + has FT_UInt $!idx = 0; + + method pull-one { + + given $!idx { + $!lock.protect: { + $!glyph.char-code = $_ + ?? $!raw.FT_Get_Next_Char( $!glyph.char-code, $_) + !! $!raw.FT_Get_First_Char($_); + + if $_ { + $!glyph.stat = $!raw.FT_Load_Glyph($_, $!flags ) + if $!load; + $!glyph.glyph-index = $_; + $!glyph; + } + else { + IterationEnd; } } } - method iterator { self } } - AllCharsIteration.new: :$face, :$flags, :$load, :$!lock; + method iterator { self } } + AllCharsIteration.new: :$face, :$flags, :$load, :$!lock; +} - # not thread-safe: deprecated - method iterate-glyphs(::?CLASS:D $face: :$flags = $!load-flags) is DEPRECATED { - class AllGlyphsIteration does Iterator does Iterable { - has Font::FreeType::Face:D $.face is required; - has $.to-unicode is required; - has Int:D $.flags is required; - has Lock:D $.lock is required; - has FT_Face $!raw = $!face.raw; - has Font::FreeType::Glyph $!glyph .= new: :$!face, :raw($!raw.glyph), :$flags; - has UInt:D $!idx = 0; - - method pull-one { - if $!idx < $!raw.num-glyphs { - $!lock.protect: { - $!glyph.stat = $!raw.FT_Load_Glyph( $!idx, $!flags ); - $!glyph.glyph-index = $!idx; - $!glyph.char-code = $!to-unicode[$!idx++]; - $!glyph; - } - } - else { - IterationEnd; +# not thread-safe: deprecated +method iterate-glyphs(::?CLASS:D $face: :$flags = $!load-flags) is DEPRECATED { + class AllGlyphsIteration does Iterator does Iterable { + has Font::FreeType::Face:D $.face is required; + has $.to-unicode is required; + has Int:D $.flags is required; + has Lock:D $.lock is required; + has FT_Face $!raw = $!face.raw; + has Font::FreeType::Glyph $!glyph .= new: :$!face, :raw($!raw.glyph), :$flags; + has UInt:D $!idx = 0; + + method pull-one { + if $!idx < $!raw.num-glyphs { + $!lock.protect: { + $!glyph.stat = $!raw.FT_Load_Glyph( $!idx, $!flags ); + $!glyph.glyph-index = $!idx; + $!glyph.char-code = $!to-unicode[$!idx++]; + $!glyph; } } - method iterator { self } + else { + IterationEnd; + } } - my $to-unicode := $.index-to-unicode; - AllGlyphsIteration.new: :$face, :$to-unicode, :$flags, :$!lock; + method iterator { self } } + my $to-unicode := $.index-to-unicode; + AllGlyphsIteration.new: :$face, :$to-unicode, :$flags, :$!lock; +} - method NativeCall::Types::Pointer { nativecast(Pointer, $!raw) } +method NativeCall::Types::Pointer { nativecast(Pointer, $!raw) } - submethod DESTROY { - with $!raw { - ft-try { .FT_Done_Face }; - $_ = Nil; - } +submethod DESTROY { + with $!raw { + ft-try { .FT_Done_Face }; + $_ = Nil; } } diff --git a/lib/Font/FreeType/Glyph.rakumod b/lib/Font/FreeType/Glyph.rakumod index efd3b4e..6c817e3 100644 --- a/lib/Font/FreeType/Glyph.rakumod +++ b/lib/Font/FreeType/Glyph.rakumod @@ -1,51 +1,49 @@ #| iterator for font typeface glyphs -class Font::FreeType::Glyph { - - use Font::FreeType::_Glyph; - also is Font::FreeType::_Glyph; - - use NativeCall; - use Font::FreeType::GlyphImage; - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - use Font::FreeType::Error; - - use Font::FreeType::BitMap; - constant Dot6 = Font::FreeType::Raw::Defs::Dot6; - - has FT_GlyphSlot $.raw is built handles ; - has UInt:D $.flags = FT_LOAD_DEFAULT; - has Numeric $!x-scale; - has Numeric $!y-scale; - - submethod TWEAK(FT_GlyphSlot:D :$!raw!) { - my FT_Face:D $face := $!raw.face; - my FT_Size_Metrics:D $metrics := $face.size.metrics; - $!x-scale = $face.face-flags +& FT_FACE_FLAG_SCALABLE == 0 || ($metrics.x-scale && ($!flags +& FT_LOAD_NO_SCALE == 0)) ?? Dot6 !! 1; - $!y-scale = $face.face-flags +& FT_FACE_FLAG_SCALABLE == 0 || ($metrics.y-scale && ($!flags +& FT_LOAD_NO_SCALE == 0)) ?? Dot6 !! 1; - } - method left-bearing returns Rat:D { $.metrics.hori-bearing-x / $!x-scale } - method right-bearing returns Rat:D { - (.hori-advance - .hori-bearing-x - .width) / $!x-scale - with $.metrics - } - method top-bearing returns Rat:D { $.metrics.vert-bearing-y / $!y-scale } - method horizontal-advance returns Rat:D { - $.metrics.hori-advance / $!x-scale; - } - method vertical-advance returns Rat:D { - $.metrics.vert-advance / $!y-scale; - } - method width returns Rat:D { $.metrics.width / $!x-scale } - method height returns Rat:D { $.metrics.height / $!y-scale } - method format returns UInt:D { FT_GLYPH_FORMAT($!raw.format) } - - method glyph-image handles returns Font::FreeType::GlyphImage:D { - my $top = $!raw.bitmap-top; - my $left = $!raw.bitmap-left; - Font::FreeType::GlyphImage.new: :$.face, :glyph($!raw), :$left, :$top, :$.char-code, :$.index, :$.stat; - } - +unit class Font::FreeType::Glyph; + +use Font::FreeType::_Glyph; +also is Font::FreeType::_Glyph; + +use NativeCall; +use Font::FreeType::GlyphImage; +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; +use Font::FreeType::Error; + +use Font::FreeType::BitMap; +constant Dot6 = Font::FreeType::Raw::Defs::Dot6; + +has FT_GlyphSlot $.raw is built handles ; +has UInt:D $.flags = FT_LOAD_DEFAULT; +has Numeric $!x-scale; +has Numeric $!y-scale; + +submethod TWEAK(FT_GlyphSlot:D :$!raw!) { + my FT_Face:D $face := $!raw.face; + my FT_Size_Metrics:D $metrics := $face.size.metrics; + $!x-scale = $face.face-flags +& FT_FACE_FLAG_SCALABLE == 0 || ($metrics.x-scale && ($!flags +& FT_LOAD_NO_SCALE == 0)) ?? Dot6 !! 1; + $!y-scale = $face.face-flags +& FT_FACE_FLAG_SCALABLE == 0 || ($metrics.y-scale && ($!flags +& FT_LOAD_NO_SCALE == 0)) ?? Dot6 !! 1; +} +method left-bearing returns Rat:D { $.metrics.hori-bearing-x / $!x-scale } +method right-bearing returns Rat:D { + (.hori-advance - .hori-bearing-x - .width) / $!x-scale + with $.metrics +} +method top-bearing returns Rat:D { $.metrics.vert-bearing-y / $!y-scale } +method horizontal-advance returns Rat:D { + $.metrics.hori-advance / $!x-scale; +} +method vertical-advance returns Rat:D { + $.metrics.vert-advance / $!y-scale; +} +method width returns Rat:D { $.metrics.width / $!x-scale } +method height returns Rat:D { $.metrics.height / $!y-scale } +method format returns UInt:D { FT_GLYPH_FORMAT($!raw.format) } + +method glyph-image handles returns Font::FreeType::GlyphImage:D { + my $top = $!raw.bitmap-top; + my $left = $!raw.bitmap-left; + Font::FreeType::GlyphImage.new: :$.face, :glyph($!raw), :$left, :$top, :$.char-code, :$.index, :$.stat; } =begin pod diff --git a/lib/Font/FreeType/GlyphImage.rakumod b/lib/Font/FreeType/GlyphImage.rakumod index f7e8799..ae75a0b 100644 --- a/lib/Font/FreeType/GlyphImage.rakumod +++ b/lib/Font/FreeType/GlyphImage.rakumod @@ -1,83 +1,82 @@ #| Glyph images from font typefaces -class Font::FreeType::GlyphImage { - - use Font::FreeType::_Glyph; - also is Font::FreeType::_Glyph; - use NativeCall; - use Font::FreeType::Error; - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - - use Font::FreeType::BitMap; - - has FT_Glyph $.raw is built handles ; - - method !library(--> FT_Library:D) { $.face.ft-lib.raw; } - - submethod TWEAK(FT_GlyphSlot :$glyph!, :$top = $glyph.bitmap-top, :$left = $glyph.bitmap-left,) { - my $glyph-p = Pointer[FT_Glyph].new; - ft-try { $glyph.FT_Get_Glyph($glyph-p) }; - my FT_Glyph $glyph-image = $glyph-p.deref; - - given $glyph-image { - when .format == FT_GLYPH_FORMAT_OUTLINE { - $_ = nativecast(FT_OutlineGlyph, $_); - } - when .format == FT_GLYPH_FORMAT_BITMAP { - $_ = nativecast(FT_BitmapGlyph, $_); - .top = $top; - .left = $left; - } - default { - die "unknown glyph image format: {.format}"; - } - } +unit class Font::FreeType::GlyphImage; - $!raw := $glyph-image; - } - method format returns UInt:D { FT_GLYPH_FORMAT($!raw.format) } +use Font::FreeType::_Glyph; +also is Font::FreeType::_Glyph; +use NativeCall; +use Font::FreeType::Error; +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; + +use Font::FreeType::BitMap; + +has FT_Glyph $.raw is built handles ; - method bold(Int $s) is DEPRECATED { self.set-bold($s) } - method set-bold(Int $strength) { - if self.is-outline { - my FT_Outline:D $outline = $!raw.outline; - ft-try { $outline.FT_Outline_Embolden($strength); }; +method !library(--> FT_Library:D) { $.face.ft-lib.raw; } + +submethod TWEAK(FT_GlyphSlot :$glyph!, :$top = $glyph.bitmap-top, :$left = $glyph.bitmap-left,) { + my $glyph-p = Pointer[FT_Glyph].new; + ft-try { $glyph.FT_Get_Glyph($glyph-p) }; + my FT_Glyph $glyph-image = $glyph-p.deref; + + given $glyph-image { + when .format == FT_GLYPH_FORMAT_OUTLINE { + $_ = nativecast(FT_OutlineGlyph, $_); } - elsif self.is-bitmap { - my FT_Bitmap:D $bitmap = $!raw.bitmap; - ft-try { self!library.FT_Bitmap_Embolden($bitmap, $strength, $strength); }; + when .format == FT_GLYPH_FORMAT_BITMAP { + $_ = nativecast(FT_BitmapGlyph, $_); + .top = $top; + .left = $left; + } + default { + die "unknown glyph image format: {.format}"; } } - method is-bitmap { - .format == FT_GLYPH_FORMAT_BITMAP with $!raw; - } - method to-bitmap( - :$render-mode = FT_RENDER_MODE_NORMAL, - :$origin = FT_Vector.new, - Bool :$destroy = True, - ) { - my FT_BBox $bbox .= new; - $!raw.FT_Glyph_Get_CBox(FT_GLYPH_BBOX_PIXELS, $bbox); - my $raw-p = nativecast(Pointer[FT_Glyph], $!raw); - ft-try { FT_Glyph_To_Bitmap($raw-p, +$render-mode, $origin, $destroy); }; - $!raw = nativecast(FT_BitmapGlyph, $raw-p.deref); - $.left = $bbox.x-min; - $.top = $bbox.y-max; - self; + $!raw := $glyph-image; +} +method format returns UInt:D { FT_GLYPH_FORMAT($!raw.format) } + +method bold(Int $s) is DEPRECATED { self.set-bold($s) } +method set-bold(Int $strength) { + if self.is-outline { + my FT_Outline:D $outline = $!raw.outline; + ft-try { $outline.FT_Outline_Embolden($strength); }; } - method bitmap(UInt :$render-mode = FT_RENDER_MODE_NORMAL --> Font::FreeType::BitMap:D) { - self.to-bitmap(:$render-mode) - unless self.is-bitmap; + elsif self.is-bitmap { my FT_Bitmap:D $bitmap = $!raw.bitmap; - my FT_Bitmap $raw = $bitmap.clone(self!library); - my $top = $.top; - Font::FreeType::BitMap.new: :$.face, :$raw, :$.left, :$top, :$.char-code; + ft-try { self!library.FT_Bitmap_Embolden($bitmap, $strength, $strength); }; } +} - method DESTROY { - $!raw.FT_Done_Glyph; - } +method is-bitmap { + .format == FT_GLYPH_FORMAT_BITMAP with $!raw; +} +method to-bitmap( + :$render-mode = FT_RENDER_MODE_NORMAL, + :$origin = FT_Vector.new, + Bool :$destroy = True, + ) { + my FT_BBox $bbox .= new; + $!raw.FT_Glyph_Get_CBox(FT_GLYPH_BBOX_PIXELS, $bbox); + my $raw-p = nativecast(Pointer[FT_Glyph], $!raw); + ft-try { FT_Glyph_To_Bitmap($raw-p, +$render-mode, $origin, $destroy); }; + $!raw = nativecast(FT_BitmapGlyph, $raw-p.deref); + $.left = $bbox.x-min; + $.top = $bbox.y-max; + self; +} +method bitmap(UInt :$render-mode = FT_RENDER_MODE_NORMAL --> Font::FreeType::BitMap:D) { + self.to-bitmap(:$render-mode) + unless self.is-bitmap; + my FT_Bitmap:D $bitmap = $!raw.bitmap; + my FT_Bitmap $raw = $bitmap.clone(self!library); + my $top = $.top; + Font::FreeType::BitMap.new: :$.face, :$raw, :$.left, :$top, :$.char-code; +} + +method DESTROY { + $!raw.FT_Done_Glyph; } =begin pod diff --git a/lib/Font/FreeType/NamedInfo.rakumod b/lib/Font/FreeType/NamedInfo.rakumod index 1121d93..7c4c06d 100644 --- a/lib/Font/FreeType/NamedInfo.rakumod +++ b/lib/Font/FreeType/NamedInfo.rakumod @@ -1,20 +1,20 @@ #| Information from 'names table' in font file -class Font::FreeType::NamedInfo { - use NativeCall; - use Font::FreeType::Raw; - - has FT_SfntName $!raw handles ; - submethod TWEAK(FT_SfntName:D :$!raw!) { } - - method Str { - my $len := $.string-len; - my buf8 $buf .= allocate($len); - if $len { - Font::FreeType::Raw::memcpy(nativecast(Pointer, $buf), $!raw.string, $len); - } - # todo various encoding schemes - $buf.decode; +unit class Font::FreeType::NamedInfo; + +use NativeCall; +use Font::FreeType::Raw; + +has FT_SfntName $!raw handles ; +submethod TWEAK(FT_SfntName:D :$!raw!) { } + +method Str { + my $len := $.string-len; + my buf8 $buf .= allocate($len); + if $len { + Font::FreeType::Raw::memcpy(nativecast(Pointer, $buf), $!raw.string, $len); } + # todo various encoding schemes + $buf.decode; } =begin pod diff --git a/lib/Font/FreeType/Outline.rakumod b/lib/Font/FreeType/Outline.rakumod index 4e2b46a..319fc52 100644 --- a/lib/Font/FreeType/Outline.rakumod +++ b/lib/Font/FreeType/Outline.rakumod @@ -1,152 +1,151 @@ #| Glyph outlines from font typefaces -class Font::FreeType::Outline { - - has $.face is required; - use NativeCall; - use Font::FreeType::Error; - use Font::FreeType::BBox; - use Font::FreeType::Raw; - use Font::FreeType::Raw::Defs; - use Method::Also; - - method !library(--> FT_Library:D) { - $!face.ft-lib.raw; - } +unit class Font::FreeType::Outline; + +has $.face is required; +use NativeCall; +use Font::FreeType::Error; +use Font::FreeType::BBox; +use Font::FreeType::Raw; +use Font::FreeType::Raw::Defs; +use Method::Also; + +method !library(--> FT_Library:D) { + $!face.ft-lib.raw; +} - enum FT_OUTLINE_OP « - :FT_OUTLINE_OP_NONE - :FT_OUTLINE_OP_MOVE_TO - :FT_OUTLINE_OP_LINE_TO - :FT_OUTLINE_OP_CUBIC_TO - :FT_OUTLINE_OP_CONIC_TO - »; +enum FT_OUTLINE_OP « + :FT_OUTLINE_OP_NONE + :FT_OUTLINE_OP_MOVE_TO + :FT_OUTLINE_OP_LINE_TO + :FT_OUTLINE_OP_CUBIC_TO + :FT_OUTLINE_OP_CONIC_TO + »; - class ft6_shape_t is repr('CStruct') { - has int32 $.n-points; - has int32 $.n-ops; - has int32 $!max-points; - has CArray[uint8] $.ops; - has CArray[num64] $.points; +class ft6_shape_t is repr('CStruct') { + has int32 $.n-points; + has int32 $.n-ops; + has int32 $!max-points; + has CArray[uint8] $.ops; + has CArray[num64] $.points; - submethod TWEAK(:$!max-points!) { } + submethod TWEAK(:$!max-points!) { } - method gather_outlines(FT_Outline $outline, int32 $shift, FT_Pos $delta, uint8 $conic-opt) - returns FT_Error is native($FT-WRAPPER-LIB) is symbol('ft6_outline_gather') {*} + method gather_outlines(FT_Outline $outline, int32 $shift, FT_Pos $delta, uint8 $conic-opt) + returns FT_Error is native($FT-WRAPPER-LIB) is symbol('ft6_outline_gather') {*} - method ft6_outline_gather_done - is native($FT-WRAPPER-LIB) {*} + method ft6_outline_gather_done + is native($FT-WRAPPER-LIB) {*} - submethod DESTROY { - self.ft6_outline_gather_done; - } + submethod DESTROY { + self.ft6_outline_gather_done; } +} - has FT_Outline $!raw handles ; +has FT_Outline $!raw handles ; - submethod TWEAK(:$!raw!) { } +submethod TWEAK(:$!raw!) { } - method !render-shape(ft6_shape_t:D $shape, %cb) { +method !render-shape(ft6_shape_t:D $shape, %cb) { - my $ops = $shape.ops; - my $pts = $shape.points; - my int $j = 0; - my $*cur-x = 0e0; - my $*cur-y = 0e0; + my $ops = $shape.ops; + my $pts = $shape.points; + my int $j = 0; + my $*cur-x = 0e0; + my $*cur-y = 0e0; - with %cb -> &cubic-to { - %cb //= -> $x, $y, $cx, $cy { - &cubic-to( - $x, $y, - ($*cur-x + 2 * $cx) / 3, - ($*cur-y + 2 * $cy) / 3, - (2 * $cx + $x) / 3, - (2 * $cy + $y) / 3, - ); - } + with %cb -> &cubic-to { + %cb //= -> $x, $y, $cx, $cy { + &cubic-to( + $x, $y, + ($*cur-x + 2 * $cx) / 3, + ($*cur-y + 2 * $cy) / 3, + (2 * $cx + $x) / 3, + (2 * $cy + $y) / 3, + ); } + } - for ^$shape.n-ops -> int $i { - my $op = $ops[$i]; - my $key = [$op - 1]; - my $n-args = $op == 1|2 ?? 2 !! 6; - my @args = $pts[$j++] xx $n-args; - with %cb{$key} { - .(|@args); - } - else { - note sprintf("unhandled outline callback: \%s(\%s)", $key, @args>>.fmt('%.2f').join(', ')); - } - $*cur-x = @args[0]; - $*cur-y = @args[1]; + for ^$shape.n-ops -> int $i { + my $op = $ops[$i]; + my $key = [$op - 1]; + my $n-args = $op == 1|2 ?? 2 !! 6; + my @args = $pts[$j++] xx $n-args; + with %cb{$key} { + .(|@args); } + else { + note sprintf("unhandled outline callback: \%s(\%s)", $key, @args>>.fmt('%.2f').join(', ')); + } + $*cur-x = @args[0]; + $*cur-y = @args[1]; } +} - method decompose( - Bool :$conic = False, - Int :$shift = 0, - Int :$delta = 0, - :%callbacks - --> ft6_shape_t:D - ) { - my int32 $max-points = $!raw.n-points * 6; - my ft6_shape_t $shape .= new: :$max-points; - ft-try { $shape.gather_outlines($!raw, $shift, $delta, $conic ?? 1 !! 0); }; - self!render-shape($shape, %callbacks) - if %callbacks; - $shape; - } +method decompose( + Bool :$conic = False, + Int :$shift = 0, + Int :$delta = 0, + :%callbacks + --> ft6_shape_t:D +) { + my int32 $max-points = $!raw.n-points * 6; + my ft6_shape_t $shape .= new: :$max-points; + ft-try { $shape.gather_outlines($!raw, $shift, $delta, $conic ?? 1 !! 0); }; + self!render-shape($shape, %callbacks) + if %callbacks; + $shape; +} - method bbox returns Font::FreeType::BBox:D is also { - my FT_BBox $bbox .= new; - ft-try { $!raw.FT_Outline_Get_BBox($bbox); }; - Font::FreeType::BBox.new: :$bbox; - } +method bbox returns Font::FreeType::BBox:D is also { + my FT_BBox $bbox .= new; + ft-try { $!raw.FT_Outline_Get_BBox($bbox); }; + Font::FreeType::BBox.new: :$bbox; +} - method postscript returns Str:D { - my Str @lines; - my ft6_shape_t $shape = self.decompose; - my $ops = $shape.ops; - my $pts = $shape.points; - my int $j = 0; - for ^$shape.n-ops -> int $i { - my $op = $ops[$i]; - my $ps-op = [$op - 1]; - my $n-args = $op == 1|2 ?? 2 !! 6; - my @args = $pts[$j++] xx $n-args; - @lines.push: @args>>.fmt('%.2f').join(' ') ~ " $ps-op"; - } - @lines.push: ''; - @lines.join: "\n"; +method postscript returns Str:D { + my Str @lines; + my ft6_shape_t $shape = self.decompose; + my $ops = $shape.ops; + my $pts = $shape.points; + my int $j = 0; + for ^$shape.n-ops -> int $i { + my $op = $ops[$i]; + my $ps-op = [$op - 1]; + my $n-args = $op == 1|2 ?? 2 !! 6; + my @args = $pts[$j++] xx $n-args; + @lines.push: @args>>.fmt('%.2f').join(' ') ~ " $ps-op"; } + @lines.push: ''; + @lines.join: "\n"; +} - method svg returns Str:D { - my Str @path; - my ft6_shape_t $shape = self.decompose(:conic); - my $ops = $shape.ops; - my $pts = $shape.points; - my int $j = 0; - for ^$shape.n-ops -> int $i { - my $op = $ops[$i]; - my $svg-op = [$op - 1]; - my $n-args = [2, 2, 6, 4][$op - 1]; - my @args = $pts[$j++] xx $n-args; - @path.push: $svg-op ~ @args>>.fmt('%.2f').join(' '); - } - @path.join: ' '; +method svg returns Str:D { + my Str @path; + my ft6_shape_t $shape = self.decompose(:conic); + my $ops = $shape.ops; + my $pts = $shape.points; + my int $j = 0; + for ^$shape.n-ops -> int $i { + my $op = $ops[$i]; + my $svg-op = [$op - 1]; + my $n-args = [2, 2, 6, 4][$op - 1]; + my @args = $pts[$j++] xx $n-args; + @path.push: $svg-op ~ @args>>.fmt('%.2f').join(' '); } + @path.join: ' '; +} - method bold(Int $strength) { - ft-try { $!raw.FT_Outline_Embolden($strength); } - } +method bold(Int $strength) { + ft-try { $!raw.FT_Outline_Embolden($strength); } +} - method clone returns ::?CLASS:D { - my $outline = $!raw.clone(self!library); - self.new: :raw($outline), :$!face; - } +method clone returns ::?CLASS:D { + my $outline = $!raw.clone(self!library); + self.new: :raw($outline), :$!face; +} - method DESTROY { - ft-try { self!library.FT_Outline_Done($!raw); }; - } +method DESTROY { + ft-try { self!library.FT_Outline_Done($!raw); }; } =begin pod diff --git a/lib/Font/FreeType/_Glyph.rakumod b/lib/Font/FreeType/_Glyph.rakumod index f624b80..348e030 100644 --- a/lib/Font/FreeType/_Glyph.rakumod +++ b/lib/Font/FreeType/_Glyph.rakumod @@ -18,8 +18,8 @@ method name returns Str { $!face.glyph-name-from-index: $.index } method index returns UInt:D { $!glyph-index ||= $!face.raw.FT_Get_Char_Index: $!char-code; } -method is-outline { - .format == FT_GLYPH_FORMAT_OUTLINE with $.raw; +method is-outline returns Bool { + (.format == FT_GLYPH_FORMAT_OUTLINE with $.raw).so; } method outline handles returns Font::FreeType::Outline:D {