This file is indexed.

/usr/share/perl5/PostScript/TextBlock.pm is in libpostscript-perl 0.06-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# -*- Perl -*-
# TextBlock.pm
# An object that may be used to construct a block of text in PostScript
#
package PostScript::TextBlock;
use strict;
use PostScript::Metrics;

use vars qw($VERSION);
$VERSION = '0.06';

# The valid text block attribute names
#
my @paramnames = ( 'text', 'font', 'size', 'leading');

# The default attribute values
#
my %defaults = (
                text    => '',
                font    => 'CharterBT-Roman',
                size    => 12,
                leading => 16
               );
sub new {
    # The constructor method
    #
    my $proto = shift;                   # allow use as a class or object method
    my $class = ref($proto) || $proto;   # see perltoot man page

    # A text block consists of a list of 'elements',
    # (not to be confused with the PostScript::Elements object)
    #
    my $self = [];

    return  bless($self,$class);
}

sub addText {
    # Add an element of text to the TextBlock
    #
    my $self = shift;
    my %params = @_;
    $params{'text'} =~ s/(\(|\))/\\$1/g;    # escape parentheses

    # Use the default values if an attribute is not given
    #
    foreach (@paramnames) {
        $params{$_} = $defaults{$_} unless ($params{$_});
    }
    push @$self, { %params };
}

sub numElements {
    # Returns the number of elements in the TextBlock
    #
    my $self = shift;
    return $#{@$self}+1;
}

