pdf_paradise 0.3.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +933 -0
  3. data/bin/automatic_pdf_title +7 -0
  4. data/bin/burst_this_pdf_file +7 -0
  5. data/bin/combine_these_pdf_pages +7 -0
  6. data/bin/compress_via_hexapdf +7 -0
  7. data/bin/convert_markdown_to_pdf +7 -0
  8. data/bin/convert_pdf_to_text +7 -0
  9. data/bin/delete_first_page_of_this_pdf_file +7 -0
  10. data/bin/djvu_to_pdf +7 -0
  11. data/bin/merge_then_open +7 -0
  12. data/bin/n_pages +10 -0
  13. data/bin/open_main_pdf +7 -0
  14. data/bin/pdf_paradise +9 -0
  15. data/bin/rotate_pdf +7 -0
  16. data/bin/set_main_book +7 -0
  17. data/bin/set_title_of_this_pdf_file +15 -0
  18. data/doc/README.gen +871 -0
  19. data/doc/todo/todo.md +13 -0
  20. data/images/Logo_for_the_pdf_paradise_project.avif +0 -0
  21. data/lib/pdf_paradise/base/base.rb +344 -0
  22. data/lib/pdf_paradise/base/colours.rb +67 -0
  23. data/lib/pdf_paradise/colours/colours.rb +27 -0
  24. data/lib/pdf_paradise/commandline/commandline.rb +109 -0
  25. data/lib/pdf_paradise/commandline/help.rb +77 -0
  26. data/lib/pdf_paradise/commandline/menu.rb +173 -0
  27. data/lib/pdf_paradise/compress/compress_this_pdf_file.rb +108 -0
  28. data/lib/pdf_paradise/compress/compress_via_hexapdf.rb +27 -0
  29. data/lib/pdf_paradise/compress/compress_via_qpdf.rb +32 -0
  30. data/lib/pdf_paradise/constants/constants.rb +76 -0
  31. data/lib/pdf_paradise/convert_text_to_pdf.rb +94 -0
  32. data/lib/pdf_paradise/css/project.css +17 -0
  33. data/lib/pdf_paradise/fpdf/README.md +2 -0
  34. data/lib/pdf_paradise/fpdf/bookmark.rb +129 -0
  35. data/lib/pdf_paradise/fpdf/chinese.rb +454 -0
  36. data/lib/pdf_paradise/fpdf/fpdf.rb +1902 -0
  37. data/lib/pdf_paradise/fpdf/fpdf_eps.rb +138 -0
  38. data/lib/pdf_paradise/fpdf/makefont.rb +1794 -0
  39. data/lib/pdf_paradise/gui/README.md +6 -0
  40. data/lib/pdf_paradise/gui/fox/split_pdf_file.rb +77 -0
  41. data/lib/pdf_paradise/gui/gtk2/pdf_viewer/pdf_viewer.rb +34 -0
  42. data/lib/pdf_paradise/gui/gtk2/split_pdf_file/split_pdf_file.rb +34 -0
  43. data/lib/pdf_paradise/gui/gtk2/statistics_widget/statistics_widget.rb +34 -0
  44. data/lib/pdf_paradise/gui/gtk3/controller/controller.rb +214 -0
  45. data/lib/pdf_paradise/gui/gtk3/pdf_viewer/pdf_viewer.rb +34 -0
  46. data/lib/pdf_paradise/gui/gtk3/split_pdf_file/split_pdf_file.rb +34 -0
  47. data/lib/pdf_paradise/gui/jruby/delete_the_first_or_the_last_page_of_this_pdf_file/delete_the_first_or_the_last_page_of_this_pdf_file.rb +167 -0
  48. data/lib/pdf_paradise/gui/jruby/remove_the_first_page_of_this_pdf_file/remove_the_first_page_of_this_pdf_file.rb +103 -0
  49. data/lib/pdf_paradise/gui/libui/extract_all_images_from_this_pdf_file/extract_all_images_from_this_pdf_file.rb +223 -0
  50. data/lib/pdf_paradise/gui/libui/remove_the_first_page_of_this_pdf_file/remove_the_first_page_of_this_pdf_file.rb +267 -0
  51. data/lib/pdf_paradise/gui/libui/rotate_pdf_file/rotate_pdf_file.rb +219 -0
  52. data/lib/pdf_paradise/gui/libui/statistics_widget/statistics_widget.rb +233 -0
  53. data/lib/pdf_paradise/gui/shared_code/pdf_viewer/pdf_viewer.css +5 -0
  54. data/lib/pdf_paradise/gui/shared_code/pdf_viewer/pdf_viewer_module.rb +287 -0
  55. data/lib/pdf_paradise/gui/shared_code/remove_the_first_page_of_this_pdf_file_module/remove_the_first_page_of_this_pdf_file_module.rb +31 -0
  56. data/lib/pdf_paradise/gui/shared_code/split_pdf_file/split_pdf_file_module.rb +295 -0
  57. data/lib/pdf_paradise/gui/universal_widgets/convert_pdf_to_text/convert_pdf_to_text.rb +366 -0
  58. data/lib/pdf_paradise/gui/universal_widgets/delete_the_first_or_the_last_page_of_this_pdf_file/delete_the_first_or_the_last_page_of_this_pdf_file.rb +776 -0
  59. data/lib/pdf_paradise/gui/universal_widgets/statistics_widget/statistics_widget.rb +407 -0
  60. data/lib/pdf_paradise/gui/universal_widgets/to_pdf/to_pdf.rb +351 -0
  61. data/lib/pdf_paradise/hexapdf/001_rainbow_pattern_example.rb +0 -0
  62. data/lib/pdf_paradise/hexapdf/hexapdf.rb +123 -0
  63. data/lib/pdf_paradise/images/PDF_PARADISE_LOGO.png +0 -0
  64. data/lib/pdf_paradise/main_pdf/main_pdf.rb +474 -0
  65. data/lib/pdf_paradise/merge_pdf/menu.rb +63 -0
  66. data/lib/pdf_paradise/merge_pdf/merge_pdf.rb +307 -0
  67. data/lib/pdf_paradise/merge_pdf_namespace.rb +9 -0
  68. data/lib/pdf_paradise/merge_then_open/merge_then_open.rb +105 -0
  69. data/lib/pdf_paradise/prawn_addons/README.md +2 -0
  70. data/lib/pdf_paradise/prawn_addons/prawn_addons.rb +17 -0
  71. data/lib/pdf_paradise/project/project.rb +22 -0
  72. data/lib/pdf_paradise/remove_pdf_password.rb +391 -0
  73. data/lib/pdf_paradise/requires/batch_require_toplevel_files.rb +22 -0
  74. data/lib/pdf_paradise/requires/colours.rb +11 -0
  75. data/lib/pdf_paradise/requires/colours_and_esystem_and_save_file_and_fileutils_and_opn.rb +13 -0
  76. data/lib/pdf_paradise/requires/esystem_and_colours.rb +11 -0
  77. data/lib/pdf_paradise/requires/esystem_and_opn_and_colours.rb +10 -0
  78. data/lib/pdf_paradise/requires/require_the_whole_project.rb +30 -0
  79. data/lib/pdf_paradise/requires/require_utility_scripts.rb +9 -0
  80. data/lib/pdf_paradise/set_main_book.rb +156 -0
  81. data/lib/pdf_paradise/set_pdf_title.rb +220 -0
  82. data/lib/pdf_paradise/sinatra/embeddable_interface.rb +389 -0
  83. data/lib/pdf_paradise/toplevel_methods/convert_epub_to_pdf.rb +27 -0
  84. data/lib/pdf_paradise/toplevel_methods/convert_markdown_to_pdf.rb +45 -0
  85. data/lib/pdf_paradise/toplevel_methods/convert_ppt_to_pdf.rb +35 -0
  86. data/lib/pdf_paradise/toplevel_methods/e.rb +16 -0
  87. data/lib/pdf_paradise/toplevel_methods/esystem.rb +20 -0
  88. data/lib/pdf_paradise/toplevel_methods/misc.rb +228 -0
  89. data/lib/pdf_paradise/toplevel_methods/number_pages.rb +38 -0
  90. data/lib/pdf_paradise/toplevel_methods/opened_pdf_files.rb +221 -0
  91. data/lib/pdf_paradise/toplevel_methods/query_pdf_title.rb +201 -0
  92. data/lib/pdf_paradise/toplevel_methods/reduce_size_of_this_pdf_file.rb +46 -0
  93. data/lib/pdf_paradise/toplevel_methods/roebe.rb +17 -0
  94. data/lib/pdf_paradise/toplevel_methods/to_pdf.rb +12 -0
  95. data/lib/pdf_paradise/utility_scripts/README.md +3 -0
  96. data/lib/pdf_paradise/utility_scripts/automatic_pdf_title.rb +104 -0
  97. data/lib/pdf_paradise/utility_scripts/check_syntax_of_pdf_files.rb +106 -0
  98. data/lib/pdf_paradise/utility_scripts/combine_these_pdf_pages.rb +118 -0
  99. data/lib/pdf_paradise/utility_scripts/convert_pdf_to_text.rb +179 -0
  100. data/lib/pdf_paradise/utility_scripts/delete_last_page_of_this_pdf_file.rb +180 -0
  101. data/lib/pdf_paradise/utility_scripts/delete_the_first_page_of_this_pdf_file/delete_the_first_page_of_this_pdf_file.rb +429 -0
  102. data/lib/pdf_paradise/utility_scripts/delete_this_page_of_this_pdf_file.rb +356 -0
  103. data/lib/pdf_paradise/utility_scripts/djvu_to_pdf.rb +87 -0
  104. data/lib/pdf_paradise/utility_scripts/extract_all_images_from_this_pdf_file.rb +129 -0
  105. data/lib/pdf_paradise/utility_scripts/extract_pdf_page.rb +283 -0
  106. data/lib/pdf_paradise/utility_scripts/pdf_file_n_total_pages.rb +348 -0
  107. data/lib/pdf_paradise/utility_scripts/pdf_optimizer.rb +111 -0
  108. data/lib/pdf_paradise/utility_scripts/pdf_statistics.rb +148 -0
  109. data/lib/pdf_paradise/utility_scripts/pdf_to_html.rb +75 -0
  110. data/lib/pdf_paradise/utility_scripts/remove_images.rb +110 -0
  111. data/lib/pdf_paradise/utility_scripts/rotate_pdf_file.rb +303 -0
  112. data/lib/pdf_paradise/utility_scripts/split_pdf.rb +364 -0
  113. data/lib/pdf_paradise/utility_scripts/to_pdf.rb +130 -0
  114. data/lib/pdf_paradise/utility_scripts/to_qdf.rb +66 -0
  115. data/lib/pdf_paradise/version/version.rb +19 -0
  116. data/lib/pdf_paradise/www/README.md +2 -0
  117. data/lib/pdf_paradise/www/sinatra/app.rb +304 -0
  118. data/lib/pdf_paradise/yaml/working_on_these_pdf_files.yml +4 -0
  119. data/lib/pdf_paradise.rb +5 -0
  120. data/pdf_paradise.gemspec +61 -0
  121. data/test/fpdf/001_minimal_example.rb +12 -0
  122. data/test/fpdf/002.pdf +0 -0
  123. data/test/fpdf/002_header_and_footer_example.rb +64 -0
  124. data/test/fpdf/003.pdf +98 -0
  125. data/test/fpdf/003_justified_paragraphs.rb +96 -0
  126. data/test/fpdf/file1.md +3 -0
  127. data/test/fpdf/file2.md +3 -0
  128. data/test/fpdf/test.pdf +0 -0
  129. data/test/testing_pdf_paradise.rb +12 -0
  130. metadata +239 -0
