forked from metaskills/pdf-writer
-
Notifications
You must be signed in to change notification settings - Fork 1
/
manual.pwd
5965 lines (4967 loc) · 233 KB
/
manual.pwd
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
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# This is the main manual for PDF::Writer. It is interpreted by PDF::TechBook
# which implements an interpreter for the simple markup language presented
# here.
#
# 0. Comments, headings, and directives must begin in column 0 and consume
# the whole line.
# 1. Comments begin with '#' at the beginning of the line.
# 2. Directives are in the form '.<directive>[ [<args>] <comment>]', where
# both args (arguments) and comment are optional. The arguments must be
# separated from the directive by a space and comments must be separated
# from the arguments by a space. The format of the arguments is directive
# dependent. Directive names must begin with a letter, may only contain
# the word characters.
# 2.1. .newpage starts a new page.
# 2.2. .pre starts a "preserved newlines" block of text. This is similar to
# normal text (see #4) but the lines are not flowed. This is useful for
# creating lists or other text that must be in a particular form
# without switching to a code font. .pre blocks may not be nested in
# any other block (that is to say, when an .endpre is encountered, the
# text state is set to normal text).
# 2.3. .endpre ends a preserved newlines block of text.
# 2.4. .code starts a "code" block of text. Similar to .pre, this changes
# the display font to the "code" font for the text, which is generally
# a Courier font. .code blocks may be nested in .pre blocks (when an
# .endcode is encountered, the text state is restored to the previous
# text state, either normal or preserved newlines).
# 2.5. .endcode ends a code block of text.
# 2.6. .eval starts an eval block. Text between this line and .endeval will
# be collected and passed to Ruby's Kernel#eval at $SAFE level 3. .eval
# blocks may be nested in .pre or .code blocks.
# 2.7. .endeval ends an eval block.
# 2.8. .done stops processing on the document.
# 2.9. .columns turns on and off column output. The number of columns is
# specified as an argment, e.g., ".columns 3". Turn columns off with
# ".columns 1" or ".columns 0". A second parameter may be provided
# specifying the gutter width (e.g., ".columns 3 15").
# 2.10. .toc indicates that the table of contents should be generated with
# the argument value as the title of the table of contents page. This
# is an advisory value only. If this is not present, a table of
# contents will not be automatically generated.
# 2.11. .author, .title, .subject, and .keywords set values in the PDF info
# object. The arguments are used as the values.
# 2.12. .blist starts a list block. Lists may be nested within each other,
# and will successively shift the left margin by 10% of the initial
# left margin value. Each line in the list block will be treated as a
# separate list item with a bullet inserted in front. This will be
# inserted as a callback (e.g., <C:bullet/> or <C:disc/>) before the
# text of the list item.
# 2.13. .endblist will end the current list block.
# 3. Headings are added to the text with the form '#<heading-text>' where #
# is the level of the heading. The only heading levels currently
# supported are 1 - 5. Heading levels 1 and 2 will automatically be added
# to the table of contents. Headings *may* include markup (see #5), but
# should not exceed a single line (the heading box is not currently set
# up for this). All heading levels may also have an optional name
# comprised word characters (a-zA-Z0-9_) for automated cross-reference
# building. The form will be like this: '1<chapter>name'. If the name is
# not provided, the cross-reference identifier will be the position of
# the identifier.
# 3.1. 1<chapter> is a first-level heading with text of "chapter". Usually
# used for chapter headings.
# 3.2. 2<section> is a second-level heading with text of "section". Usually
# used for section headings.
# 3.3. 3<section>, 4<section>, and 5<section>, are headings that use the
# text of "section" and identify various subsections of the text.
# 3.4. Automated cross-reference handling is achived with the replacing text
# directive 'xref': <r:xref name='name' label='labeltype'>. The name
# will be the name associated with the heading level and labeltype will
# be either 'page', 'title', or 'text'. The cross-reference directive
# is replaced with either the page number or the section label. The
# page type can only be replaced if the cross-reference target has
# already been placed on a document page.
# 4. Normal text is treated as flowed text. That is, two lines back to back
# will be joined into a single line and then wrapped by the text
# formatting within PDF::Writer. Paragraphs are separated by an empty
# line, a valid directive, or a heading.
# 5. Within normal, preserved, code text, or headings, HTML-like markup may
# be used for bold (<b></b>), italic (<i></i>), superscript (<sup></sup>),
# and subscript (<sub></sub>).
# Other markup options may be used with the callback tag. Clickable
# external hyperlinks may be added by using the 'alink' callback:
# <c:alink uri="URI">LABEL</c:alink>
# 6. Special characters (e.g., <, &, and >) must be escaped in the HTML
# style.
#
# Please note that this syntax will be evolving in future versions of
# PDF::Writer.
#
# Austin Ziegler
# Tuesday, 3 May 2005
#--
# We want a table of contents for this document.
.toc Table of Contents
# Set the PDF document info.
.author Austin Ziegler
.title PDF::Writer for Ruby Manual
.subject The use and care of PDF::Writer for Ruby.
# Use .eval to do encoding configuration.
.eval {{{
pdf.techbook_encoding = {
:encoding => "WinAnsiEncoding",
:differences => {
169 => "copyright",
215 => "multiply",
}
}
.endeval }}}
# Use .eval to create the heading and footing.
.eval {{{
pdf.select_font "Helvetica", pdf.techbook_encoding
# Put a heading on all pages.
pdf.open_object do |heading|
pdf.save_state
pdf.stroke_color! Color::RGB::Black
pdf.stroke_style! PDF::Writer::StrokeStyle::DEFAULT
s = 6
t = "PDF::Writer for Ruby ~ Manual"
w = pdf.text_width(t, s) / 2.0
x = pdf.margin_x_middle
y = pdf.absolute_top_margin
pdf.add_text(x - w, y, t, s)
x = pdf.absolute_left_margin
w = pdf.absolute_right_margin
y -= (pdf.font_height(s) * 1.01)
pdf.line(x, y, w, y).stroke
pdf.restore_state
pdf.close_object
pdf.add_object(heading, :all_following_pages)
end
# Put a footing on all pages.
pdf.open_object do |footing|
pdf.save_state
pdf.stroke_color! Color::RGB::Black
pdf.stroke_style! PDF::Writer::StrokeStyle::DEFAULT
s = 6
t = "http://rubyforge.org/projects/ruby-pdf"
x = pdf.absolute_left_margin
y = pdf.absolute_bottom_margin
pdf.add_text(x, y, t, s)
x = pdf.absolute_left_margin
w = pdf.absolute_right_margin
y += (pdf.font_height(s) * 1.05)
pdf.line(x, y, w, y).stroke
pdf.restore_state
pdf.close_object
pdf.add_object(footing, :all_following_pages)
end
.endeval }}}
# Use .eval to create the title page and start page numbering.
.eval {{{
# Get the info we need for placing page numbering after we draw the title
# page.
pny = pdf.absolute_bottom_margin
pnx = pdf.absolute_right_margin
pdf.margins_pt(54) # 54 point (3/4") margins on all sides...
pdf.left_margin = 72 # ...except the left margin (1")
image = File.join(pdf.techbook_source_dir, "images", "bluesmoke.jpg")
pdf.add_image_from_file(image, 300, 50)
# Draw the title page.
title = "PDF::Writer for Ruby"
size = 72
fh = pdf.font_height(size) * 1.01
fd = pdf.font_descender(size) * 1.01
pdf.save_state
pdf.fill_color Color::RGB::Red
pdf.stroke_color Color::RGB::Red
pdf.rectangle(pdf.absolute_left_margin, 0, fh, pdf.page_height).fill
pdf.fill_color Color::RGB::White
pdf.stroke_color Color::RGB::White
pdf.add_text(pdf.absolute_left_margin + fh + fd, 70, title, size, 90)
pdf.restore_state
pdf.select_font pdf.techbook_textfont, pdf.techbook_encoding
pdf.text("\nNative Ruby PDF Document Creation\n", :font_size => 24, :justification => :right)
info = <<-"INFO"
The Ruby PDF Project
<c:alink uri="http://rubyforge.org/projects/ruby-pdf/">http://rubyforge.org/projects/ruby-pdf</c:alink>
version #{PDF::Writer::VERSION}
INFO
pdf.text(info, :font_size => 20, :justification => :right)
info = <<-INFO
Copyright © 20032005
<c:alink uri="mailto:[email protected]">Austin Ziegler</c:alink>
INFO
pdf.text(info, :font_size => 18, :justification => :right)
pdf.open_here("Fit")
pdf.techbook_fontsize = 12
.endeval }}}
.newpage
.eval {{{
pnx = pdf.absolute_right_margin
pdf.start_page_numbering(pnx, 36, 6, :right, "<PAGENUM>", 1)
.endeval }}}
# The main document.
1<Introduction to PDF::Writer for Ruby>Introduction
PDF::Writer is designed to provide a pure Ruby way to dynamically create PDF
documents. Obviously, this will not be as fast as one that uses a compiled
extension, but it is surprisingly fast. This manual is, in some ways, a worst
case scenario because of the number of examples that must be displayed.
PDF::Writer does not yet implement the full PDF specification (any version),
but it implements a substantial portion of it, with more to come. It also
implements a more complex document layer on top of the portion that is
implemented.
This manual (manual.pdf) is generated by PDF::TechBook using the application
runner (bin/techbook) and the text version of the manual (manual.pwd). It is
a comprehesive introduction to the use of PDF::Writer and a simple markup
language interpreter. PDF::Writer itself only implements a few markup items,
mostly relating to bold and italic text.
PDF::Writer is based on Adobes PDF Reference, Fifth Edition, version 1.6.
This and earlier editions are available from the
<c:alink
uri="http://partners.adobe.com/public/developer/pdf/index_reference.html">Adobe
website</c:alink>. The original implementation was a port of the public
domain <c:alink uri="http://www.ros.co.nz/pdf/">R&OS PDF Class for PHP</c:alink>.
Other demo programs are available in the <b>demo/</b> directory.
Alternatively, they may be downloaded separately from the <c:alink
uri="http://rubyforge.org/projects/ruby-pdf">Ruby PDF Tools</c:alink> project on RubyForge.
2<Installation>
If this manual was generated from a local installation of PDF::Writer, then
congratulations! PDF::Writer is installed. Otherwise, you are reading a
manual generated otherwise. If you want to install PDF::Writer, you can
download it from the Ruby PDF Tools project on RubyForge or install it with
RubyGems.
PDF::Writer has dependencies on <c:alink
uri="http://rubyforge.org/frs/?group_id=295&release_id=2130">Transaction::Simple
1.3.0</c:alink> or later and <c:alink
uri="http://rubyforge.org/frs/?group_id=81&release_id=2127">color-tools
1.0.0</c:alink> or later. These must be installed for PDF::Writer to work.
RubyGems installation will automatically detect and offer to download and
install these dependencies.
PDF::Writer is installed with:
.code {{{
% ruby setup.rb
.endcode }}}
.newpage
1<Making Documents with PDF::Writer>MakingDocuments
Document creation with PDF::Writer is quite easy. The following code will
create a single-page document that contains the phrase Hello, Ruby.
centered at the top of the page in 72-point (1) type.
.code {{{
# This code is demo/hello.rb
require "pdf/writer"
pdf = PDF::Writer.new
pdf.select_font "Times-Roman"
pdf.text "Hello, Ruby.", :font_size => 72, :justification => :center
File.open("hello.pdf", "wb") { |f| f.write pdf.render }
.endcode }}}
This one, on the other hand, uses a very (in)famous phrase and image. Note
that the images are JPEG and PNGPDF::Writer cannot use GIF images.
.code {{{
# This code is demo/chunkybacon.rb
require "pdf/writer"
pdf = PDF::Writer.new
pdf.select_font "Times-Roman"
pdf.text "Chunky Bacon!!", :font_size => 72, :justification => :center
# PDF::Writer#image returns the image object that was added.
i0 = pdf.image "../images/chunkybacon.jpg", :resize => 0.75
pdf.image "../images/chunkybacon.png",
:justification => :center, :resize => 0.75
# It can reinsert an image if wanted.
pdf.image i0, :justification => :right, :resize => 0.75
pdf.text "Chunky Bacon!!", :font_size => 72, :justification => :center
File.open("chunkybacon.pdf", "wb") { |f| f.write pdf.render }
.endcode }}}
If those arent enough to whet your appetite for how easy PDF::Writer can be,
well, no chunky bacon for you!
.newpage
1<Fundamental PDF Concepts>FundamentalConcepts
This section covers fundamental concepts to the creation of PDF documents
that will assist in the understanding of the details that follow in later
sections.
2<PDF Coordinate Space and User Units>CoordinateSpace
PDF documents dont use inches or millimetres for measurement, and the
coordinate space is slightly different than that used by other graphics
canvases.
3<PDF User Units>
PDF user units are by default points. A modern point is exactly 1/72 (1/72
inch); it is generally accepted that 72 point type is one inch in height.
Historically, one point was 0.0138, or just under 1/72.
.eval {{{
pdf.select_font("Helvetica")
PDF::SimpleTable.new do |tab|
tab.title = "PDF User Unit Conversions"
tab.column_order.push(*%w(from1 to1 from2 to2))
tab.columns["from1"] = PDF::SimpleTable::Column.new("from1") { |col|
col.heading = "From"
}
tab.columns["to1"] = PDF::SimpleTable::Column.new("to1") { |col|
col.heading = "To"
}
tab.columns["from2"] = PDF::SimpleTable::Column.new("from2") { |col|
col.heading = "From"
}
tab.columns["to2"] = PDF::SimpleTable::Column.new("to2") { |col|
col.heading = "To"
}
tab.show_lines = :all
tab.show_headings = true
tab.orientation = :center
tab.position = :center
data = [
{ "from1" => "1 point", "to1" => "0.3528 mm",
"from2" => "1 point", "to2" => "1/72" },
{ "from1" => "10 mm", "to1" => "28.35 pts",
"from2" => "", "to2" => "" },
{ "from1" => "A4", "to1" => "210 mm × 297 mm",
"from2" => "A4", "to2" => "595.28 pts × 841.89 pts" },
{ "from1" => "LETTER", "to1" => "8½ × 11",
"from2" => "LETTER", "to2" => "612 pts × 792 pts" },
]
tab.data.replace data
tab.render_on(pdf)
end
.endeval }}}
It is possible to scale the user coordinates with PDF::Writer#scale_axis.
With scale_axis(0.5, 0.5), each coordinate is ½ point in size (1/144) in
both the x and y directions. See the following discussion on PDF Coordinate
Space for the limitation of performing transformations on the coordinate
axis.
PDF::Writer provides utility methods to convert between normal measurement
units and and points. These utility methods are available either as class
mehods or as instance methods.
.eval {{{
$rm = pdf.right_margin
pdf.right_margin += 175
y = pdf.absolute_bottom_margin + 40
pdf.stroke_style(PDF::Writer::StrokeStyle.new(1))
pdf.rectangle(400, y, 150, 200).stroke
x0y0 = "(0, 0)"
hh = pdf.font_height(9) + pdf.font_descender(9)
pdf.add_text(405, y + hh - 2, x0y0, 9)
x1y1 = "(150, 200)"
ww = pdf.text_width(x1y1, 9)
pdf.add_text(545 - ww, y + 198 - hh, x1y1, 9)
.endeval }}}
4<PDF::Writer.cm2pts(x)>
Converts from centimetres to points.
4<PDF::Writer.in2pts(x)>
Converts from inches to points.
4<PDF::Writer.mm2pts(x)>
Converts from millimetres to points.
3<PDF Coordinate Space>PDFCoordinateSpace
The standard PDF coordinate space is a little different than might be
expected. Most graphics canvases place the base coordinate (0, 0) in the
upper left-hand corner of the canvas; the common PDF coordinate sytem places
the base coordinate in the lower left-hand corner of the canvas.
.eval {{{
pdf.right_margin = $rm
.endeval }}}
PDF::Writer uses the standard coordinate space. Any transformations on the
coordinate space (or, more accurately, on the coordinate axis) may affect the
usability of PDF::Writer. Standard transformations available against the
coordinate space axis are rotations (counter-clockwise), scaling, translation
(repositioning of the base coordinate) and skew. Each of these will be
discussed in more detail in a later section of this manual, after other
concepts have been introduced.
<b>NOTE:</b> As of PDF::Writer 1.1.0, angle rotations are now
counter-clockwise, not clockwise as in earlier versions. This is a necessary
incompatible change to make transformations more compatible with other vector
graphics systems.
2<Fonts, Special Characters, and Character Maps in PDF::Writer>FontBasics
All PDF readers support fourteen standard fonts; twelve of these fonts are
bold, italic (or oblique), and bold-italic (bold-oblique) variants of three
base font families and the other two are symbolic fonts.
.eval {{{
pdf.select_font("Helvetica")
PDF::SimpleTable.new do |tab|
tab.title = "Default Fonts in PDF"
tab.column_order.push(*%w(family name filename))
tab.columns["family"] = PDF::SimpleTable::Column.new("family") { |col|
col.heading = "Family"
}
tab.columns["name"] = PDF::SimpleTable::Column.new("name") { |col|
col.heading = "Name"
}
tab.columns["filename"] = PDF::SimpleTable::Column.new("filename") { |col|
col.heading = "Filename"
}
tab.show_lines = :outer
tab.show_headings = true
tab.shade_headings = true
tab.orientation = :center
tab.position = :center
data = [
{ "family" => "Courier",
"name" => "Courier",
"filename" => "Courier.afm" },
{ "family" => "",
"name" => "Courier-Bold",
"filename" => "Courier-Bold.afm" },
{ "family" => "",
"name" => "Courier-Oblique",
"filename" => "Courier-Oblique.afm" },
{ "family" => "",
"name" => "Courier-BoldOblique",
"filename" => "Courier-BoldOblique.afm" },
{ "family" => "Helvetica",
"name" => "Helvetica",
"filename" => "Helvetica.afm" },
{ "family" => "",
"name" => "Helvetica-Bold",
"filename" => "Helvetica-Bold.afm" },
{ "family" => "",
"name" => "Helvetica-Oblique",
"filename" => "Helvetica-Oblique.afm" },
{ "family" => "",
"name" => "Helvetica-BoldOblique",
"filename" => "Helvetica-BoldOblique.afm" },
{ "family" => "Symbol",
"name" => "Symbol",
"filename" => "Symbol.afm" },
{ "family" => "Times-Roman",
"name" => "Times-Roman",
"filename" => "Times-Roman.afm" },
{ "family" => "",
"name" => "Times-Bold",
"filename" => "Times-Bold.afm" },
{ "family" => "",
"name" => "Times-Italic",
"filename" => "Times-Italic.afm" },
{ "family" => "",
"name" => "Times-BoldItalic",
"filename" => "Times-BoldItalic.afm" },
{ "family" => "ZapfDingbats",
"name" => "ZapfDingbats",
"filename" => "ZapfDingbats.afm" },
]
tab.data.replace data
tab.render_on(pdf)
end
.endeval }}}
In addition to these fourteen standard fonts, PDF supports the embedding of
PostScript Type 1, TrueType, and OpenType fonts. PDF::Writer explicitly
supports Type 1 and TrueType fonts. Inasmuch as OpenType fonts may be
compatible with TrueType fonts, they may be supported. Embedded fonts require
font metrics information. See <r:xref name="EmbeddingFonts" label="title"
/>, <r:xref name="Type1Fonts" label="title" /> and <r:xref
name="TrueTypeFonts" label="title" /> below for more information.
PDF::Writer will find (or attempt to find) system font location(s) so that
any existing Type 1 or TrueType fonts can be loaded directly from the system
locations. These paths are found in <b>PDF::Writer::FONT_PATH</b>. By
modifying this value, it is possible to tell PDF::Writer to find font files
in alternate locations. Adobe font metrics (AFM) files are found in the paths
described by <b>PDF::Writer::FontMetrics::METRICS_PATH</b>, the current
directory, and the Ruby <b>$LOAD_PATH</b>.
Fonts are selected with PDF::Writer#select_font. This will load the
appropriate font metrics file andif the font is not one of the fourteen
built-in fontssearch for the associated Type 1 or TrueType font to embed in
the generated PDF document.
3<PDF::Writer#select_font(font_name, encoding = nil)>
This selects, and possibly loads, a font to be used from this point in the
document. The font name is as noted above. The format and the use of the
<b>encoding</b> parameter is discussed extensively in <r:xref
name="CharMaps" label="title" />.
The encoding directive will be effective <b>only when the font is first
loaded</b>.
.code {{{
# use a Times-Roman font with MacExpertEncoding
pdf.select_font("Times-Roman", "MacExpertEncoding")
# this next line should be equivalent
pdf.select_font("Times-Roman", { :encoding => "MacExpertEncoding" })
# Set up the Helvetica font for use with German characters as an offset
# of the WinAnsiEncoding.
diff= {
196 => "Adieresis",
228 => "adieresis",
214 => "Odieresis",
246 => "odieresis",
220 => "Udieresis",
252 => "udieresis",
223 => "germandbls"
}
pdf.select_font("Helvetica", { :encoding => "WinAnsiEncoding", :differences => diff })
.endcode }}}
3<Font Families>FontFamilies
It is possible to define font families in PDF::Writer so that text state
translations may be performed. The only text state translations that
PDF::Writer currently recognises are b (bold), i (italic), bi (bold
italic), and ib (italic bold).
4<PDF::Writer#font_families>
PDF::Writer#font_families is a Hash that maps the default font name (such as
Helvetica) to a mapping of text states and font names, as illustrated
below.
.code {{{
pdf = PDF::Writer.new
# Note: "bi" (<b><i>) can be implemented as a
# different font than "ib" (<i><b>).
pdf.font_families["Helvetica"] =
{
"b" => "Helvetica-Bold",
"i" => "Helvetica-Oblique",
"bi" => "Helvetica-BoldOblique",
"ib" => "Helvetica-BoldOblique"
}
.endcode }}}
The use of font families will allow the in-line switching between text states
and make it unnecessary to use <b>#select_font</b> to change between these
fonts. This will also ensure that the encoding and differences (see <r:xref
name="SpecialCharacters" label="title" /> below) for the selected font family
will be consistent. The default font families detailed above (for Helvetica,
Times Roman, and Courier) have already been defined for PDF::Writer.
3<Embedding Fonts>EmbeddingFonts
PDF::Writer will properly embed both TrueType and PostScript Type 1 fonts,
but these fonts will require Adobe Font Metrics (AFM) files (extension .afm).
These files should exist for PostScript Type 1 files but may not exist for
TrueType files on any given system.
4<Type 1 Fonts>Type1Fonts
PostScript Type 1 fonts are fully supported by PDF, but only the binary
(.pfb) forms of Type 1 fonts may be embedded into a PDF document. If only
the ASCII (.pfa) form of the Type 1 font is available, then the program
t1binary available as part of the Type 1 utilities package found at the
<c:alink uri="http://www.lcdf.org/~eddietwo/type/#t1utils">LCDF type software
page</c:alink> will create the binary form from the ASCII form. There is a
suggestion on that page that older Macintosh Type 1 fonts will need
additional conversion with one of the provided utilities.
4<TrueType Fonts>TrueTypeFonts
AFM files can be generated for TrueType fonts with the program ttf2afm,
which is part of the package <c:alink
uri="http://www.tug.org/applications/pdftex/">pdfTex</c:alink> by Han The
Thanh. In a future release of PDF::Writer or another program from the Ruby
PDF project, this requirement should be eliminated.
4<Embedded Font Licensing Restrictions>
As noted above, PDF::Writer will embed Type 1 or TrueType font programs in
the PDF document. Fonts are recognised as copyrightable intellectual property
in some jurisdictions. TrueType fonts encode some licensing rights in the
font programs and PDF::Writer will check and warn if fonts not licensed for
inclusion in a document are selected. It is up to the users of PDF::Writer to
ensure that there are no licence violations of fonts embedded in the
generated PDF documents.
3<Special Characters, Character Maps, and Unicode>SpecialCharacters
Fonts in PDF documents can include encoding information. The PDF standard
encodings are none, WinAnsiEncoding, MacRomanEncoding, or
MacExpertEncoding. Symbolic fonts should be encoded with the none
encoding. The default encoding used in PDF::Writer is WinAnsiEncoding.
WinAnsiEncoding encoding is not quite the same as Windows code page 1252
(roughly equivalent to latin 1). Appendix D of the Adobe PDF Reference
version 1.6 contains full information on all encoding mappings (including
mappings for the two included symbolic fonts).
4<Encoding Character Maps>CharMaps
The main way of providing particular character support with PDF::Writer is
through a differences map, or a character subtitution table. This is done
only when initially selecting the font; it will not be reapplied after the
font has been loaded from disk once (this limitation applies to the fourteen
built-in fonts as well).
.code {{{
encoding = {
:encoding => "WinAnsiEncoding",
:differences => {
215 => "multiply",
148 => "copyright",
}
}
pdf.select_font("Helvetica", encoding)
.endcode }}}
The above code maps the bytes 215 (0xd7) and 148 (0x94) to the named
characters multiply and copyright in the Helvetica font. These byte
values are the characters © and × in Windows 1252 but are undefined in
the font and therefore require additional information to present and space
these characters properly.
#TODO
As of PDF::Writer version 1.1, these difference maps will be inserted into
the PDF documents as well. This change is experimental but should be safe.
4<Unicode>
PDF supports UTF-16BE encoding of stringsbut each such string must begin
with the UTF-16BE byte order mark (BOM) of U+FEFF (0xFE followed by 0xFF).
PDF::Writer does not, at this point, explicitly support either UTF-8 or
UTF-16BE strings. If all of the conditions are correct, the following code
should display the Japanese characters for Nihongo (the name of the
Japanese language). As of 2005.05.02, this will not work (at least in this
manual).
.code {{{
pdf.text("\xfe\xff\x65\xe5\x67\x2c\x8a\x9e")
.endcode }}}
# .eval {{{
# pdf.text("\xfe\xff\x65\xe5\x67\x2c\x8a\x9e", :justification => :center)
# .endeval }}}
There are additional limitations and features for Unicode support, but a
later version of PDF::Writer will support Unicode (at least UTF-8) documents.
.newpage
1<Working with PDF Documents>WorkingWith
The PDF::Writer class is used to create PDF documents in Ruby. It is a
smart writing canvas on which both text and graphics may be drawn. <r:xref
name="GraphicsOps" label="title" /> covers the methods that are used to draw
graphics in PDF::Writer. This chapter covers text drawing and most other PDF
document operations.
The canvas provided by PDF::Writer is described as a smart canvas because
it is aware of common text writing conventions including page margins and
multi-column output.
2<Creating PDF Documents>CreatingDocuments
There are two ways to create PDF documents with PDF::Writer. Both will
provide instances of PDF::Writer; these are <b>PDF::Writer.new</b> and
<b>PDF::Writer.prepress</b>.
3<PDF::Writer.new>
This method creates a new PDF document as a writing canvas. Without any
parameters, it will create a PDF version 1.3 document that uses pages that
are standard US Letter (8½ × 11) in portrait orientation. It accepts three
named parameters, <b>:paper</b>, <b>:version</b>, and <b>:orientation</b>.
.code {{{
require "pdf/writer"
# US Letter, portrait, 1.3
PDF::Writer.new
# 1. A4, portrait, 1.3
PDF::Writer.new(:paper => "A4")
# 2. 150cm × 200cm, portrait, 1.3
PDF::Writer.new(:paper => [ 150, 200 ])
# 3. 150pt × 200pt, portrait, 1.3
PDF::Writer.new(:paper => [ 0, 0, 150, 200 ])
# US Letter, landscape, 1.3
PDF::Writer.new(:orientation => :landscape)
# US Letter, portrait, 1.5
PDF::Writer.new(:version => PDF_VERSION_15)
.endcode }}}
The <b>:paper</b> parameter specifies the size of a page in PDF::Writer. It
may be specified as: (1) a standard paper size name (see the table
PDF::Writer Page Sizes below for the defined paper size names); (2) a
<b>[width, height]</b> array measured in centimetres; or (3) a <b>[x0, y0,
x1, y1]</b> array measured in points where <b>(x0, y0)</b> represents the
lower left-hand corner and <b>(x1, y1)</b> represent the upper right-hand
corner of the page.
The <b>:orientation</b> parameter specifies whether the pages will be
<b>:portrait</b> (the long edge is the height of the page) or
<b>:landscape</b> (the long edge is the width of the page). These are the
only allowable values.
The <b>:version</b> parameter specifies the minimum specification version
that the document will adhere to. As of this version, the PDF document
version is inserted but not used to control what features may be inserted
into a document. The latest version of the PDF specification is PDF 1.6
(associated with Adobe Acrobat 7); it is recommended that the default version
(1.3) be kept in most cases as that will ensure that most users will have
access to the features in the document. A later version of PDF::Writer will
include version controls so that if when creating a PDF 1.3 document,
features from PDF 1.6 will not be available. PDF::Writer currently supports
features from PDF 1.3 and does not yet provide access to the advanced
features of PDF 1.4 or higher.
.eval {{{
pdf.select_font("Helvetica")
PDF::SimpleTable.new do |tab|
tab.title = "PDF::Writer Page Sizes"
tab.column_order.replace %w(name1 size1 name2 size2)
tab.columns["name1"] = PDF::SimpleTable::Column.new("name1") { |col|
col.heading = PDF::SimpleTable::Column::Heading.new("Type")
}
tab.columns["size1"] = PDF::SimpleTable::Column.new("size1") { |col|
col.heading = PDF::SimpleTable::Column::Heading.new("Size")
}
tab.columns["name2"] = PDF::SimpleTable::Column.new("name2") { |col|
col.heading = PDF::SimpleTable::Column::Heading.new("Type")
}
tab.columns["size2"] = PDF::SimpleTable::Column.new("size2") { |col|
col.heading = PDF::SimpleTable::Column::Heading.new("Size")
}
tab.show_lines = :all
tab.show_headings = true
tab.orientation = :center
tab.position = :center
data = [
{ "name1" => "2A0", "size1" => '46.811" x 66.220" (118.9cm x 168.2cm)', "name2" => "4A0", "size2" => '66.220" x 93.622" (168.2cm x 237.8cm)' },
{ "name1" => "A0", "size1" => '33.110" x 46.811" (84.1cm x 118.9cm)', "name2" => "A1", "size2" => '23.386" x 33.110" (59.4cm x 84.1cm)' },
{ "name1" => "A2", "size1" => '16.535" x 23.386" (42cm x 59.4cm)', "name2" => "A3", "size2" => '11.693" x 16.535" (29.7cm x 42cm)' },
{ "name1" => "A4", "size1" => '8.268" x 11.693" (21cm x 29.7cm)', "name2" => "A5", "size2" => '5.827" x 8.268" (14.8cm x 21cm)' },
{ "name1" => "A6", "size1" => '4.134" x 5.827" (10.5cm x 14.8cm)', "name2" => "A7", "size2" => '2.913" x 4.134" (7.4cm x 10.5cm)' },
{ "name1" => "A8", "size1" => '2.047" x 2.913" (5.2cm x 7.4cm)', "name2" => "A9", "size2" => '1.457" x 2.047" (3.7cm x 5.2cm)' },
{ "name1" => "A10", "size1" => '1.024" x 1.457" (2.6cm x 3.7cm)' },
{ "name1" => "B0", "size1" => '39.370" x 55.669" (100cm x 141.4cm)', "name2" => "B1", "size2" => '27.835" x 39.370" (70.7cm x 100cm)' },
{ "name1" => "B2", "size1" => '19.685" x 27.835" (50cm x 70.7cm)', "name2" => "B3", "size2" => '13.898" x 19.685" (35.3cm x 50cm)' },
{ "name1" => "B4", "size1" => '9.842" x 13.898" (25cm x 35.3cm)', "name2" => "B5", "size2" => '6.929" x 9.842" (17.6cm x 25cm)' },
{ "name1" => "B6", "size1" => '4.921" x 6.929" (12.5cm x 17.6cm)', "name2" => "B7", "size2" => '3.465" x 4.921" (8.8cm x 12.5cm)' },
{ "name1" => "B8", "size1" => '2.441" x 3.465" (6.2cm x 8.8cm)', "name2" => "B9", "size2" => '1.732" x 2.441" (4.4cm x 6.2cm)' },
{ "name1" => "B10", "size1" => '1.220" x 1.732" (3.1cm x 4.4cm)' },
{ "name1" => "C0", "size1" => '36.102" x 51.063" (91.7cm x 129.7cm)', "name2" => "C1", "size2" => '25.512" x 36.102" (64.8cm x 91.7cm)' },
{ "name1" => "C2", "size1" => '18.032" x 25.512" (45.8cm x 64.8cm)', "name2" => "C3", "size2" => '12.756" x 18.032" (32.4cm x 45.8cm)' },
{ "name1" => "C4", "size1" => '9.016" x 12.756" (22.9cm x 32.4cm)', "name2" => "C5", "size2" => '6.378" x 9.016" (16.2cm x 22.9cm)' },
{ "name1" => "C6", "size1" => '4.488" x 6.378" (11.4cm x 16.2cm)', "name2" => "C7", "size2" => '3.189" x 4.488" (8.1cm x 11.4cm)' },
{ "name1" => "C8", "size1" => '2.244" x 3.189" (5.7cm x 8.1cm)', "name2" => "C9", "size2" => '1.575" x 2.244" (4.0cm x 5.7cm)' },
{ "name1" => "C10", "size1" => '1.102" x 1.575" (2.8cm x 4.0cm)' },
{ "name1" => "EXECUTIVE", "size1" => '7.248" x 10.5" (18.410cm x 26.670cm)', "name2" => "FOLIO", "size2" => '8.5" x 13" (21.590cm x 33.020cm)' },
{ "name1" => "LEGAL", "size1" => '8.5" x 14" (21.590cm x 35.560cm)', "name2" => "LETTER", "size2" => '8.5" x 11" (21.590cm x 27.940cm)' },
{ "name1" => "RA0", "size1" => '33.858" x 48.032" (86cm x 122cm)', "name2" => "RA1", "size2" => '24.016" x 33.858" (61cm x 86cm)' },
{ "name1" => "RA2", "size1" => '16.929" x 24.016" (43cm x 61cm)', "name2" => "RA3", "size2" => '12.008" x 16.929" (30.5cm x 43cm)' },
{ "name1" => "RA4", "size1" => '8.465" x 12.008" (21.5cm x 30.5cm)' },
{ "name1" => "SRA0", "size1" => '35.433" x 50.394" (90cm x 128cm)', "name2" => "SRA1", "size2" => '25.197" x 35.433" (64cm x 90cm)' },
{ "name1" => "SRA2", "size1" => '17.717" x 25.197" (45cm x 64cm)', "name2" => "SRA3", "size2" => '12.598" x 17.717" (32cm x 45cm)' },
{ "name1" => "SRA4", "size1" => '8.858" x 12.598" (22.5cm x 32cm)' }
]
tab.data.replace data
tab.render_on(pdf)
end
.endeval }}}
3<PDF::Writer.prepress>
This is an alternative way to create a new PDF document. In addition to the
named parameters in PDF::Writer.new (<b>:paper</b>, <b>:orientation</b>, and
<b>:version</b>), this method accepts the following options as well.
<b>:left_margin</b>, <b>:right_margin</b>, <b>:top_margin</b>, and
<b>:bottom_margin</b> specify the margins. Prepress marks are placed relative
to the margins, so when creating a document in prepress mode the margins must
be specified as the document is created. Future versions of PDF::Writer will
be more flexible on this. The default margins are 36pts (about ½).
<b>:bleed_size</b> and <b>:mark_length</b> specify the size of the bleed area
(default is 12 points) and the length of the prepress marks (default is 18
points).
Prepress marks will appear on all pages.
.code {{{
require "pdf/writer"
PDF::Writer.prepress # US Letter, portrait, 1.3, prepress
.endcode }}}
2<Adding Text to PDF Document>AddingText
There are two different and complementary ways of adding text to a PDF
document with PDF::Writer. The easier is to use the generated PDF as a
smart canvas, where information internal to the document is used to
maintain a current writing pointer (see <r:xref name="WritingPointer"
label="title" />) and text is written within the writing space defined by
document margins (see <r:xref name="DocumentMargins" label="title" />). The
harder is to place the text on the document canvas explicitly, ensuring that
the text does not overflow the page horizontally or vertically.
In order to support more flexible and humane layout, PDF::Writer supports
XML-like tags that can be used to change the current text state (discussed
earlier in <r:xref name="FontFamilies" label="title" />), substitute
alternate text, or apply custom formatting. This is discussed in detail in
<r:xref name="TextTags" label="title" />.
3<Text Wrapping>TextWrapping
PDF::Writer uses a very simple text wrapping formula. If a line looks like
its going to exceed its permitted width, then PDF::Writer backtracks to the
previous hyphen (-) or space to see if the text will then fit into the
permitted width. It will do this until the text will fit. If no acceptable
hyphen or space can be found, the text will be forcibly split at the largest
position that will fit in the permitted width.
3<Smart Text Writing>
As noted, PDF::Writer has two different ways to write text to a PDF document.
This is expressed with three different methods. The method that uses the PDF
document as a smart canvas is <b>PDF::Writer#text</b>.
4<PDF::Writer#text(text, options = nil)>
This method will add text to the document starting at the current drawing
position (described in <r:xref name="WritingPointer" label="title" />). It
uses the document margins (see <r:xref name="DocumentMargins" label="title"
/>) to flow text onto the page. If an explicit newline (\n) is
encountered, it will be used.
The text is drawn from the left margin until it reaches the right margin. If
the text exceeds the right margin, then the text will be wrapped and
continued on the next line. If the text to be written will exceed the bottom
margin, then a new page will be created (detailed in <r:xref
name="ColumnOutput" label="title" />).
.code {{{
pdf.text("Ruby is fun!")
.endcode }}}
5<Text Size>
If the <b>:font_size</b> is not specified, then the last font size
or the default font size of 12 points will be used. If this value is
provided, this <b>changes</b> the current font size just as if the #font_size
attribute were set.
.code {{{
pdf.text("Ruby is fun!", :font_size => 16)
.endcode }}}
5<Text Justification>TextJustification
Text may be justified within the margins (normal or overridden) in one of
four ways with the option <b>:justification</b>.
.blist disc {{{
<b>:left</b> justification is also called ragged-right, because the text is flush against the left margin and the right edge of the text is uneven. This is the default justification.
<b>:right</b> justification places the text flush agains the right margin; the left edge of the text is uneven.
<b>:center</b> justification centers the text between the margins.
<b>:full</b> justification ensures that the text is flush against both the left and right margins. Additional space is inserted between words to make this happen. The last line of a paragraph is only made flush left.
.endblist }}}
.code {{{
pdf.text("Ruby is fun!", :justification => :left)
pdf.text("Ruby is fun!", :justification => :right)
pdf.text("Ruby is fun!", :justification => :center)
pdf.text("Ruby is fun!", :justification => :full)
.endcode }}}
5<Margin Override Options>
The drawing positions and margins may be modified with the <b>:left</b>,
<b>:right</b>, <b>:absolute_left</b>, and <b>:absolute_right</b> options. The
<b>:left</b> and <b>:right</b> values are PDF userspace unit offsets from the
left and right margins, respectively; the <b>:absolute_left</b> and
<b>:absolute_right</b> values are userspace units effectively providing
<i>new</i> margins for the text to be written. Note that
<b>:absolute_right</b> is offset from the left side of the page, not the
right side of the page.
.code {{{
# The left margin is shifted inward 50 points.
pdf.text("Ruby is fun!", :left => 50)
# The right margin is shifted inward 50 points.
pdf.text("Ruby is fun!", :right => 50)
# The text is drawn starting at 50 points from the left of the page.
pdf.text("Ruby is fun!", :absolute_left => 50)
# The text is drawn ending at 300 points from the left of the page.
pdf.text("Ruby is fun!", :absolute_right => 300)
.endcode }}}
5<Line Spacing Options>
Normally, each line of text is separated only by the descender space for the
font. This means that each line will be separated by only enough space to
keep it readable. This may be changed by changing the line spacing
(<b>:spacing</b>) as a multiple of the normal line height (authors
manuscripts are often printed double spaced or <b>:spacing => 2</b>). This
can also be changed by redefining the total height of the line (the
<i>leading</i>) independent of the font size. With <b>:font_size =>
12</b>, both <b>:spacing => 2</b> and <b>:leading => 24</b> do the same
thing. The <b>:leading</b> value overrides the <b>:spacing</b> value.
.code {{{
# These are the same
pdf.text("Ruby is fun!", :spacing => 2)
pdf.text("Ruby is fun!", :leading => 24)
.endcode }}}
5<Test Writing>
Not generally used, the <b>:test</b> option can be set to <b>true</b>. This
will prevent the text from being written to the page and the method will
return <b>true</b> if the text will be overflowed to a new page or column.
3<Explicit Text Placement>
Text can also be placed starting with specific X and Y coordinates using
either <b>PDF::Writer#add_text_wrap</b> or <b>PDF::Writer#add_text</b>.
4<PDF::Writer#add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false)>
This will add text to the page at position <b>(x, y)</b>, but ensures that it
fits within the provided <b>width</b>. If it does not fit, then as much as
possible will be written using the wrapping rules on page <r:xref
name="TextWrapping" label="page" />. The remaining text to be written will be
returned to the caller.
.code {{{
rest = pdf.add_text_wrap(150, pdf.y, 150, "Ruby is fun!", 72)
.endcode }}}
If <b>size</b> is not specified, the current #font_size will be used. This
parameter has changed position with <b>text</b> in PDF::Writer 1.1. If your
code appears to be using the old parameter call, it will be warned.
The optional justification parameter works just as described on page <r:xref
name="TextJustification" label="page" />, except that instead of margin
width, it is the boundaries defined by the <b>x</b> and <b>width</b>
parameters.
Text may be drawn at an angle. This will be discussed fully in the section on
<b>PDF::Writer#add_text</b>.
If the <b>test</b> parameter is <b>true</b>, the text will not be written,
but the remainder of the text will be returned.
4<PDF::Writer#add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0)>
This places the full text string at the <b>(x, y)</b> position and the
specified <b>angle</b> (measured in degrees of a circle, in a
counter-clockwise direction) with respect to the angle of the coordinate
axis.
<b>NOTE:</b> As of PDF::Writer 1.1.0, angle rotations are now
counter-clockwise, not clockwise as in earlier versions. This is a necessary
incompatible change to make transformations more compatible with other vector
graphics systems.
If <b>size</b> is not specified, the current #font_size will be used. This
parameter has changed position with <b>text</b> in PDF::Writer 1.1. If your
code appears to be using the old parameter call, it will be warned.
.code {{{
0.step(315, 45) do |angle|
pdf.add_text(pdf.margin_x_middle, pdf.y, "#{angle}º".rjust(8), 12,
angle)
end
.endcode }}}
.eval {{{
x = pdf.margin_x_middle
y = pdf.y - 50
0.step(315, 45) do |angle|
pdf.add_text(x, y, "#{angle}º".rjust(8), 12, angle)
end
pdf.y = y - 30
.endeval }}}
The <b>adjust</b> parameter adjusts the space between words by the specified
number of PDF userspace units. This is primarily used by PDF::Writer#text and
PDF::Writer#add_text_wrap to support text justification.
3<Text Tags>TextTags