sub Write {
	# The Write() method takes four parameters: w, h, x , and y,
	# where w and h are the width and height of the block (in points),
	# and x and y specify the upper left corner of the TextBlock (in the
	# PostScript coordinate system). This method returns a string containing
	# the PostScript code that generated the block, and a TextBlock object
	# the contains the portion that doesn't fit within the given bounds.
	#
	my $self = shift;
	my ($w, $h, $x, $y) = @_;

	my ($x1, $y1) = ($x, $y);
	my $returnval = "";
	my @remainder = ();
	my ($maxlead, $wcount, $linebuffer) = (0, 0, "");
	my ($line, $word, @words);
	my $wordwidth;
	$returnval .= "0 setgray\n";

	my $element = {};
	my $index = 0;
	$element = $self->[$index];

	my $maxindex = $self->numElements;
	my $firstindex = 0;

	ELEMENT:  while (($index < $maxindex) && ($y1 >= ($y-$h))) {
		$wcount = 0;
		($line, $word) = (undef, undef);
		@words = ();
		$linebuffer = "";
		$maxlead = 0;
		$firstindex = $index;

		# Loop until a complete line is formed, or
		# until we run out of elements
		#
		LINE: while (($index < $maxindex) && $wcount<=$w) {
			$linebuffer .= "/$element->{font} findfont\n";
			$linebuffer .= "$element->{size} scalefont setfont\n";

			# Calculate the maximum leading on this line
			#
			$maxlead = $element->{leading} if ($element->{leading} > $maxlead);

			@words = split /( +|\t|\n)/, $element->{text};
			while (@words) {
				$word = shift @words;
				$wordwidth = PostScript::Metrics::stringwidth($word,
																$element->{font},
																$element->{size});

				# If the word is longer than the line, break by character.
				# Note that we could still have the problem of a single
				# character not fitting the width, which we will leave
				# as an exercise for the reader.
				#
				if ($wordwidth > $w) {
                    unshift @words, split //, $word;
                    $word = shift @words;
                    $wordwidth = PostScript::Metrics::stringwidth($word,
                                                                $element->{font},
                                                                $element->{size});
				}
				$wcount += $wordwidth;

				# If we've gone over, push the word back on
				# for later processing.
				#
			        if ( ($wcount>$w) || ($word =~ s/\n//) ) {
                                     if ($word =~ /^ /) { $word =~ s/^[ ]+//; }
                                           unshift @words, $word;
                                           last LINE;
                                   }	
				$line .= $word;
			}
			$index++;
			$element = $self->[$index];
		}

		# Show the line
		#
		if (defined($line)) {
			$linebuffer .= "($line) show\n";
		}

		# Subtract the maximum leading from the current coordinate
		#
		$y1 -= $maxlead;

		# If this line doesn't fit, put the elements making up the line
		# back on for later processing...
		#
		if ($y1 < ($y-$h)) {
			for (my $i=$firstindex; $i < $maxindex; $i++) {
				push @remainder, $self->[$i];
			}
			last ELEMENT;
		} else {
			# Put any remaining words back for later processing
			#
			if (@words) {
				$element->{text} = join '', @words;
			} else {
				$index++;
				$element = $self->[$index];
			}
			$returnval .= "0 setgray $x1 $y1 moveto\n";
			$returnval .= $linebuffer;
		}
	}
	return ($returnval, bless([@remainder], 'PostScript::TextBlock'));
}


sub FitToRegion {
	# The FitToRegion() method takes four parameters: w, h, x , and y,
	# where w and h are the width and height of the block (in points),
	# and x and y specify the upper left corner of the TextBlock (in the
	# PostScript coordinate system). This method returns a string containing
	# the PostScript code that generated the block, and a TextBlock object
	# the contains the portion that doesn't fit within the given bounds.
	#
	my $self = shift;
	my ($w, $h, $x, $y, $minimum_font_size) = @_;

	my ($x1, $y1) = ($x, $y);
	my $returnval = "";
	my @remainder = ();
	my ($maxlead, $wcount, $linebuffer) = (0, 0, "");
	my ($line, $word, @words);
	my $wordwidth;
	$returnval .= "0 setgray\n";

	my $element = {};
	my $index = 0;

	$element = $self->[$index];

	my %original_element = {};
	foreach (keys %$element) {
		$original_element{$_} = $self->[$index]->{$_};
	} # foreach

	my $maxindex = $self->numElements;
	my $firstindex = 0;

	ELEMENT:  while (($index < $maxindex) && ($y1 >= ($y-$h))) {
		$wcount = 0;
		($line, $word) = (undef, undef);
		@words = ();
		$linebuffer = "";
		$maxlead = 0;
		$firstindex = $index;

		# Loop until a complete line is formed, or
		# until we run out of elements
		#
		LINE: while (($index < $maxindex) && $wcount<=$w) {
			$linebuffer .= "/$element->{font} findfont\n";
			$linebuffer .= "$element->{size} scalefont setfont\n";

			# Calculate the maximum leading on this line
			#
			$maxlead = $element->{leading} if ($element->{leading} > $maxlead);

			@words = split /( +|\t|\n)/, $element->{text};
			while (@words) {
				$word = shift @words;
				$wordwidth = PostScript::Metrics::stringwidth($word,
																$element->{font},
																$element->{size});

				# If the word is longer than the line, break by character.
				# Note that we could still have the problem of a single
				# character not fitting the width, which we will leave
				# as an exercise for the reader.
				#
#				if ($wordwidth > $w) {
#                    unshift @words, split //, $word;
#                    $word = shift @words;
#                    $wordwidth = PostScript::Metrics::stringwidth($word,
#                                                                $element->{font},
#                                                                $element->{size});
#				} # if
				$wcount += $wordwidth;

				# If we've gone over, push the word back on
				# for later processing.
				#
				if (($wcount > $w) || ($word =~ s/\n//)) {
					unshift @words, $word;
					last LINE;
				} # if
				$line .= $word;
			} # while
			$index++;
			$element = $self->[$index];
		} # while

		# Show the line
		#
		if (defined($line)) {
			$linebuffer .= "($line) show\n";
		}

		# Subtract the maximum leading from the current coordinate
		#
		$y1 -= $maxlead;

		# If this line doesn't fit, put the elements making up the line
		# back on for later processing...
		#
		if ($y1 < ($y-$h)) {
			for (my $i=$firstindex; $i < $maxindex; $i++) {
				push @remainder, $self->[$i];
			}
			last ELEMENT;
		} else {
			# Put any remaining words back for later processing
			#
			if (@words) {
				$element->{text} = join '', @words;
			} else {
				$index++;
				$element = $self->[$index];
			} # if
			$returnval .= "0 setgray $x1 $y1 moveto\n";
			$returnval .= $linebuffer;
		} # else
	} # while

	if (@remainder and ($original_element{size} - 1 >= $minimum_font_size)) {
		--$original_element{size};
		if ($original_element{leading}) {
			--$original_element{leading};
		} # if
		$self->[0] = { %original_element };
		($returnval, @remainder) = &FitToRegion($self, $w, $h, $x, $y, $minimum_font_size);
	} # if

	return ($returnval, bless([@remainder], 'PostScript::TextBlock'));
#	return $returnval;
} # FitToRegion
1;      # All Perl modules should return true


__END__

=head1 NAME

PostScript::TextBlock - An object that may be used to construct a block of
                text in PostScript.

=head1 SYNOPSIS

    use PostScript::TextBlock;
    my $tb = new PostScript::TextBlock;
    $tb->addText( text => "Hullaballo in Hoosick Falls.\n",
                  font => 'CenturySchL-Ital',
                  size => 24,
                  leading => 26
                 );
    $tb->addText( text => "by Charba Gaspee.\n",
                  font => 'URWGothicL-Demi',
                  size => 12,
                  leading => 14
                 );
    print 'There are '.$tb->numElements.' elements in this object.';
    open OUT, '>psoutput.ps';
    my ($code, $remainder) = $tb->Write(572, 752, 20, 772);
    print OUT $code;

=head1 DESCRIPTION


The PostScript::TextBlock module implements four methods:

=over 3

=item new() - Create a New PostScript::TextBlock object

This method instantiates a new object of class PostScript::TextBlock.

=item addText( text=>$text,
               [ font=>$font ],
               [ size=>$size ],
               [ leading=>$leading ] )

The addText() method will add a new 'text element' to the TextBlock object. A
'text element' can be thought of as a section of text that has the same
characteristics, i.e. all the characters are the same font, size and leading.
this representation allows you to include text rendered in multiple fonts at
multiple sizes within the same text block by including them as separate
elements.

This method takes up to four attributes (note that the '[]' brackets above
indicate that a parameter is optional, not an array reference):

text
The text attribute is required, though nothing bad will happen if you leave it
out. This is simply the text to be rendered in the text block. Line breaks may
be inserted by including a newline "\n".

font
The font attribute is a string indicating the name of the font to be used to
render this element. The PS package uses an internal description of the Font
Metrics of various fonts that is contained in the PostScript::Metrics module. As of
this writing, the PostScript::Metrics module supports the following fonts (basically,
the default GhostScript fonts that have AFM files):

NimbusSanL-ReguCond	  URWGothicL-Book
CenturySchL-Bold          CharterBT-Italic
URWBookmanL-Ligh          CharterBT-BoldItalic
NimbusRomNo9L-ReguItal    URWBookmanL-DemiBoldItal
CharterBT-Roman           NimbusMonL-ReguObli
NimbusSanL-ReguCondItal   CenturySchL-Ital
CenturySchL-BoldItal      URWPalladioL-Roma
URWBookmanL-LighItal      CharterBT-Bold
NimbusSanL-BoldCond       NimbusMonL-BoldObli
NimbusSanL-BoldCondItal   URWGothicL-DemiObli
NimbusSanL-Regu           URWPalladioL-Bold
NimbusMonL-Regu           NimbusSanL-ReguItal
URWGothicL-BookObli       URWPalladioL-Ital

You can get a list of the currently supported fonts with the following:

    use PostScript::Metrics;
    @okfonts = PostScript::Metrics->listFonts();

NOTE: The font must be available to the PostScript interpreter that is used
to render the page described by the program. If the interpreter cannot load
the font, it will ususally attempt to substitute a similar font. If a font is
substituted with a font with different metrics, lines of text may overrun the
right margin of the text block. You have been warned.

It is very easy to create stylesheets for a document:

    # Define the styles
    #
    %body = ( font => 'URWGothicL-DemiObli', size => 12, leading => 16 );
    %head1 = ( font => 'NimbusSanL-BoldCond', size => 24, leading => 36 );
    %head2 = ( font => 'NimbusSanL-BoldCond', size => 18, leading => 30 );

    # Use them where appropriate
    #
    $tb->addText(text => "Chapter 10\n", %head1);
    $tb->addText(text => "Spokane Sam and His Spongepants\n", %head2);
    $tb->addText(text => "It was a dark and stormy night and Spokane Sam\'s
    Spongepants were thirsty...", %body);

=back

=over 3

=item numElements()

Returns the number of elements in the text block object. An 'element' is
created each time the addText() method is called.

=item Write( $width, $height, $xoffset, $yoffset )

The Write() method will generate the PostScript code that will render the text
on a page when passed to a PostScript interpreter such as Ghostscript. The
four parameters are expressed in points (1/72 inch) and indicate the width and
height of the box within which the text should be printed, and the x and y
offset of the upper left corner of this box.

Important: PostScript defines the orgin (0,0) as the lower left corner of
the page! This *will* mess you up.

Standard page sizes in points are:

     Paper Size                      Width, Height (in points)
     .........................       .........................
     Letter                          612, 792
     Legal                           612, 1008
     Ledger                          1224, 792
     Tabloid                         792, 1224
     A0                              2384, 3370
     A1                              1684, 2384
     A2                              1191, 1684
     A3                              842, 1191
     A4                              595, 842
     A5                              420, 595
     A6                              297, 420
     A7                              210, 297
     A8                              148, 210
     A9                              105, 148
     B0                              2920, 4127
     B1                              2064, 2920
     B2                              1460, 2064
     B3                              1032, 1460
     B4                              729, 1032
     B5                              516, 729
     B6                              363, 516
     B7                              258, 363
     B8                              181, 258
     B9                              127, 181
     B10                             91, 127
     #10 Envelope                    297, 684
     C5 Envelope                     461, 648
     DL Envelope                     312, 624
     Folio                           595, 935
     Executive                       522, 756

The write() method returns two values: a string consisting of the PostScript
code (suitable for printing to a file), and a TextBlock object containing the
elements (and partial elements) that did not fit within the specified area,
if any. If the entire text block fits with the area, the remainder will be
undef. The remainder can be used to layout multiple pages and columns, etc. in
a similar manner to most modern desktop publishing programs. In general, the
write() method should be called as in the following, which writes the
PostScript code to a file called 'psoutput.ps':

    open OUT, '>psoutput.ps';
    my ($code, $remainder) = $tb->Write(572, 752, 20, 772);
    print OUT $code;

To print an entire text block that spans multiple pages, you could do
something like this:

(add enough text to the text block first..)

    open OUT, '>psoutput.ps';
    my $pages = 1;

    # Create the first page
    #
    my ($code, $remainder) = $tb->Write(572, 752, 20, 772);
    print OUT "%%Page:$pages\n";      # this is required by the Adobe
                                      # Document Structuring Conventions
    print OUT $code;
    print OUT "showpage\n";

    # Print the rest of the pages, if any
    #
    while ($remainder->numElements) {
        $pages++;
        print OUT "%%Page:$pages\n";
        ($code, $remainder) = $remainder->Write(572, 752, 20, 772);
        print OUT $code;
        print OUT "showpage\n";
    }

However, if you use the PostScript::Document module to construct generic
multi-page PostScript documents, you don't have to worry about this.

=back

=head1 A NOTE ABOUT FONT METRICS

The write() method uses the module PostScript::Metrics to determine the width of
each character; widths vary from font to font and character to character.
If you were writing a stright PostScript program, you would let the PostScript
interpreter do this for you, but in the case of this program, we need to know
the width of each character in a font within the Perl script. The PostScript::Metrics
module contains the font metrics (i.e., a list containing the width of each
character in the font) for a bunch of fonts that are listed above under the
description of the addText() method. This set started with the metrics for all
of the default fonts with AFM files that came with GhostScript. It is slowly
growing as more fonts are mapped. To add support for a new font, you must
create the array with the metrics for that font and add it to the PostScript::Metrics
module. For a font with an AFM file, the AFM file can be parsed with Gisle
Aas' Font::AFM module, available on CPAN.

Please send all PostScript::Metrics patches to the author at shawn@as220.org.

=head1 TODO

* better compliance with Adobe's Document Structuring Conventions
* more font metrics descriptions
* make font loading code smarter and more efficient for the interpreter
* support a larger character set
* it would be nice to add more functions, e.g. Clone()
* how about settable defaults?

=head1 AUTHOR

Copyright 1998, 1999 Shawn Wallace. All rights reserved.

Contact the author: shawn@as220.org
http://www.as220.org/shawn

Portions of code contributed by Dan Smeltz.

This is free software. You may use, modify, and
redistribute this package under the same terms as Perl itself.

PostScript is a trademark of Adobe Systems.

=cut