@@ -0,0 +1,1902 @@
1
+ #!/usr/bin/ruby -w
2
+ # Encoding: UTF-8
3
+ # frozen_string_literal: true
4
+ # =========================================================================== #
5
+ # === Forked from ruby-fpdf FPDF 1.53d
6
+ #
7
+ # FPDF 1.53 by Olivier Plathey ported to Ruby by Brian Ollenberger
8
+ # Copyright 2005 Brian Ollenberger
9
+ # Please retain this entire copyright notice. If you distribute any
10
+ # modifications, place an additional comment here that clearly indicates
11
+ # that it was modified. You may (but are not send any useful modifications that you make
12
+ # back to me at
13
+ #
14
+ # http://zeropluszero.com/software/fpdf/
15
+ #
16
+ # Bug fixes, examples, external fonts, JPEG support, and upgrade to version
17
+ # 1.53 contributed by Kim Shrier.
18
+ #
19
+ # Bookmark support contributed by Sylvain Lafleur.
20
+ #
21
+ # EPS support contributed by Thiago Jackiw, ported from the PHP version by Valentin Schmidt.
22
+ #
23
+ # Many other bug reports and fixes contributed by many other people.
24
+ # =========================================================================== #
25
+ class FPDF
26
+
27
+ require 'date'
28
+ require 'zlib'
29
+
30
+ # ========================================================================= #
31
+ # === FPDF_VERSION
32
+ # ========================================================================= #
33
+ FPDF_VERSION = '1.53d'
34
+
35
+ # ========================================================================= #
36
+ # === Charwidths
37
+ # ========================================================================= #
38
+ Charwidths = {
39
+ 'courier' => [
40
+ 600,600,600,600,600,
41
+ 600,600,600,600,600,
42
+ 600,600,600,600,600,
43
+ 600,600,600,600,600,
44
+ 600,600,600,600,600,
45
+ 600,600,600,600,600,
46
+ 600,600,600,600,600,
47
+ 600,600,600,600,600,
48
+ 600,600,600,600,600,
49
+ 600,600,600,600,600,
50
+ 600,600,600,600,600,
51
+ 600,600,600,600,600,
52
+ 600,600,600,600,600,
53
+ 600,600,600,600,600,
54
+ 600,600,600,600,600,
55
+ 600,600,600,600,600,
56
+ 600,600,600,600,600,
57
+ 600,600,600,600,600,
58
+ 600,600,600,600,600,
59
+ 600,600,600,600,600,
60
+ 600,600,600,600,600,
61
+ 600,600,600,600,600,
62
+ 600,600,600,600,600,
63
+ 600,600,600,600,600,
64
+ 600,600,600,600,600,
65
+ 600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600
66
+ ],
67
+
68
+ 'courierB' => [
69
+ 600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600
70
+ ],
71
+
72
+ 'courierI' => [
73
+ 600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
74
+
75
+ 'courierBI' => [
76
+ 600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
77
+
78
+ 'helvetica' => [
79
+ 278, 278, 278, 278, 278,
80
+ 278, 278, 278, 278, 278,
81
+ 278, 278, 278, 278, 278,
82
+ 278, 278, 278, 278, 278,
83
+ 278, 278, 278, 278, 278,
84
+ 278, 278, 278, 278, 278,
85
+ 278, 278, 278, 278, 355,
86
+ 556, 556, 889, 667, 191,
87
+ 333, 333, 389, 584, 278,
88
+ 333, 278, 278, 556, 556,
89
+ 556, 556, 556, 556, 556,
90
+ 556, 556, 556, 278, 278,
91
+ 584, 584, 584, 556, 1015,
92
+ 667, 667, 722, 722, 667,
93
+ 611, 778, 722, 278, 500,
94
+ 667, 556, 833, 722, 778,
95
+ 667, 778, 722, 667, 611,
96
+ 722, 667, 944, 667, 667,
97
+ 611, 278, 278, 278, 469,
98
+ 556, 333, 556, 556, 500,
99
+ 556, 556, 278, 556, 556,
100
+ 222, 222, 500, 222, 833,
101
+ 556, 556, 556, 556, 333,
102
+ 500, 278, 556, 500, 722,
103
+ 500, 500, 500, 334, 260,
104
+ 334, 584, 350, 556, 350,
105
+ 222, 556, 333, 1000, 556,
106
+ 556, 333, 1000, 667, 333,
107
+ 1000, 350, 611, 350, 350,
108
+ 222, 222, 333, 333, 350,
109
+ 556, 1000, 333, 1000, 500,
110
+ 333, 944, 350, 500, 667,
111
+ 278, 333, 556, 556, 556,
112
+ 556, 260, 556, 333, 737,
113
+ 370, 556, 584, 333, 737,
114
+ 333, 400, 584, 333, 333,
115
+ 333, 556, 537, 278, 333,
116
+ 333, 365, 556, 834, 834,
117
+ 834, 611, 667, 667, 667,
118
+ 667, 667, 667, 1000, 722,
119
+ 667, 667, 667, 667, 278,
120
+ 278, 278, 278, 722, 722,
121
+ 778, 778, 778, 778, 778,
122
+ 584, 778, 722, 722, 722,
123
+ 722, 667, 667, 611, 556,
124
+ 556, 556, 556, 556, 556,
125
+ 889, 500, 556, 556, 556,
126
+ 556, 278, 278, 278, 278,
127
+ 556, 556, 556, 556, 556,
128
+ 556, 556, 584, 611, 556,
129
+ 556, 556, 556, 500, 556,
130
+ 500
131
+ ],
132
+
133
+ 'helveticaB' => [
134
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 350, 556, 350, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556
135
+ ],
136
+
137
+ 'helveticaI' => [
138
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 350, 556, 350, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500
139
+ ],
140
+
141
+ 'helveticaBI' => [
142
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 350, 556, 350, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556
143
+ ],
144
+
145
+ 'times' => [
146
+ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 408, 500, 500, 833, 778, 180, 333, 333, 500, 564, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 350, 500, 350, 333, 500, 444, 1000, 500, 500, 333, 1000, 556, 333, 889, 350, 611, 350, 350, 333, 333, 444, 444, 350, 500, 1000, 333, 980, 389, 333, 722, 350, 444, 722, 250, 333, 500, 500, 500, 500, 200, 500, 333, 760, 276, 500, 564, 333, 760, 333, 400, 564, 300, 300, 333, 500, 453, 250, 333, 300, 310, 500, 750, 750, 750, 444, 722, 722, 722, 722, 722, 722, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 722, 722, 722, 722, 722, 722, 564, 722, 722, 722, 722, 722, 722, 556, 500, 444, 444, 444, 444, 444, 444, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 564, 500, 500, 500, 500, 500, 500, 500, 500
147
+ ],
148
+
149
+ 'timesB' => [
150
+ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 555, 500, 500, 1000, 833, 278, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 350, 500, 350, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 1000, 350, 667, 350, 350, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 350, 444, 722, 250, 333, 500, 500, 500, 500, 220, 500, 333, 747, 300, 500, 570, 333, 747, 333, 400, 570, 300, 300, 333, 556, 540, 250, 333, 300, 330, 500, 750, 750, 750, 500, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 778, 778, 778, 778, 778, 570, 778, 722, 722, 722, 722, 722, 611, 556, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 500, 556, 500
151
+ ],
152
+
153
+ 'timesI' => [
154
+ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 420, 500, 500, 833, 778, 214, 333, 333, 500, 675, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 675, 675, 675, 500, 920, 611, 611, 667, 722, 611, 611, 722, 722, 333, 444, 667, 556, 833, 667, 722, 611, 722, 611, 500, 556, 722, 611, 833, 611, 556, 556, 389, 278, 389, 422, 500, 333, 500, 500, 444, 500, 444, 278, 500, 500, 278, 278, 444, 278, 722, 500, 500, 500, 500, 389, 389, 278, 500, 444, 667, 444, 444, 389, 400, 275, 400, 541, 350, 500, 350, 333, 500, 556, 889, 500, 500, 333, 1000, 500, 333, 944, 350, 556, 350, 350, 333, 333, 556, 556, 350, 500, 889, 333, 980, 389, 333, 667, 350, 389, 556, 250, 389, 500, 500, 500, 500, 275, 500, 333, 760, 276, 500, 675, 333, 760, 333, 400, 675, 300, 300, 333, 500, 523, 250, 333, 300, 310, 500, 750, 750, 750, 500, 611, 611, 611, 611, 611, 611, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 667, 722, 722, 722, 722, 722, 675, 722, 722, 722, 722, 722, 556, 611, 500, 500, 500, 500, 500, 500, 500, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 675, 500, 500, 500, 500, 500, 444, 500, 444
155
+ ],
156
+
157
+ 'timesBI' => [
158
+ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 389, 555, 500, 500, 833, 778, 278, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 832, 667, 667, 667, 722, 667, 667, 722, 778, 389, 500, 667, 611, 889, 722, 722, 611, 722, 667, 556, 611, 722, 667, 889, 667, 611, 611, 333, 278, 333, 570, 500, 333, 500, 500, 444, 500, 444, 333, 500, 556, 278, 278, 500, 278, 778, 556, 500, 500, 500, 389, 389, 278, 556, 444, 667, 500, 444, 389, 348, 220, 348, 570, 350, 500, 350, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 944, 350, 611, 350, 350, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 350, 389, 611, 250, 389, 500, 500, 500, 500, 220, 500, 333, 747, 266, 500, 606, 333, 747, 333, 400, 570, 300, 300, 333, 576, 500, 250, 333, 300, 300, 500, 750, 750, 750, 500, 667, 667, 667, 667, 667, 667, 944, 667, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 722, 722, 722, 722, 722, 570, 722, 722, 722, 722, 722, 611, 611, 500, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 444, 500, 444
159
+ ],
160
+
161
+ 'symbol' => [
162
+ 250, 250, 250, 250, 250,
163
+ 250, 250, 250, 250, 250,
164
+ 250, 250, 250, 250, 250,
165
+ 250, 250, 250, 250, 250,
166
+ 250, 250, 250, 250, 250,
167
+ 250, 250, 250, 250, 250,
168
+ 250, 250, 250, 333, 713,
169
+ 500, 549, 833, 778, 439,
170
+ 333, 333, 500, 549, 250,
171
+ 549, 250, 278, 500, 500,
172
+ 500, 500, 500, 500, 500,
173
+ 500, 500, 500, 278, 278,
174
+ 549, 549, 549, 444, 549,
175
+ 722, 667, 722, 612, 611,
176
+ 763, 603, 722, 333, 631,
177
+ 722, 686, 889, 722, 722,
178
+ 768, 741, 556, 592, 611,
179
+ 690, 439, 768, 645, 795,
180
+ 611, 333, 863, 333, 658,
181
+ 500, 500, 631, 549, 549,
182
+ 494, 439, 521, 411, 603,
183
+ 329, 603, 549, 549, 576,
184
+ 521, 549, 549, 521, 549,
185
+ 603, 439, 576, 713, 686,
186
+ 493, 686, 494, 480, 200,
187
+ 480, 549, 0, 0, 0, 0, 0,
188
+ 0, 0, 0, 0, 0, 0, 0, 0,
189
+ 0, 0, 0, 0, 0, 0, 0, 0,
190
+ 0, 0, 0, 0, 0, 0, 0, 0,
191
+ 0, 0, 0, 0, 750, 620,
192
+ 247, 549, 167, 713, 500,
193
+ 753, 753, 753, 753, 1042,
194
+ 987, 603, 987, 603, 400,
195
+ 549, 411, 549, 549, 713,
196
+ 494, 460, 549, 549, 549,
197
+ 549, 1000, 603, 1000, 658,
198
+ 823, 686, 795, 987, 768,
199
+ 768, 823, 768, 768, 713,
200
+ 713, 713, 713, 713, 713,
201
+ 713, 768, 713, 790, 790,
202
+ 890, 823, 549, 250, 713,
203
+ 603, 603, 1042, 987, 603,
204
+ 987, 603, 494, 329, 790,
205
+ 790, 786, 713, 384, 384,
206
+ 384, 384, 384, 384, 494,
207
+ 494, 494, 494, 0, 329,
208
+ 274, 686, 686, 686, 384,
209
+ 384, 384, 384, 384, 384,
210
+ 494, 494, 494, 0
211
+ ],
212
+
213
+ 'zapfdingbats'=> [
214
+ 0, 0, 0, 0, 0,
215
+ 0, 0, 0, 0, 0,
216
+ 0, 0, 0, 0, 0,
217
+ 0, 0, 0, 0, 0,
218
+ 0, 0, 0, 0, 0,
219
+ 0, 0, 0, 0, 0,
220
+ 0, 0, 278, 974, 961,
221
+ 974, 980, 719, 789, 790,
222
+ 791, 690, 960, 939, 549,
223
+ 855, 911, 933, 911, 945,
224
+ 974, 755, 846, 762, 761,
225
+ 571, 677, 763, 760, 759,
226
+ 754, 494, 552, 537, 577,
227
+ 692, 786, 788, 788, 790,
228
+ 793, 794, 816, 823, 789,
229
+ 841, 823, 833, 816, 831,
230
+ 923, 744, 723, 749, 790,
231
+ 792, 695, 776, 768, 792,
232
+ 759, 707, 708, 682, 701,
233
+ 826, 815, 789, 789, 707,
234
+ 687, 696, 689, 786, 787,
235
+ 713, 791, 785, 791, 873,
236
+ 761, 762, 762, 759, 759,
237
+ 892, 892, 788, 784, 438,
238
+ 138, 277, 415, 392, 392,
239
+ 668, 668, 0, 390, 390,
240
+ 317, 317, 276, 276, 509,
241
+ 509, 410, 410, 234, 234,
242
+ 334, 334, 0, 0, 0, 0,
243
+ 0, 0, 0, 0, 0, 0, 0,
244
+ 0, 0, 0, 0, 0, 0, 0,
245
+ 0, 732, 544, 544, 910,
246
+ 667, 760, 760, 776, 595,
247
+ 694, 626, 788, 788, 788,
248
+ 788, 788, 788, 788, 788,
249
+ 788, 788, 788, 788, 788,
250
+ 788, 788, 788, 788, 788,
251
+ 788, 788, 788, 788, 788,
252
+ 788, 788, 788, 788, 788,
253
+ 788, 788, 788, 788, 788,
254
+ 788, 788, 788, 788, 788,
255
+ 788, 788, 894, 838, 1016,
256
+ 458, 748, 924, 748, 918,
257
+ 927, 928, 928, 834, 873,
258
+ 828, 924, 924, 917, 930,
259
+ 931, 463, 883, 836, 836,
260
+ 867, 867, 696, 696, 874,
261
+ 0, 874, 760, 946, 771,
262
+ 865, 771, 888, 967, 888,
263
+ 831, 873, 927, 970, 918, 0
264
+ ]
265
+ }
266
+
267
+ # ========================================================================= #
268
+ # === initialize
269
+ # ========================================================================= #
270
+ def initialize(
271
+ orientation = 'P',
272
+ unit = 'mm',
273
+ format = 'A4'
274
+ )
275
+ reset
276
+ # ======================================================================= #
277
+ # === @core_fonts
278
+ #
279
+ # Standard fonts will be defined next.
280
+ # ======================================================================= #
281
+ @core_fonts = {}
282
+ @core_fonts['courier']='Courier'
283
+ @core_fonts['courierB']='Courier-Bold'
284
+ @core_fonts['courierI']='Courier-Oblique'
285
+ @core_fonts['courierBI']='Courier-BoldOblique'
286
+ @core_fonts['helvetica']='Helvetica'
287
+ @core_fonts['helveticaB']='Helvetica-Bold'
288
+ @core_fonts['helveticaI']='Helvetica-Oblique'
289
+ @core_fonts['helveticaBI']='Helvetica-BoldOblique'
290
+ @core_fonts['times']='Times-Roman'
291
+ @core_fonts['timesB']='Times-Bold'
292
+ @core_fonts['timesI']='Times-Italic'
293
+ @core_fonts['timesBI']='Times-BoldItalic'
294
+ @core_fonts['symbol']='Symbol'
295
+ @core_fonts['zapfdingbats']='ZapfDingbats'
296
+
297
+ # Scale factor
298
+ case unit
299
+ when 'pt'
300
+ @k = 1
301
+ when 'mm'
302
+ @k = 72/25.4
303
+ when 'cm'
304
+ @k = 72/2.54
305
+ when 'in'
306
+ @k = 72
307
+ else
308
+ raise "Incorrect unit: #{unit}"
309
+ end
310
+
311
+ # Page format
312
+ if format.is_a? String
313
+ format = format.dup if format.frozen?
314
+ format.downcase!
315
+ case format
316
+ when 'a3'
317
+ format = [841.89,1190.55]
318
+ when 'a4'
319
+ format = [595.28,841.89]
320
+ when 'a5'
321
+ format = [420.94,595.28]
322
+ when 'letter'
323
+ format = [612,792]
324
+ when 'legal'
325
+ format = [612,1008]
326
+ else
327
+ raise 'Unknown page format: '+format
328
+ end
329
+ @fwPt,@fhPt = format
330
+ else
331
+ @fwPt = format[0]*@k
332
+ @fhPt = format[1]*@k
333
+ end
334
+ @fw = @fwPt / @k
335
+ @fh = @fhPt / @k
336
+
337
+ # Page orientation
338
+ if orientation.frozen?
339
+ orientation = orientation.dup
340
+ end
341
+ orientation.downcase!
342
+ case orientation
343
+ when 'p','portrait'
344
+ @DefOrientation='P'
345
+ @wPt=@fwPt
346
+ @hPt=@fhPt
347
+ when 'l','landscape'
348
+ @DefOrientation='L'
349
+ @wPt=@fhPt
350
+ @hPt=@fwPt
351
+ else
352
+ raise 'Incorrect orientation: '+orientation
353
+ end
354
+ @CurOrientation=@DefOrientation
355
+ @w=@wPt/@k
356
+ @h=@hPt/@k
357
+
358
+ # Page margins (1 cm)
359
+ margin=28.35/@k
360
+ SetMargins(margin,margin)
361
+ # Interior cell margin (1 mm)
362
+ @cMargin=margin/10
363
+ # Line width (0.2 mm)
364
+ @LineWidth=0.567/@k
365
+ # Automatic page break
366
+ SetAutoPageBreak(true,2*margin)
367
+ # Full width display mode
368
+ SetDisplayMode('fullwidth')
369
+ # Enable compression
370
+ SetCompression(true)
371
+ # Set default PDF version number
372
+ @PDFVersion='1.3'
373
+ end
374
+
375
+ # ========================================================================= #
376
+ # === reset
377
+ # ========================================================================= #
378
+ def reset
379
+ @page=0
380
+ @n=2
381
+ @buffer=''
382
+ @pages=[]
383
+ @OrientationChanges=[]
384
+ @state=0
385
+ @fonts = {}
386
+ @FontFiles = {}
387
+ @diffs=[]
388
+ @images = {}
389
+ @links=[]
390
+ @PageLinks = {}
391
+ @InFooter=false
392
+ @font_family = ''
393
+ @FontStyle=''
394
+ @FontSizePt=12
395
+ @underline= false
396
+ @DrawColor='0 G'
397
+ @FillColor='0 g'
398
+ @TextColor='0 g'
399
+ @ColorFlag=false
400
+ @ws=0
401
+ @offsets=[]
402
+ end
403
+
404
+ # ========================================================================= #
405
+ # === dounderline
406
+ # ========================================================================= #
407
+ def dounderline(x,y,txt)
408
+ # Underline text
409
+ up = @CurrentFont['up']
410
+ ut = @CurrentFont['ut']
411
+ w = GetStringWidth(txt)+@ws*txt.count(' ')
412
+ sprintf(
413
+ '%.2f %.2f %.2f %.2f re f',
414
+ x*@k, (@h - (y - up/1000.0 * @FontSize))*@k,w*@k,-ut/1000.0 * @FontSizePt
415
+ )
416
+ end
417
+
418
+ # ========================================================================= #
419
+ # === set_margins
420
+ # ========================================================================= #
421
+ def set_margins(
422
+ left, top, right=-1
423
+ )
424
+ # Set left, top and right margins
425
+ @left_margin = left
426
+ @top_margin = top
427
+ right=left if right==-1
428
+ @rMargin=right
429
+ end; alias SetMargins set_margins # === SetMargins
430
+
431
+ # ========================================================================= #
432
+ # === set_title
433
+ #
434
+ # Set the title of the .pdf document through this method here.
435
+ # ========================================================================= #
436
+ def set_title(i)
437
+ @title = i
438
+ end; alias SetTitle set_title # === SetTitle
439
+
440
+ # ========================================================================= #
441
+ # === set_left_margin
442
+ #
443
+ # This method can be used to set the left margin of the .pdf file at hand.
444
+ # ========================================================================= #
445
+ def set_left_margin(i)
446
+ @left_margin = i
447
+ @x = i if @page>0 and @x < i
448
+ end; alias SetLeftMargin set_left_margin # === SetLeftMargin
449
+
450
+ # === Write
451
+ def Write(h,txt,link='')
452
+ # Output text in flowing mode
453
+ cw=@CurrentFont['cw']
454
+ w=@w-@rMargin-@x
455
+ wmax=(w-2*@cMargin)*1000/@FontSize
456
+ s=txt.gsub("\r",'')
457
+ nb=s.length
458
+ sep=-1
459
+ i=0
460
+ j=0
461
+ l=0
462
+ nl=1
463
+ while i<nb
464
+ # Get next character
465
+ c=s[i]
466
+ if c=="\n"[0]
467
+ # Explicit line break
468
+ self.Cell(w,h,s[j,i-j],0,2,'',0,link)
469
+ i += 1
470
+ sep = -1
471
+ j=i
472
+ l=0
473
+ if nl==1
474
+ @x=@left_margin
475
+ w=@w-@rMargin-@x
476
+ wmax=(w-2*@cMargin)*1000/@FontSize
477
+ end
478
+ nl += 1
479
+ next
480
+ end
481
+ if c == ' '[0]
482
+ sep=i
483
+ ls=l
484
+ end
485
+ l = l+cw[c]
486
+ if l>wmax
487
+ # Automatic line break
488
+ if sep==-1
489
+ if @x>@left_margin
490
+ # Move to next line
491
+ @x=@left_margin
492
+ @y=@y+h
493
+ w=@w-@rMargin-@x
494
+ wmax=(w-2*@cMargin)*1000/@FontSize
495
+ i=i+1
496
+ nl += 1
497
+ next
498
+ end
499
+ i=i+1 if i==j
500
+ self.Cell(w,h,s[j,i-j],0,2,'',0,link)
501
+ else
502
+ self.Cell(w,h,s[j,sep-j],0,2,'',0,link)
503
+ i=sep+1
504
+ end
505
+ sep=-1
506
+ j=i
507
+ l=0
508
+ if nl==1
509
+ @x=@left_margin
510
+ w=@w-@rMargin-@x
511
+ wmax=(w-2*@cMargin)*1000/@FontSize
512
+ end
513
+ nl += 1
514
+ else
515
+ i=i+1
516
+ end
517
+ end
518
+ # Last chunk
519
+ self.Cell(l/1000.0*@FontSize,h,s[j,i],0,0,'',0,link) if i != j
520
+ end
521
+
522
+ # ========================================================================= #
523
+ # === SetTopMargin
524
+ #
525
+ # Set top margin
526
+ # ========================================================================= #
527
+ def SetTopMargin(i)
528
+ @top_margin = i
529
+ end
530
+
531
+ # ========================================================================= #
532
+ # === SetRightMargin
533
+ # ========================================================================= #
534
+ def SetRightMargin(i)
535
+ #Set right margin
536
+ @rMargin = i
537
+ end
538
+
539
+ # ========================================================================= #
540
+ # === alias_nb_pages
541
+ #
542
+ # Define an alias for total number of pages
543
+ # ========================================================================= #
544
+ def alias_nb_pages(i = '{nb}')
545
+ @AliasNbPages = i
546
+ end; alias AliasNbPages alias_nb_pages # === AliasNbPages
547
+
548
+ # ========================================================================= #
549
+ # === SetAutoPageBreak
550
+ # ========================================================================= #
551
+ def SetAutoPageBreak(auto, margin = 0)
552
+ # Set auto page break mode and triggering margin
553
+ @AutoPageBreak=auto
554
+ @bMargin=margin
555
+ @PageBreakTrigger=@h-margin
556
+ end
557
+
558
+ def SetDisplayMode(zoom, layout='continuous')
559
+ # Set display mode in viewer
560
+ if zoom=='fullpage' or zoom=='fullwidth' or zoom=='real' or
561
+ zoom=='default' or not zoom.kind_of? String
562
+ @ZoomMode=zoom
563
+ elsif zoom=='zoom'
564
+ @ZoomMode=layout
565
+ else
566
+ raise 'Incorrect zoom display mode: '+zoom
567
+ end
568
+ if layout == 'single' or
569
+ layout == 'continuous' or
570
+ layout == 'two' or
571
+ layout == 'default'
572
+ @LayoutMode = layout
573
+ elsif zoom != 'zoom'
574
+ raise 'Incorrect layout display mode: '+layout
575
+ end
576
+ end
577
+
578
+ # === SetCompression
579
+ def SetCompression(i)
580
+ # Set page compression
581
+ @compress = i
582
+ end
583
+
584
+ def SetSubject(subject)
585
+ # Subject of document
586
+ @subject=subject
587
+ end
588
+
589
+ def SetAuthor(author)
590
+ # Author of document
591
+ @author=author
592
+ end
593
+
594
+ def SetKeywords(keywords)
595
+ # Keywords of document
596
+ @keywords=keywords
597
+ end
598
+
599
+ def SetCreator(creator)
600
+ # Creator of document
601
+ @creator=creator
602
+ end
603
+
604
+ # === error
605
+ def error(i)
606
+ raise "FPDF error: #{i}"
607
+ end; alias Error error # === Error
608
+
609
+ def Open
610
+ # Begin document
611
+ @state=1
612
+ end
613
+
614
+ # === Close
615
+ def Close
616
+ # Terminate document
617
+ return if @state==3
618
+ self.add_page if @page==0
619
+ # Page footer
620
+ @InFooter=true
621
+ self.Footer
622
+ @InFooter=false
623
+ # Close page
624
+ endpage
625
+ # Close document
626
+ enddoc
627
+ end
628
+
629
+ # ========================================================================= #
630
+ # Draw a line
631
+ # ========================================================================= #
632
+ def Line(x1, y1, x2, y2)
633
+ out(
634
+ sprintf('%.2f %.2f m %.2f %.2f l S',x1*@k,(@h-y1)*@k,x2*@k,(@h-y2)*@k)
635
+ )
636
+ end
637
+
638
+ # ========================================================================= #
639
+ # === new_page
640
+ # ========================================================================= #
641
+ def new_page(orientation = '')
642
+ # Start a new page
643
+ self.Open if @state==0
644
+ family = @font_family
645
+ style = @FontStyle+(@underline ? 'U' : '')
646
+ size=@FontSizePt
647
+ lw=@LineWidth
648
+ dc=@DrawColor
649
+ fc=@FillColor
650
+ tc=@TextColor
651
+ cf=@ColorFlag
652
+ if @page>0
653
+ # Page footer
654
+ @InFooter=true
655
+ self.Footer
656
+ @InFooter=false
657
+ # Close page
658
+ endpage
659
+ end
660
+ # Start new page
661
+ beginpage(orientation)
662
+ # Set line cap style to square
663
+ out('2 J')
664
+ # Set line width
665
+ @LineWidth = lw
666
+ out(sprintf('%.2f w',lw*@k))
667
+ # Set font
668
+ SetFont(family,style,size) if family
669
+ # Set colors
670
+ @DrawColor=dc
671
+ out(dc) if dc!='0 G'
672
+ @FillColor=fc
673
+ out(fc) if fc!='0 g'
674
+ @TextColor=tc
675
+ @ColorFlag=cf
676
+ # Page header
677
+ self.Header
678
+ # Restore line width
679
+ if @LineWidth!=lw
680
+ @LineWidth=lw
681
+ out(sprintf('%.2f w',lw*@k))
682
+ end
683
+ # Restore font
684
+ SetFont(family,style,size) if family
685
+ # Restore colors
686
+ if @DrawColor != dc
687
+ @DrawColor=dc
688
+ out(dc)
689
+ end
690
+ if @FillColor!=fc
691
+ @FillColor=fc
692
+ out(fc)
693
+ end
694
+ @TextColor=tc
695
+ @ColorFlag=cf
696
+ end; alias AddPage new_page # === AddPage
697
+ alias add_page new_page # === add_page
698
+
699
+ def Header
700
+ # To be implemented in your inherited class
701
+ end
702
+
703
+ def Footer
704
+ # To be implemented in your inherited class
705
+ end
706
+
707
+ def PageNo
708
+ # Get current page number
709
+ @page
710
+ end
711
+
712
+ # ========================================================================= #
713
+ #
714
+ # ========================================================================= #
715
+ def SetDrawColor(r,g=-1,b=-1)
716
+ # Set color for all stroking operations
717
+ if (r==0 and g==0 and b==0) or g==-1
718
+ @DrawColor=sprintf('%.3f G',r/255.0)
719
+ else
720
+ @DrawColor=sprintf('%.3f %.3f %.3f RG',r/255.0,g/255.0,b/255.0)
721
+ end
722
+ out(@DrawColor) if(@page>0)
723
+ end
724
+
725
+ # ========================================================================= #
726
+ # === set_fill_colour
727
+ #
728
+ # This method will set the colour for all filling operations.
729
+ # ========================================================================= #
730
+ def set_fill_colour(r, g = -1, b = -1)
731
+ if (r==0 and g==0 and b==0) or g==-1
732
+ @FillColor=sprintf('%.3f g',r/255.0)
733
+ else
734
+ @FillColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
735
+ end
736
+ @ColorFlag=(@FillColor!=@TextColor)
737
+ out(@FillColor) if(@page>0)
738
+ end; alias SetFillColor set_fill_colour # === SetFillColor
739
+
740
+ def SetTextColor(r,g=-1,b=-1)
741
+ # Set color for text
742
+ if (r==0 and g==0 and b==0) or g==-1
743
+ @TextColor=sprintf('%.3f g',r/255.0)
744
+ else
745
+ @TextColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
746
+ end
747
+ @ColorFlag=(@FillColor!=@TextColor)
748
+ end
749
+
750
+ def GetStringWidth(s)
751
+ # Get width of a string in the current font
752
+ cw=@CurrentFont['cw']
753
+ w=0
754
+ s.each_byte do |c|
755
+ w=w+cw[c]
756
+ end
757
+ w*@FontSize/1000.0
758
+ end
759
+
760
+ def SetLineWidth(width)
761
+ # Set line width
762
+ @LineWidth = width
763
+ out(sprintf('%.2f w',width*@k)) if @page>0
764
+ end
765
+
766
+
767
+ def Rect(x, y, w, h, style='')
768
+ # Draw a rectangle
769
+ if style=='F'
770
+ op='f'
771
+ elsif style=='FD' or style=='DF'
772
+ op='B'
773
+ else
774
+ op='S'
775
+ end
776
+ out(sprintf('%.2f %.2f %.2f %.2f re %s', x*@k,(@h-y)*@k,w*@k,-h*@k,op))
777
+ end
778
+
779
+ # ========================================================================= #
780
+ # === add_font
781
+ # ========================================================================= #
782
+ def add_font(
783
+ family,
784
+ style = '',
785
+ file = ''
786
+ )
787
+ # Add a TrueType or Type1 font next:
788
+ family = family.downcase
789
+ family = 'helvetica' if family == 'arial'
790
+
791
+ style = style.upcase
792
+ style = 'BI' if style == 'IB'
793
+
794
+ fontkey = family + style
795
+
796
+ if @fonts.has_key?(fontkey)
797
+ self.Error("Font already added: #{family} #{style}")
798
+ end
799
+
800
+ file = family.gsub(' ', '') + style.downcase + '.rb' if file == ''
801
+
802
+ if self.class.const_defined? 'FPDF_FONTPATH'
803
+ if FPDF_FONTPATH.end_with?('/')
804
+ file = FPDF_FONTPATH + file
805
+ else
806
+ file = FPDF_FONTPATH + '/' + file
807
+ end
808
+ end
809
+
810
+ # Changed from "require file" to fix bug reported by Hans Allis.
811
+ load(file)
812
+
813
+ if FontDef.desc.nil?
814
+ self.Error("Could not include font definition file #{file}")
815
+ end
816
+
817
+ i = @fonts.length + 1
818
+
819
+ @fonts[fontkey] = {'i' => i,
820
+ 'type' => FontDef.type,
821
+ 'name' => FontDef.name,
822
+ 'desc' => FontDef.desc,
823
+ 'up' => FontDef.up,
824
+ 'ut' => FontDef.ut,
825
+ 'cw' => FontDef.cw,
826
+ 'enc' => FontDef.enc,
827
+ 'file' => FontDef.file
828
+ }
829
+
830
+ if FontDef.diff
831
+ # Search existing encodings
832
+ idx = @diffs.index(FontDef.diff)
833
+
834
+ unless idx
835
+ @diffs.push(FontDef.diff)
836
+ @fonts[fontkey]['diff'] = @diffs.length
837
+ else
838
+ @fonts[fontkey]['diff'] = idx + 1
839
+ end
840
+ end
841
+
842
+ if FontDef.file
843
+ if FontDef.type == 'TrueType'
844
+ @FontFiles[FontDef.file] = {'length1' => FontDef.originalsize}
845
+ else
846
+ @FontFiles[FontDef.file] = {'length1' => FontDef.size1, 'length2' => FontDef.size2}
847
+ end
848
+ end
849
+
850
+ return self
851
+ end; alias AddFont add_font # === AddFont
852
+
853
+ # === SetFont
854
+ def SetFont(
855
+ family,
856
+ style = '',
857
+ size = 0
858
+ )
859
+ # Select a font; the size is given in points.
860
+ if family.frozen?
861
+ family = family.dup
862
+ end
863
+ family.downcase!
864
+ family = @font_family if family.empty?
865
+ case family
866
+ when 'arial'
867
+ family = 'helvetica'
868
+ when 'symbol','zapfdingbats'
869
+ style = ''
870
+ end
871
+ style.upcase!
872
+ unless style.index('U').nil?
873
+ @underline=true
874
+ style.gsub!('U','')
875
+ else
876
+ @underline = false
877
+ end
878
+ style='BI' if style=='IB'
879
+ size = @FontSizePt if size == 0
880
+ # Test if font is already selected
881
+ return if @font_family==family and
882
+ @FontStyle==style and @FontSizePt==size
883
+ # Test if used for the first time
884
+ fontkey=family+style
885
+ unless @fonts.has_key?(fontkey)
886
+ if @core_fonts.has_key?(fontkey)
887
+ unless Charwidths.has_key?(fontkey)
888
+ raise 'Font unavailable'
889
+ end
890
+ @fonts[fontkey]={
891
+ 'i'=>@fonts.size,
892
+ 'type'=>'core',
893
+ 'name'=>@core_fonts[fontkey],
894
+ 'up' =>-100,
895
+ 'ut' =>50,
896
+ 'cw' =>Charwidths[fontkey]}
897
+ else
898
+ raise 'Font unavailable'
899
+ end
900
+ end
901
+
902
+ # Select it
903
+ @font_family = family
904
+ @FontStyle = style
905
+ @FontSizePt = size
906
+ @FontSize = size/@k
907
+ @CurrentFont = @fonts[fontkey]
908
+ if @page > 0
909
+ out(sprintf('BT /F%d %.2f Tf ET', @CurrentFont['i'], @FontSizePt))
910
+ end
911
+ end; alias set_font SetFont # === set_font
912
+
913
+ # === SetFontSize
914
+ def SetFontSize(size)
915
+ # Set font size in points
916
+ return if @FontSizePt==size
917
+ @FontSizePt=size
918
+ @FontSize=size/@k
919
+ if @page>0
920
+ out(sprintf('BT /F%d %.2f Tf ET',@CurrentFont['i'],@FontSizePt))
921
+ end
922
+ end
923
+
924
+ # ========================================================================= #
925
+ # === AddLink
926
+ # ========================================================================= #
927
+ def AddLink
928
+ # Create a new internal link
929
+ @links.push([0, 0])
930
+ @links.size
931
+ end
932
+
933
+ # ========================================================================= #
934
+ # === SetLink
935
+ # ========================================================================= #
936
+ def SetLink(
937
+ link, y = 0, page = -1
938
+ )
939
+ # Set destination of internal link
940
+ y = @y if y == -1
941
+ page=@page if page==-1
942
+ @links[link]=[page, y]
943
+ end
944
+
945
+ # ========================================================================= #
946
+ # === Link
947
+ # ========================================================================= #
948
+ def Link(x, y, w, h, link)
949
+ # Put a link on the page
950
+ @PageLinks[@page]=Array.new unless @PageLinks.has_key?(@page)
951
+ @PageLinks[@page].push([x*@k,@hPt-y*@k,w*@k,h*@k,link])
952
+ end
953
+
954
+ # ========================================================================= #
955
+ # === AcceptPageBreak
956
+ # ========================================================================= #
957
+ def AcceptPageBreak
958
+ # Accept automatic page break or not
959
+ @AutoPageBreak
960
+ end
961
+
962
+ # ========================================================================= #
963
+ # === Cell
964
+ # ========================================================================= #
965
+ def Cell(
966
+ w,
967
+ h = 0,
968
+ txt='',
969
+ border=0,
970
+ ln=0,
971
+ align='',
972
+ fill=0,
973
+ link=''
974
+ )
975
+ # Output a cell
976
+ if @y+h>@PageBreakTrigger and !@InFooter and self.AcceptPageBreak
977
+ # Automatic page break
978
+ x=@x
979
+ ws=@ws
980
+ if ws>0
981
+ @ws=0
982
+ out('0 Tw')
983
+ end
984
+ self.AddPage(@CurOrientation)
985
+ @x=x
986
+ if ws>0
987
+ @ws=ws
988
+ out(sprintf('%.3f Tw',ws*@k))
989
+ end
990
+ end
991
+ w = @w - @rMargin - @x if w == 0
992
+ s = ''
993
+ if fill==1 or border == 1
994
+ if fill == 1
995
+ op=(border==1) ? 'B' : 'f'
996
+ else
997
+ op = 'S'
998
+ end
999
+ s = sprintf('%.2f %.2f %.2f %.2f re %s ',@x*@k,(@h-@y)*@k,w*@k,-h*@k,op)
1000
+ end
1001
+ if border.is_a? String
1002
+ x=@x
1003
+ y=@y
1004
+ unless border.index('L').nil?
1005
+ s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
1006
+ x*@k,(@h-y)*@k,x*@k,(@h-(y+h))*@k)
1007
+ end
1008
+ unless border.index('T').nil?
1009
+ s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
1010
+ x*@k,(@h-y)*@k,(x+w)*@k,(@h-y)*@k)
1011
+ end
1012
+ unless border.index('R').nil?
1013
+ s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
1014
+ (x+w)*@k,(@h-y)*@k,(x+w)*@k,(@h-(y+h))*@k)
1015
+ end
1016
+ unless border.index('B').nil?
1017
+ s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
1018
+ x*@k,(@h-(y+h))*@k,(x+w)*@k,(@h-(y+h))*@k)
1019
+ end
1020
+ end
1021
+ if txt!=''
1022
+ if align=='R'
1023
+ dx=w-@cMargin-self.GetStringWidth(txt)
1024
+ elsif align=='C'
1025
+ dx=(w-self.GetStringWidth(txt))/2
1026
+ else
1027
+ dx=@cMargin
1028
+ end
1029
+ txt = txt.gsub(')', '\\)')
1030
+ txt.gsub!('(', '\\(')
1031
+ txt.gsub!('\\', '\\\\')
1032
+ if @ColorFlag
1033
+ s=s+'q '+@TextColor+' '
1034
+ end
1035
+ s=s+sprintf('BT %.2f %.2f Td (%s) Tj ET',
1036
+ (@x+dx)*@k,(@h-(@y+0.5*h+0.3*@FontSize))*@k,txt)
1037
+ s=s+' '+dounderline(@x+dx,@y+0.5*h+0.3*@FontSize,txt) if @underline
1038
+ s=s+' Q' if @ColorFlag
1039
+ if link and link != ''
1040
+ Link(@x+dx,@y+0.5*h-0.5*@FontSize,GetStringWidth(txt),@FontSize,link)
1041
+ end
1042
+ end
1043
+ out(s) if s
1044
+ @lasth=h
1045
+ if ln > 0
1046
+ # Go to next line
1047
+ @y = @y+h
1048
+ @x = @left_margin if ln==1
1049
+ else
1050
+ @x = @x+w
1051
+ end
1052
+ end
1053
+
1054
+ def MultiCell(w,h,txt,border=0,align='J',fill=0)
1055
+ # Output text with automatic or explicit line breaks
1056
+ cw=@CurrentFont['cw']
1057
+ w=@w-@rMargin-@x if w==0
1058
+ wmax=(w-2*@cMargin)*1000/@FontSize
1059
+ s=txt.gsub('\r','')
1060
+ nb=s.length
1061
+ nb=nb-1 if nb>0 and s[nb-1].chr=='\n'
1062
+ b=0
1063
+ if border != 0
1064
+ case border
1065
+ when 1
1066
+ border='LTRB'
1067
+ b='LRT'
1068
+ b2='LR'
1069
+ else
1070
+ b2=''
1071
+ b2='L' unless border.index('L').nil?
1072
+ b2=b2+'R' unless border.index('R').nil?
1073
+ b=(not border.index('T').nil?) ? (b2+'T') : b2
1074
+ end
1075
+ end
1076
+ sep=-1
1077
+ i=0
1078
+ j=0
1079
+ l=0
1080
+ ns=0
1081
+ nl=1
1082
+ while i<nb
1083
+ # Get next character
1084
+ c=s[i].chr
1085
+ if c=="\n"
1086
+ # Explicit line break
1087
+ if @ws > 0
1088
+ @ws=0
1089
+ out('0 Tw')
1090
+ end
1091
+
1092
+ # Changed from s[j..i] to fix bug reported by Hans Allis.
1093
+ self.Cell(w,h,s[j..i-1],b,2,align,fill)
1094
+
1095
+ i=i+1
1096
+ sep=-1
1097
+ j=i
1098
+ l=0
1099
+ ns=0
1100
+ nl=nl+1
1101
+ b=b2 if border and nl==2
1102
+ else
1103
+ if c==' '
1104
+ sep=i
1105
+ ls=l
1106
+ ns=ns+1
1107
+ end
1108
+ l=l+cw[c[0]]
1109
+ if l>wmax
1110
+ # Automatic line break
1111
+ if sep == -1
1112
+ i += 1 if i == j
1113
+ if @ws>0
1114
+ @ws=0
1115
+ out('0 Tw')
1116
+ end
1117
+ self.Cell(w,h,s[j..i],b,2,align,fill)
1118
+ else
1119
+ if align=='J'
1120
+ @ws=(ns>1) ? (wmax-ls)/1000.0*@FontSize/(ns-1) : 0
1121
+ out(sprintf('%.3f Tw',@ws*@k))
1122
+ end
1123
+ self.Cell(w,h,s[j..sep],b,2,align,fill)
1124
+ i=sep+1
1125
+ end
1126
+ sep=-1
1127
+ j=i
1128
+ l=0
1129
+ ns=0
1130
+ nl += 1
1131
+ b=b2 if border and nl==2
1132
+ else
1133
+ i=i+1
1134
+ end
1135
+ end
1136
+ end
1137
+
1138
+ # Last chunk
1139
+ if @ws > 0
1140
+ @ws = 0
1141
+ out('0 Tw')
1142
+ end
1143
+ b = b+'B' if border!=0 and not border.index('B').nil?
1144
+ self.Cell(w,h,s[j..i],b,2,align,fill)
1145
+ @x=@left_margin
1146
+ end
1147
+
1148
+ # ========================================================================= #
1149
+ # === Text
1150
+ # ========================================================================= #
1151
+ def Text(x, y, txt)
1152
+ # Output a string
1153
+ txt.gsub!(')', '\\)')
1154
+ txt.gsub!('(', '\\(')
1155
+ txt.gsub!('\\', '\\\\')
1156
+ s = sprintf('BT %.2f %.2f Td (%s) Tj ET',x*@k,(@h-y)*@k,txt)
1157
+ s=s+' '+dounderline(x,y,txt) if @underline and txt!=''
1158
+ s='q '+@TextColor+' '+s+' Q' if @ColorFlag
1159
+ out(s)
1160
+ end
1161
+
1162
+ # === Image
1163
+ def Image(file,x,y,w,h=0,type='',link='')
1164
+ # Put an image on the page
1165
+ unless @images.has_key?(file)
1166
+ # First use of image, get info
1167
+ if type.empty?
1168
+ pos=file.rindex('.')
1169
+ if pos.nil?
1170
+ self.Error('Image file has no extension and no type was '+
1171
+ 'specified: '+file)
1172
+ end
1173
+ type=file[pos+1..-1]
1174
+ end
1175
+ type.downcase!
1176
+ if type=='jpg' or type=='jpeg'
1177
+ info=parsejpg(file)
1178
+ elsif type=='png'
1179
+ info=parsepng(file)
1180
+ else
1181
+ self.Error('Unsupported image file type: '+type)
1182
+ end
1183
+ info['i']=@images.length+1
1184
+ @images[file]=info
1185
+ else
1186
+ info=@images[file]
1187
+ end
1188
+ # Automatic width or height calculation
1189
+ w=h*info['w']/info['h'] if w==0
1190
+ h=w*info['h']/info['w'] if h==0
1191
+ out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',
1192
+ w*@k,h*@k,x*@k,(@h-(y+h))*@k,info['i']))
1193
+ Link(x,y,w,h,link) if link and link != ''
1194
+ end
1195
+
1196
+ def Ln(h='')
1197
+ # Line feed; default value is last cell height
1198
+ @x=@left_margin
1199
+ if h.kind_of?(String)
1200
+ @y=@y+@lasth
1201
+ else
1202
+ @y=@y+h
1203
+ end
1204
+ end
1205
+
1206
+ def GetX
1207
+ # Get x position
1208
+ @x
1209
+ end
1210
+
1211
+ def SetX(x)
1212
+ # Set x position
1213
+ if x>=0
1214
+ @x=x
1215
+ else
1216
+ @x=@w+x
1217
+ end
1218
+ end
1219
+
1220
+ def GetY
1221
+ # Get y position
1222
+ @y
1223
+ end
1224
+
1225
+ def SetY(y)
1226
+ # Set y position and reset x
1227
+ @x=@left_margin
1228
+ if y>=0
1229
+ @y=y
1230
+ else
1231
+ @y=@h+y
1232
+ end
1233
+ end
1234
+
1235
+ def SetXY(x,y)
1236
+ # Set x and y positions
1237
+ SetY(y)
1238
+ SetX(x)
1239
+ end
1240
+
1241
+ # === Output
1242
+ def Output(file=nil)
1243
+ # Output PDF to file or return as a string
1244
+
1245
+ # Finish document if necessary
1246
+ self.Close if(@state<3)
1247
+
1248
+ if file.nil?
1249
+ # Return as a string
1250
+ return @buffer
1251
+ else
1252
+ # Save file locally
1253
+ open(file,'wb') do |f|
1254
+ f.write(@buffer)
1255
+ end
1256
+ end
1257
+ end
1258
+
1259
+ private
1260
+
1261
+ def putpages
1262
+ nb = @page
1263
+ unless @AliasNbPages.nil? or @AliasNbPages.empty?
1264
+ # Replace number of pages
1265
+ 1.upto(nb) do |n|
1266
+ @pages[n].gsub!(@AliasNbPages,nb.to_s)
1267
+ end
1268
+ end
1269
+ if @DefOrientation=='P'
1270
+ wPt=@fwPt
1271
+ hPt=@fhPt
1272
+ else
1273
+ wPt=@fhPt
1274
+ hPt=@fwPt
1275
+ end
1276
+ filter=(@compress) ? '/Filter /FlateDecode ' : ''
1277
+ 1.upto(nb) do |n|
1278
+ # Page
1279
+ newobj
1280
+ out('<</Type /Page')
1281
+ out('/Parent 1 0 R')
1282
+ unless @OrientationChanges[n].nil?
1283
+ out(sprintf('/MediaBox [0 0 %.2f %.2f]',hPt,wPt))
1284
+ end
1285
+ out('/Resources 2 0 R')
1286
+ if @PageLinks[n]
1287
+ # Links
1288
+ annots='/Annots ['
1289
+ @PageLinks[n].each do |pl|
1290
+ rect=sprintf('%.2f %.2f %.2f %.2f',
1291
+ pl[0],pl[1],pl[0]+pl[2],pl[1]-pl[3])
1292
+ annots=annots+'<</Type /Annot /Subtype /Link /Rect ['+rect+
1293
+ '] /Border [0 0 0] '
1294
+ if pl[4].kind_of?(String)
1295
+ annots=annots+'/A <</S /URI /URI '+textstring(pl[4])+
1296
+ '>>>>'
1297
+ else
1298
+ l=@links[pl[4]]
1299
+ h=@OrientationChanges[l[0]].nil? ? hPt : wPt
1300
+ annots=annots+sprintf(
1301
+ '/Dest [%d 0 R /XYZ 0 %.2f null]>>',
1302
+ 1+2*l[0],h-l[1]*@k)
1303
+ end
1304
+ end
1305
+ out(annots+']')
1306
+ end
1307
+ out('/Contents '+(@n+1).to_s+' 0 R>>')
1308
+ out('endobj')
1309
+ # Page content
1310
+ p=(@compress) ? Zlib::Deflate.deflate(@pages[n]) : @pages[n]
1311
+ newobj
1312
+ out('<<'+filter+'/Length '+p.length.to_s+'>>')
1313
+ putstream(p)
1314
+ out('endobj')
1315
+ end
1316
+ # Pages root
1317
+ @offsets[1]=@buffer.length
1318
+ out('1 0 obj')
1319
+ out('<</Type /Pages')
1320
+ kids='/Kids ['
1321
+ nb.times do |i|
1322
+ kids=kids+(3+2*i).to_s+' 0 R '
1323
+ end
1324
+ out(kids+']')
1325
+ out('/Count '+nb.to_s)
1326
+ out(sprintf('/MediaBox [0 0 %.2f %.2f]',wPt,hPt))
1327
+ out('>>')
1328
+ out('endobj')
1329
+ end
1330
+
1331
+ # === putfonts
1332
+ def putfonts
1333
+ nf = @n
1334
+ @diffs.each do |diff|
1335
+ # Encodings
1336
+ newobj
1337
+ out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences '+
1338
+ '['+diff+']>>')
1339
+ out('endobj')
1340
+ end
1341
+
1342
+ @FontFiles.each do |file, info|
1343
+ # Font file embedding
1344
+ newobj
1345
+ @FontFiles[file]['n'] = @n
1346
+
1347
+ if self.class.const_defined? 'FPDF_FONTPATH'
1348
+ if FPDF_FONTPATH[-1,1] == '/'
1349
+ file = FPDF_FONTPATH + file
1350
+ else
1351
+ file = FPDF_FONTPATH + '/' + file
1352
+ end
1353
+ end
1354
+
1355
+ size = File.size(file)
1356
+ unless File.exists?(file)
1357
+ Error('Font file not found')
1358
+ end
1359
+
1360
+ out('<</Length ' + size.to_s)
1361
+
1362
+ if file[-2, 2] == '.z'
1363
+ out('/Filter /FlateDecode')
1364
+ end
1365
+ out('/Length1 ' + info['length1'])
1366
+ out('/Length2 ' + info['length2'] + ' /Length3 0') if info['length2']
1367
+ out('>>')
1368
+ open(file, 'rb') do |f|
1369
+ putstream(f.read())
1370
+ end
1371
+ out('endobj')
1372
+ end
1373
+
1374
+ file = 0
1375
+ @fonts.each do |k, font|
1376
+ # Font objects
1377
+ @fonts[k]['n']=@n+1
1378
+ type=font['type']
1379
+ name=font['name']
1380
+ if type=='core'
1381
+ # Standard font
1382
+ newobj
1383
+ out('<</Type /Font')
1384
+ out('/BaseFont /'+name)
1385
+ out('/Subtype /Type1')
1386
+ if name!='Symbol' and name!='ZapfDingbats'
1387
+ out('/Encoding /WinAnsiEncoding')
1388
+ end
1389
+ out('>>')
1390
+ out('endobj')
1391
+ elsif type=='Type1' or type=='TrueType'
1392
+ # Additional Type1 or TrueType font
1393
+ newobj
1394
+ out('<</Type /Font')
1395
+ out('/BaseFont /'+name)
1396
+ out('/Subtype /'+type)
1397
+ out('/FirstChar 32 /LastChar 255')
1398
+ out('/Widths '+(@n+1).to_s+' 0 R')
1399
+ out('/FontDescriptor '+(@n+2).to_s+' 0 R')
1400
+ if font['enc'] and font['enc'] != ''
1401
+ unless font['diff'].nil?
1402
+ out('/Encoding '+(nf+font['diff']).to_s+' 0 R')
1403
+ else
1404
+ out('/Encoding /WinAnsiEncoding')
1405
+ end
1406
+ end
1407
+ out('>>')
1408
+ out('endobj')
1409
+ # Widths
1410
+ newobj
1411
+ cw=font['cw']
1412
+ s='['
1413
+ 32.upto(255) do |i|
1414
+ s << cw[i].to_s+' '
1415
+ end
1416
+ out(s+']')
1417
+ out('endobj')
1418
+ # Descriptor
1419
+ newobj
1420
+ s='<</Type /FontDescriptor /FontName /'+name
1421
+ font['desc'].each do |k, v|
1422
+ s << ' /'+k+' '+v
1423
+ end
1424
+ file=font['file']
1425
+ if file
1426
+ s << ' /FontFile'+(type=='Type1' ? '' : '2')+' '+
1427
+ @FontFiles[file]['n'].to_s+' 0 R'
1428
+ end
1429
+ out(s+'>>')
1430
+ out('endobj')
1431
+ else
1432
+ # Allow for additional types
1433
+ mtd='put'+type.downcase
1434
+ unless self.respond_to?(mtd)
1435
+ self.Error('Unsupported font type: '+type)
1436
+ end
1437
+ self.send(mtd, font)
1438
+ end
1439
+ end
1440
+ end
1441
+
1442
+ def putimages
1443
+ filter=(@compress) ? '/Filter /FlateDecode ' : ''
1444
+ @images.each do |file, info|
1445
+ newobj
1446
+ @images[file]['n']=@n
1447
+ out('<</Type /XObject')
1448
+ out('/Subtype /Image')
1449
+ out('/Width '+info['w'].to_s)
1450
+ out('/Height '+info['h'].to_s)
1451
+ if info['cs']=='Indexed'
1452
+ out("/ColorSpace [/Indexed /DeviceRGB #{info['pal'].length/3-1} #{(@n+1)} 0 R]")
1453
+ else
1454
+ out('/ColorSpace /'+info['cs'])
1455
+ if info['cs']=='DeviceCMYK'
1456
+ out('/Decode [1 0 1 0 1 0 1 0]')
1457
+ end
1458
+ end
1459
+ out('/BitsPerComponent '+info['bpc'].to_s)
1460
+ out('/Filter /'+info['f']) if info['f']
1461
+ unless info['parms'].nil?
1462
+ out(info['parms'])
1463
+ end
1464
+ if info['trns'] and info['trns'].kind_of?(Array)
1465
+ trns=''
1466
+ info['trns'].length.times do |i|
1467
+ trns=trns+info['trns'][i].to_s+' '+info['trns'][i].to_s+' '
1468
+ end
1469
+ out('/Mask ['+trns+']')
1470
+ end
1471
+ out('/Length '+info['data'].length.to_s+'>>')
1472
+ putstream(info['data'])
1473
+ @images[file]['data']=nil
1474
+ out('endobj')
1475
+ # Palette
1476
+ if info['cs']=='Indexed'
1477
+ newobj
1478
+ pal=(@compress) ? Zlib::Deflate.deflate(info['pal']) : info['pal']
1479
+ out('<<'+filter+'/Length '+pal.length.to_s+'>>')
1480
+ putstream(pal)
1481
+ out('endobj')
1482
+ end
1483
+ end
1484
+ end
1485
+
1486
+ def putxobjectdict
1487
+ @images.each_value do |image|
1488
+ out('/I'+image['i'].to_s+' '+image['n'].to_s+' 0 R')
1489
+ end
1490
+ end
1491
+
1492
+ def putresourcedict
1493
+ out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]')
1494
+ out('/Font <<')
1495
+ @fonts.each_value do |font|
1496
+ out('/F'+font['i'].to_s+' '+font['n'].to_s+' 0 R')
1497
+ end
1498
+ out('>>')
1499
+ out('/XObject <<')
1500
+ putxobjectdict
1501
+ out('>>')
1502
+ end
1503
+
1504
+ def putresources
1505
+ putfonts
1506
+ putimages
1507
+ # Resource dictionary
1508
+ @offsets[2]=@buffer.length
1509
+ out('2 0 obj')
1510
+ out('<<')
1511
+ putresourcedict
1512
+ out('>>')
1513
+ out('endobj')
1514
+ end
1515
+
1516
+ def putinfo
1517
+ out('/Producer '+textstring('Ruby FPDF '+FPDF_VERSION))
1518
+ unless @title.nil?
1519
+ out('/Title '+textstring(@title))
1520
+ end
1521
+ unless @subject.nil?
1522
+ out('/Subject '+textstring(@subject))
1523
+ end
1524
+ unless @author.nil?
1525
+ out('/Author '+textstring(@author))
1526
+ end
1527
+ unless @keywords.nil?
1528
+ out('/Keywords '+textstring(@keywords))
1529
+ end
1530
+ unless @creator.nil?
1531
+ out('/Creator '+textstring(@creator))
1532
+ end
1533
+ out('/CreationDate '+textstring('D: '+DateTime.now.to_s))
1534
+ end
1535
+
1536
+ def putcatalog
1537
+ out('/Type /Catalog')
1538
+ out('/Pages 1 0 R')
1539
+ if @ZoomMode=='fullpage'
1540
+ out('/OpenAction [3 0 R /Fit]')
1541
+ elsif @ZoomMode=='fullwidth'
1542
+ out('/OpenAction [3 0 R /FitH null]')
1543
+ elsif @ZoomMode=='real'
1544
+ out('/OpenAction [3 0 R /XYZ null null 1]')
1545
+ elsif not @ZoomMode.kind_of?(String)
1546
+ out('/OpenAction [3 0 R /XYZ null null '+(@ZoomMode/100)+']')
1547
+ end
1548
+
1549
+ if @LayoutMode=='single'
1550
+ out('/PageLayout /SinglePage')
1551
+ elsif @LayoutMode=='continuous'
1552
+ out('/PageLayout /OneColumn')
1553
+ elsif @LayoutMode=='two'
1554
+ out('/PageLayout /TwoColumnLeft')
1555
+ end
1556
+ end
1557
+
1558
+ def putheader
1559
+ out('%PDF-'+@PDFVersion)
1560
+ end
1561
+
1562
+ def puttrailer
1563
+ out('/Size '+(@n+1).to_s)
1564
+ out('/Root '+@n.to_s+' 0 R')
1565
+ out('/Info '+(@n-1).to_s+' 0 R')
1566
+ end
1567
+
1568
+ def enddoc
1569
+ putheader
1570
+ putpages
1571
+ putresources
1572
+ # Info
1573
+ newobj
1574
+ out('<<')
1575
+ putinfo
1576
+ out('>>')
1577
+ out('endobj')
1578
+ # Catalog
1579
+ newobj
1580
+ out('<<')
1581
+ putcatalog
1582
+ out('>>')
1583
+ out('endobj')
1584
+ # Cross-ref
1585
+ o=@buffer.length
1586
+ out('xref')
1587
+ out('0 '+(@n+1).to_s)
1588
+ out('0000000000 65535 f ')
1589
+ 1.upto(@n) do |i|
1590
+ out(sprintf('%010d 00000 n ',@offsets[i]))
1591
+ end
1592
+ # Trailer
1593
+ out('trailer')
1594
+ out('<<')
1595
+ puttrailer
1596
+ out('>>')
1597
+ out('startxref')
1598
+ out(o)
1599
+ out('%%EOF')
1600
+ state=3
1601
+ end
1602
+
1603
+ # === beginpage
1604
+ def beginpage(orientation)
1605
+ @page += 1
1606
+ @pages[@page] = ''
1607
+ @state=2
1608
+ @x=@left_margin
1609
+ @y=@top_margin
1610
+ @lasth = 0
1611
+ @font_family = ''
1612
+ # Page orientation
1613
+ if orientation.empty?
1614
+ orientation=@DefOrientation
1615
+ else
1616
+ orientation=orientation[0].chr.upcase
1617
+ if orientation!=@DefOrientation
1618
+ @OrientationChanges[@page]=true
1619
+ end
1620
+ end
1621
+ if orientation!=@CurOrientation
1622
+ # Change orientation
1623
+ if orientation=='P'
1624
+ @wPt=@fwPt
1625
+ @hPt=@fhPt
1626
+ @w=@fw
1627
+ @h=@fh
1628
+ else
1629
+ @wPt=@fhPt
1630
+ @hPt=@fwPt
1631
+ @w=@fh
1632
+ @h=@fw
1633
+ end
1634
+ @PageBreakTrigger=@h-@bMargin
1635
+ @CurOrientation=orientation
1636
+ end
1637
+ end
1638
+
1639
+ # === endpage
1640
+ def endpage
1641
+ # End of page contents
1642
+ @state=1
1643
+ end
1644
+ # === newobj
1645
+ def newobj
1646
+ # Begin a new object
1647
+ @n=@n+1
1648
+ @offsets[@n]=@buffer.length
1649
+ out(@n.to_s+' 0 obj')
1650
+ end
1651
+
1652
+
1653
+ # === parsejpg
1654
+ def parsejpg(file)
1655
+ # Extract info from a JPEG file
1656
+ a=extractjpginfo(file)
1657
+ raise "Missing or incorrect JPEG file: #{file}" if a.nil?
1658
+
1659
+ if a['channels'].nil? || a['channels']==3
1660
+ colspace='DeviceRGB'
1661
+ elsif a['channels']==4
1662
+ colspace='DeviceCMYK'
1663
+ else
1664
+ colspace='DeviceGray'
1665
+ end
1666
+ bpc= a['bits'] ? a['bits'].to_i : 8
1667
+
1668
+ # Read whole file
1669
+ data = nil
1670
+ open(file, 'rb') do |f|
1671
+ data = f.read
1672
+ end
1673
+ return {'w'=>a['width'],'h'=>a['height'],'cs'=>colspace,'bpc'=>bpc,'f'=>'DCTDecode','data'=>data}
1674
+ end
1675
+
1676
+ def parsepng(file)
1677
+ # Extract info from a PNG file
1678
+ f=open(file,'rb')
1679
+ # Check signature
1680
+ unless f.read(8)==137.chr+'PNG'+13.chr+10.chr+26.chr+10.chr
1681
+ self.Error('Not a PNG file: '+file)
1682
+ end
1683
+ # Read header chunk
1684
+ f.read(4)
1685
+ if f.read(4)!='IHDR'
1686
+ self.Error('Incorrect PNG file: '+file)
1687
+ end
1688
+ w=freadint(f)
1689
+ h=freadint(f)
1690
+ bpc=f.read(1)[0]
1691
+ if bpc>8
1692
+ self.Error('16-bit depth not supported: '+file)
1693
+ end
1694
+ ct=f.read(1)[0]
1695
+ if ct==0
1696
+ colspace='DeviceGray'
1697
+ elsif ct==2
1698
+ colspace='DeviceRGB'
1699
+ elsif ct==3
1700
+ colspace='Indexed'
1701
+ else
1702
+ self.Error('Alpha channel not supported: '+file)
1703
+ end
1704
+ if f.read(1)[0]!=0
1705
+ self.Error('Unknown compression method: '+file)
1706
+ end
1707
+ if f.read(1)[0]!=0
1708
+ self.Error('Unknown filter method: '+file)
1709
+ end
1710
+ if f.read(1)[0]!=0
1711
+ self.Error('Interlacing not supported: '+file)
1712
+ end
1713
+ f.read(4)
1714
+ parms='/DecodeParms <</Predictor 15 /Colors '+(ct==2 ? '3' : '1')+
1715
+ ' /BitsPerComponent '+bpc.to_s+' /Columns '+w.to_s+'>>'
1716
+ # Scan chunks looking for palette, transparency and image data
1717
+ pal=''
1718
+ trns=''
1719
+ data=''
1720
+ begin
1721
+ n=freadint(f)
1722
+ type=f.read(4)
1723
+ if type=='PLTE'
1724
+ # Read palette
1725
+ pal=f.read(n)
1726
+ f.read(4)
1727
+ elsif type=='tRNS'
1728
+ # Read transparency info
1729
+ t=f.read(n)
1730
+ if ct==0
1731
+ trns=[t[1]]
1732
+ elsif ct==2
1733
+ trns=[t[1],t[3],t[5]]
1734
+ else
1735
+ pos=t.index(0)
1736
+ trns=[pos] unless pos.nil?
1737
+ end
1738
+ f.read(4)
1739
+ elsif type=='IDAT'
1740
+ # Read image data block
1741
+ data << f.read(n)
1742
+ f.read(4)
1743
+ elsif type=='IEND'
1744
+ break
1745
+ else
1746
+ f.read(n+4)
1747
+ end
1748
+ end while n
1749
+ if colspace == 'Indexed' and pal.empty?
1750
+ self.Error('Missing palette in '+file)
1751
+ end
1752
+ f.close
1753
+ {
1754
+ 'w'=>w,'h'=>h,'cs'=>colspace,'bpc'=>bpc,'f'=>'FlateDecode',
1755
+ 'parms'=>parms,
1756
+ 'pal'=>pal,
1757
+ 'trns'=>trns,
1758
+ 'data'=>data
1759
+ }
1760
+ end
1761
+
1762
+ def freadint(f)
1763
+ # Read a 4-byte integer from file
1764
+ a = f.read(4).unpack('N')
1765
+ return a[0]
1766
+ end
1767
+
1768
+ def freadshort(f)
1769
+ a = f.read(2).unpack('n')
1770
+ return a[0]
1771
+ end
1772
+
1773
+ def freadbyte(f)
1774
+ a = f.read(1).unpack('C')
1775
+ return a[0]
1776
+ end
1777
+
1778
+ def textstring(s)
1779
+ # Format a text string
1780
+ '('+escape(s)+')'
1781
+ end
1782
+
1783
+ def escape(s)
1784
+ # Add \ before \, ( and )
1785
+ s.gsub('\\','\\\\').gsub('(','\\(').gsub(')','\\)')
1786
+ end
1787
+
1788
+ def putstream(s)
1789
+ out('stream')
1790
+ out(s)
1791
+ out('endstream')
1792
+ end
1793
+
1794
+ def out(s)
1795
+ # Add a line to the document
1796
+ if @state==2
1797
+ @pages[@page]=@pages[@page]+s+"\n"
1798
+ else
1799
+ @buffer=@buffer+s.to_s+"\n"
1800
+ end
1801
+ end
1802
+
1803
+ # jpeg marker codes
1804
+
1805
+ M_SOF0 = 0xc0
1806
+ M_SOF1 = 0xc1
1807
+ M_SOF2 = 0xc2
1808
+ M_SOF3 = 0xc3
1809
+
1810
+ M_SOF5 = 0xc5
1811
+ M_SOF6 = 0xc6
1812
+ M_SOF7 = 0xc7
1813
+
1814
+ M_SOF9 = 0xc9
1815
+ M_SOF10 = 0xca
1816
+ M_SOF11 = 0xcb
1817
+
1818
+ M_SOF13 = 0xcd
1819
+ M_SOF14 = 0xce
1820
+ M_SOF15 = 0xcf
1821
+
1822
+ M_SOI = 0xd8
1823
+ M_EOI = 0xd9
1824
+ # ========================================================================= #
1825
+ # === M_SOS
1826
+ # ========================================================================= #
1827
+ M_SOS = 0xda
1828
+
1829
+ # ========================================================================= #
1830
+ # === extractjpginfo
1831
+ # ========================================================================= #
1832
+ def extractjpginfo(file)
1833
+ result = nil
1834
+
1835
+ open(file, 'rb') do |f|
1836
+ marker = jpegnextmarker(f)
1837
+
1838
+ if marker != M_SOI
1839
+ return nil
1840
+ end
1841
+
1842
+ while true
1843
+ marker = jpegnextmarker(f)
1844
+
1845
+ case marker
1846
+ when M_SOF0,
1847
+ M_SOF1,
1848
+ M_SOF2,
1849
+ M_SOF3,
1850
+ M_SOF5,
1851
+ M_SOF6,
1852
+ M_SOF7,
1853
+ M_SOF9,
1854
+ M_SOF10,
1855
+ M_SOF11,
1856
+ M_SOF13,
1857
+ M_SOF14,
1858
+ M_SOF15
1859
+
1860
+ length = freadshort(f)
1861
+
1862
+ if result.nil?
1863
+ result = {}
1864
+
1865
+ result['bits'] = freadbyte(f)
1866
+ result['height'] = freadshort(f)
1867
+ result['width'] = freadshort(f)
1868
+ result['channels'] = freadbyte(f)
1869
+
1870
+ f.seek(length - 8, IO::SEEK_CUR)
1871
+ else
1872
+ f.seek(length - 2, IO::SEEK_CUR)
1873
+ end
1874
+ when M_SOS,
1875
+ M_EOI
1876
+ return result
1877
+ else
1878
+ length = freadshort(f)
1879
+ f.seek(length - 2, IO::SEEK_CUR)
1880
+ end
1881
+ end
1882
+ end
1883
+ end
1884
+
1885
+ # ========================================================================= #
1886
+ # === jpegnextmarker
1887
+ # ========================================================================= #
1888
+ def jpegnextmarker(f)
1889
+ while true
1890
+ # look for 0xff
1891
+ while (c = freadbyte(f)) != 0xff
1892
+ end
1893
+
1894
+ c = freadbyte(f)
1895
+
1896
+ if c != 0
1897
+ return c
1898
+ end
1899
+ end
1900
+ end
1901
+
1902
+ end