File Coverage

blib/lib/HarfBuzz/Shaper.pm
Criterion Covered Total %
statement 86 135 63.7
branch 16 42 38.1
condition 9 25 36.0
subroutine 14 21 66.6
pod 15 15 100.0
total 140 238 58.8


line stmt bran cond sub pod time code
1             #! perl
2              
3             package HarfBuzz::Shaper;
4              
5 5     5   424211 use 5.010001;
  5         49  
6 5     5   29 use strict;
  5         10  
  5         154  
7 5     5   27 use warnings;
  5         10  
  5         160  
8 5     5   27 use Carp;
  5         15  
  5         336  
9 5     5   2915 use Encode;
  5         52747  
  5         6838  
10              
11             our $VERSION = '0.025';
12              
13             require XSLoader;
14             XSLoader::load('HarfBuzz::Shaper', $VERSION);
15              
16             =head1 NAME
17              
18             HarfBuzz::Shaper - Use HarfBuzz for text shaping
19              
20             =head1 SYNOPSIS
21              
22             use HarfBuzz::Shaper;
23             my $hb = HarfBuzz::Shaper->new;
24             $hb->set_font('LiberationSans.ttf');
25             $hb->set_size(36);
26             $hb->set_text("Hello!");
27             my $info = $hb->shaper;
28              
29             The result is an array of hashes, one element for each glyph to be typeset.
30              
31             =head1 DESCRIPTION
32              
33             HarfBuzz::Shaper is a perl module that provides access to a small
34             subset of the native HarfBuzz library.
35              
36             The subset is suitable for typesetting programs that need to deal with
37             complex languages like Devanagari, Hebrew or Arabic.
38              
39             This module is intended to be used with module L. Feel
40             free to (ab)use it for other purposes.
41              
42             Following the above example, the returned info is an array of hashes,
43             one element for each glyph to be typeset. The hash contains the
44             following items:
45              
46             ax: horizontal advance
47             ay: vertical advance
48             dx: horizontal offset
49             dy: vertical offset
50             g: glyph index in font (CId)
51             name: glyph name
52              
53             Note that the number of glyphs does not necessarily match the number
54             of input characters!
55              
56             =head1 DISCLAIMER
57              
58             This module provides a thin interface layer between Perl and the
59             native HarfBuzz library. It is agnostic with regard to the details of
60             multi-language typesetting. HarfBuzz has a friendly community to help
61             you.
62              
63             L
64              
65             =head1 METHODS
66              
67             =head2 $hb = HarfBuzz::Shaper->new( [ options ] )
68              
69             Creates a new shaper object.
70              
71             Options:
72              
73             =over 4
74              
75             =item *
76              
77             B > I
78              
79             =item *
80              
81             B > I
82              
83             =back
84              
85             =cut
86              
87             sub new {
88 4     4 1 488 my ( $pkg, $opts ) = @_;
89              
90 4   50     34 $opts //= {};
91              
92 4         11 my $self = bless {} => $pkg;
93 4         88 $self->{harfbuzz} = hb_version_string();
94 4         389 $self->{buffer} = hb_buffer_create();
95 4         17 $self->{features} = [];
96              
97 4 50       16 if ( $opts->{font} ) {
98 0         0 $self->set_font( delete $opts->{font} );
99             }
100 4 50       14 if ( $opts->{size} ) {
101 0         0 $self->set_size( delete $opts->{size} );
102             }
103              
104 4         19 return $self;
105             }
106              
107             =head2 $hb->reset( [ I ] )
108              
109             Reset (clear) the buffer settings for font, size, language, direction
110             and script. With I, also clears the font cache.
111              
112             =cut
113              
114             sub reset {
115 0     0 1 0 my ( $self, $full ) = @_;
116              
117 0         0 for ( qw ( font size text language direction script shaped ) ) {
118 0         0 delete $self->{$_};
119             }
120 0 0       0 if ( $full ) {
121 0         0 for ( keys %$self ) {
122 0 0       0 next unless /^(font|face)_/;
123 0         0 delete $self->{$_};
124             }
125 0         0 hb_buffer_reset( $self->{buffer} );
126             # So basically we are like freshly created.
127             }
128              
129 0         0 $self;
130             }
131              
132             =head2 $hb->set_font( I [ , I ] )
133              
134             Explicit way to set the font (and, optionally, the size) used for
135             shaping.
136              
137             The settings persist across shaper() calls. Call without arguments to
138             remove the settings.
139              
140             The font must be a TrueType or OpenType font. Font information is
141             cached internally, after the first call subsequent calls with the same
142             font filename are very fast.
143              
144             =cut
145              
146             sub set_font {
147 4     4 1 32 my ( $self, $fontfile, $size ) = @_;
148              
149 4         10 delete $self->{shaped};
150 4 50 33     19 unless ( defined $fontfile or defined $size ) {
151 0         0 delete $self->{font};
152 0         0 delete $self->{size};
153 0         0 return $self;
154             }
155              
156 4 50       210 croak("$fontfile: $!\n") unless -s -r $fontfile;
157 4         3164 my $blob = hb_blob_create_from_file($fontfile);
158 4   33     459 my $face = $self->{"face_$fontfile"} //= hb_face_create( $blob, 0 );
159 4   33     91 $self->{font} = $self->{"font_$fontfile"} //= do {
160             # hb_font_create should default to OT.
161 4         282 my $font = hb_font_create( $face );
162 4         30 hb_ot_font_set_funcs($font);
163 4         18 $font;
164             };
165 4 50       15 $self->set_size($size) if $size;
166              
167 4         27 $self;
168             }
169              
170             =head2 $hb->set_size( I )
171              
172             Explicit way to set the font size used for shaping.
173              
174             Note that the font size will in general affect details of the
175             appearance, A 5 point fontsize magnified 10 times is not identical to
176             50 point font size.
177              
178             The setting persist across shaper() calls. Call without arguments to
179             remove the setting.
180              
181             =cut
182              
183             sub set_size {
184 4     4 1 31 my ( $self, $size ) = @_;
185              
186 4         9 delete $self->{shaped};
187 4 50       14 unless ( defined $size ) {
188 0         0 delete $self->{size};
189 0         0 return $self;
190             }
191              
192 4         11 $self->{size} = $size;
193              
194 4         15 $self;
195             }
196              
197             =head2 $hb->set_text( I [ , ... ] )
198              
199             Sets the text to shape. Multiple arguments are concatenated.
200              
201             Note that the text must be Perl strings.
202              
203             The setting persist across shaper() calls. Call without arguments to
204             remove the setting.
205              
206             =cut
207              
208             sub set_text {
209 4     4 1 29 my ( $self, @text ) = @_;
210              
211 4         8 delete $self->{shaped};
212 4 50 33     47 unless ( @_ > 1 and defined $text[0] ) {
213 0         0 delete $self->{text};
214 0         0 return $self;
215             }
216              
217 4         25 $self->{text} = join( "", @text );
218              
219 4         12 $self;
220             }
221              
222             =head2 $hb->set_features( I [ , ... ] )
223              
224             Sets persistent features for shaping. Features are strings as described in
225             L
226             and
227             L.
228              
229             Multiple feature strings may be supplied.
230              
231             Call without arguments to remove the persistent features.
232              
233             =cut
234              
235             sub set_features {
236 2     2 1 561 my ( $self ) = shift;
237 2         6 $self->{features} = [];
238 2 50 33     16 $self->add_features(@_) if @_ && defined($_[0]);
239 2         5 return $self;
240             }
241              
242             =head2 $hb->add_features( I [ , ... ] )
243              
244             Just like set_features, but the specified features are I to the
245             set of persistent features.
246              
247             =cut
248              
249             sub add_features {
250 2     2 1 6 my ( $self, @features ) = @_;
251 2         5 delete $self->{shaped};
252 2         4 foreach my $feature ( @features ) {
253 2   33     4 push( @{ $self->{features} },
  2         37  
254             hb_feature_from_string($feature)
255             || croak("Unknown shaper feature: \"$feature\"") );
256             }
257             }
258              
259             =head2 $hb->set_language( I )
260              
261             Sets the language for shaping. I must be a string containing a
262             valid BCP-47 language code.
263              
264             The setting persist across shaper() calls. Call without arguments to
265             remove the setting.
266              
267             =cut
268              
269             sub set_language {
270 0     0 1 0 my ( $self, $lang ) = @_;
271              
272 0         0 delete $self->{shaped};
273 0 0       0 unless ( defined $lang ) {
274 0         0 delete $self->{language};
275 0         0 return $self;
276             }
277              
278 0         0 $self->{language} = $lang;
279             # This is merely for checking validity;
280 0         0 hb_buffer_set_language( $self->{buffer}, $lang );
281             }
282              
283             =head2 $hb->get_language
284              
285             Returns the language currently set for this shaper, as a string.
286              
287             When called after a successful shaper() call, it returns the actual
288             value used by shaper().
289              
290             =cut
291              
292             sub get_language {
293 0     0 1 0 my ( $self ) = @_;
294 0         0 hb_buffer_get_language( $self->{buffer} );
295             }
296              
297             =head2 $hb->set_script( I