PageRenderTime 68ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/plugins/rfpdf/lib/rfpdf/fpdf.rb

https://bitbucket.org/eimajenthat/redmine
Ruby | 1632 lines | 1317 code | 145 blank | 170 comment | 163 complexity | 7544c2319f2234d41bfd3a3c5890a365 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. # Ruby FPDF 1.53d
  2. # FPDF 1.53 by Olivier Plathey ported to Ruby by Brian Ollenberger
  3. # Copyright 2005 Brian Ollenberger
  4. # Please retain this entire copyright notice. If you distribute any
  5. # modifications, place an additional comment here that clearly indicates
  6. # that it was modified. You may (but are not send any useful modifications that you make
  7. # back to me at http://zeropluszero.com/software/fpdf/
  8. # Bug fixes, examples, external fonts, JPEG support, and upgrade to version
  9. # 1.53 contributed by Kim Shrier.
  10. #
  11. # Bookmark support contributed by Sylvain Lafleur.
  12. #
  13. # EPS support contributed by Thiago Jackiw, ported from the PHP version by Valentin Schmidt.
  14. #
  15. # Bookmarks contributed by Sylvain Lafleur.
  16. #
  17. # 1.53 contributed by Ed Moss
  18. # Make sure all \n references are inside double quotes - Fix some multicell bugs
  19. # Handle "\n" at the beginning of a string
  20. # Bookmarks contributed by Sylvain Lafleur.
  21. require 'date'
  22. require 'zlib'
  23. class FPDF
  24. include RFPDF
  25. attr_accessor :default_font
  26. FPDF_VERSION = '1.53d'
  27. Charwidths = {
  28. 'courier'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
  29. 'courierB'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
  30. 'courierI'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
  31. 'courierBI'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
  32. 'helvetica'=>[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],
  33. 'helveticaB'=>[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],
  34. 'helveticaI'=>[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],
  35. 'helveticaBI'=>[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],
  36. 'times'=>[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],
  37. 'timesB'=>[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],
  38. 'timesI'=>[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],
  39. 'timesBI'=>[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],
  40. 'symbol'=>[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, 713, 500, 549, 833, 778, 439, 333, 333, 500, 549, 250, 549, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 549, 549, 549, 444, 549, 722, 667, 722, 612, 611, 763, 603, 722, 333, 631, 722, 686, 889, 722, 722, 768, 741, 556, 592, 611, 690, 439, 768, 645, 795, 611, 333, 863, 333, 658, 500, 500, 631, 549, 549, 494, 439, 521, 411, 603, 329, 603, 549, 549, 576, 521, 549, 549, 521, 549, 603, 439, 576, 713, 686, 493, 686, 494, 480, 200, 480, 549, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 750, 620, 247, 549, 167, 713, 500, 753, 753, 753, 753, 1042, 987, 603, 987, 603, 400, 549, 411, 549, 549, 713, 494, 460, 549, 549, 549, 549, 1000, 603, 1000, 658, 823, 686, 795, 987, 768, 768, 823, 768, 768, 713, 713, 713, 713, 713, 713, 713, 768, 713, 790, 790, 890, 823, 549, 250, 713, 603, 603, 1042, 987, 603, 987, 603, 494, 329, 790, 790, 786, 713, 384, 384, 384, 384, 384, 384, 494, 494, 494, 494, 0, 329, 274, 686, 686, 686, 384, 384, 384, 384, 384, 384, 494, 494, 494, 0],
  41. 'zapfdingbats'=>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 974, 961, 974, 980, 719, 789, 790, 791, 690, 960, 939, 549, 855, 911, 933, 911, 945, 974, 755, 846, 762, 761, 571, 677, 763, 760, 759, 754, 494, 552, 537, 577, 692, 786, 788, 788, 790, 793, 794, 816, 823, 789, 841, 823, 833, 816, 831, 923, 744, 723, 749, 790, 792, 695, 776, 768, 792, 759, 707, 708, 682, 701, 826, 815, 789, 789, 707, 687, 696, 689, 786, 787, 713, 791, 785, 791, 873, 761, 762, 762, 759, 759, 892, 892, 788, 784, 438, 138, 277, 415, 392, 392, 668, 668, 0, 390, 390, 317, 317, 276, 276, 509, 509, 410, 410, 234, 234, 334, 334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 732, 544, 544, 910, 667, 760, 760, 776, 595, 694, 626, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 894, 838, 1016, 458, 748, 924, 748, 918, 927, 928, 928, 834, 873, 828, 924, 924, 917, 930, 931, 463, 883, 836, 836, 867, 867, 696, 696, 874, 0, 874, 760, 946, 771, 865, 771, 888, 967, 888, 831, 873, 927, 970, 918, 0]
  42. }
  43. def initialize(orientation='P', unit='mm', format='A4')
  44. # Initialization of properties
  45. @page=0
  46. @n=2
  47. @buffer=''
  48. @pages=[]
  49. @OrientationChanges=[]
  50. @state=0
  51. @default_font = "arial"
  52. @fonts={}
  53. @FontFiles={}
  54. @diffs=[]
  55. @images={}
  56. @links=[]
  57. @PageLinks={}
  58. @InFooter=false
  59. @FontFamily=''
  60. @FontStyle=''
  61. @FontSizePt=12
  62. @underline= false
  63. @DrawColor='0 G'
  64. @FillColor='0 g'
  65. @TextColor='0 g'
  66. @ColorFlag=false
  67. @ws=0
  68. @offsets=[]
  69. # Standard fonts
  70. @CoreFonts={}
  71. @CoreFonts['courier']='Courier'
  72. @CoreFonts['courierB']='Courier-Bold'
  73. @CoreFonts['courierI']='Courier-Oblique'
  74. @CoreFonts['courierBI']='Courier-BoldOblique'
  75. @CoreFonts['helvetica']='Helvetica'
  76. @CoreFonts['helveticaB']='Helvetica-Bold'
  77. @CoreFonts['helveticaI']='Helvetica-Oblique'
  78. @CoreFonts['helveticaBI']='Helvetica-BoldOblique'
  79. @CoreFonts['times']='Times-Roman'
  80. @CoreFonts['timesB']='Times-Bold'
  81. @CoreFonts['timesI']='Times-Italic'
  82. @CoreFonts['timesBI']='Times-BoldItalic'
  83. @CoreFonts['symbol']='Symbol'
  84. @CoreFonts['zapfdingbats']='ZapfDingbats'
  85. # Scale factor
  86. if unit=='pt'
  87. @k=1
  88. elsif unit=='mm'
  89. @k=72/25.4
  90. elsif unit=='cm'
  91. @k=72/2.54;
  92. elsif unit=='in'
  93. @k=72
  94. else
  95. raise 'Incorrect unit: '+unit
  96. end
  97. # Page format
  98. if format.is_a? String
  99. format.downcase!
  100. if format=='a3'
  101. format=[841.89,1190.55]
  102. elsif format=='a4'
  103. format=[595.28,841.89]
  104. elsif format=='a5'
  105. format=[420.94,595.28]
  106. elsif format=='letter'
  107. format=[612,792]
  108. elsif format=='legal'
  109. format=[612,1008]
  110. else
  111. raise 'Unknown page format: '+format
  112. end
  113. @fwPt,@fhPt=format
  114. else
  115. @fwPt=format[0]*@k
  116. @fhPt=format[1]*@k
  117. end
  118. @fw=@fwPt/@k;
  119. @fh=@fhPt/@k;
  120. # Page orientation
  121. orientation.downcase!
  122. if orientation=='p' or orientation=='portrait'
  123. @DefOrientation='P'
  124. @wPt=@fwPt
  125. @hPt=@fhPt
  126. elsif orientation=='l' or orientation=='landscape'
  127. @DefOrientation='L'
  128. @wPt=@fhPt
  129. @hPt=@fwPt
  130. else
  131. raise 'Incorrect orientation: '+orientation
  132. end
  133. @CurOrientation=@DefOrientation
  134. @w=@wPt/@k
  135. @h=@hPt/@k
  136. # Page margins (1 cm)
  137. margin=28.35/@k
  138. SetMargins(margin,margin)
  139. # Interior cell margin (1 mm)
  140. @cMargin=margin/10
  141. # Line width (0.2 mm)
  142. @LineWidth=0.567/@k
  143. # Automatic page break
  144. SetAutoPageBreak(true,2*margin)
  145. # Full width display mode
  146. SetDisplayMode('fullwidth')
  147. # Enable compression
  148. SetCompression(true)
  149. # Set default PDF version number
  150. @PDFVersion='1.3'
  151. end
  152. def GetMargins()
  153. return @lMargin, @tMargin, @rMargin
  154. end
  155. def SetMargins(left, top, right=-1)
  156. # Set left, top and right margins
  157. @lMargin=left
  158. @tMargin=top
  159. right=left if right==-1
  160. @rMargin=right
  161. end
  162. def SetLeftMargin(margin)
  163. # Set left margin
  164. @lMargin=margin
  165. @x=margin if @page>0 and @x<margin
  166. end
  167. def SetTopMargin(margin)
  168. # Set top margin
  169. @tMargin=margin
  170. end
  171. def SetRightMargin(margin)
  172. #Set right margin
  173. @rMargin=margin
  174. end
  175. def SetAutoPageBreak(auto, margin=0)
  176. # Set auto page break mode and triggering margin
  177. @AutoPageBreak=auto
  178. @bMargin=margin
  179. @PageBreakTrigger=@h-margin
  180. end
  181. def SetDisplayMode(zoom, layout='continuous')
  182. # Set display mode in viewer
  183. if zoom=='fullpage' or zoom=='fullwidth' or zoom=='real' or
  184. zoom=='default' or not zoom.kind_of? String
  185. @ZoomMode=zoom;
  186. elsif zoom=='zoom'
  187. @ZoomMode=layout
  188. else
  189. raise 'Incorrect zoom display mode: '+zoom
  190. end
  191. if layout=='single' or layout=='continuous' or layout=='two' or
  192. layout=='default'
  193. @LayoutMode=layout
  194. elsif zoom!='zoom'
  195. raise 'Incorrect layout display mode: '+layout
  196. end
  197. end
  198. def SetCompression(compress)
  199. # Set page compression
  200. @compress = compress
  201. end
  202. def SetTitle(title)
  203. # Title of document
  204. @title=title
  205. end
  206. def SetSubject(subject)
  207. # Subject of document
  208. @subject=subject
  209. end
  210. def SetAuthor(author)
  211. # Author of document
  212. @author=author
  213. end
  214. def SetKeywords(keywords)
  215. # Keywords of document
  216. @keywords=keywords
  217. end
  218. def SetCreator(creator)
  219. # Creator of document
  220. @creator=creator
  221. end
  222. def AliasNbPages(aliasnb='{nb}')
  223. # Define an alias for total number of pages
  224. @AliasNbPages=aliasnb
  225. end
  226. def Error(msg)
  227. raise 'FPDF error: '+msg
  228. end
  229. def Open
  230. # Begin document
  231. @state=1
  232. end
  233. def Close
  234. # Terminate document
  235. return if @state==3
  236. self.AddPage if @page==0
  237. # Page footer
  238. @InFooter=true
  239. self.Footer
  240. @InFooter=false
  241. # Close page
  242. endpage
  243. # Close document
  244. enddoc
  245. end
  246. def AddPage(orientation='')
  247. # Start a new page
  248. self.Open if @state==0
  249. family=@FontFamily
  250. style=@FontStyle+(@underline ? 'U' : '')
  251. size=@FontSizePt
  252. lw=@LineWidth
  253. dc=@DrawColor
  254. fc=@FillColor
  255. tc=@TextColor
  256. cf=@ColorFlag
  257. if @page>0
  258. # Page footer
  259. @InFooter=true
  260. self.Footer
  261. @InFooter=false
  262. # Close page
  263. endpage
  264. end
  265. # Start new page
  266. beginpage(orientation)
  267. # Set line cap style to square
  268. out('2 J')
  269. # Set line width
  270. @LineWidth=lw
  271. out(sprintf('%.2f w',lw*@k))
  272. # Set font
  273. SetFont(family,style,size) if family
  274. # Set colors
  275. @DrawColor=dc
  276. out(dc) if dc!='0 G'
  277. @FillColor=fc
  278. out(fc) if fc!='0 g'
  279. @TextColor=tc
  280. @ColorFlag=cf
  281. # Page header
  282. self.Header
  283. # Restore line width
  284. if @LineWidth!=lw
  285. @LineWidth=lw
  286. out(sprintf('%.2f w',lw*@k))
  287. end
  288. # Restore font
  289. self.SetFont(family,style,size) if family
  290. # Restore colors
  291. if @DrawColor!=dc
  292. @DrawColor=dc
  293. out(dc)
  294. end
  295. if @FillColor!=fc
  296. @FillColor=fc
  297. out(fc)
  298. end
  299. @TextColor=tc
  300. @ColorFlag=cf
  301. end
  302. alias_method :add_page, :AddPage
  303. def Header
  304. # To be implemented in your inherited class
  305. end
  306. def Footer
  307. # To be implemented in your inherited class
  308. end
  309. def PageNo
  310. # Get current page number
  311. @page
  312. end
  313. def SetDrawColor(r,g=-1,b=-1)
  314. # Set color for all stroking operations
  315. if (r==0 and g==0 and b==0) or g==-1
  316. @DrawColor=sprintf('%.3f G',r/255.0)
  317. else
  318. @DrawColor=sprintf('%.3f %.3f %.3f RG',r/255.0,g/255.0,b/255.0)
  319. end
  320. out(@DrawColor) if(@page>0)
  321. end
  322. def SetFillColor(r,g=-1,b=-1)
  323. # Set color for all filling operations
  324. if (r==0 and g==0 and b==0) or g==-1
  325. @FillColor=sprintf('%.3f g',r/255.0)
  326. else
  327. @FillColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
  328. end
  329. @ColorFlag=(@FillColor!=@TextColor)
  330. out(@FillColor) if(@page>0)
  331. end
  332. def SetTextColor(r,g=-1,b=-1)
  333. # Set color for text
  334. if (r==0 and g==0 and b==0) or g==-1
  335. @TextColor=sprintf('%.3f g',r/255.0)
  336. else
  337. @TextColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
  338. end
  339. @ColorFlag=(@FillColor!=@TextColor)
  340. end
  341. def GetCharWidth(widths, index)
  342. if index.is_a?(String)
  343. widths[index.ord]
  344. else
  345. widths[index]
  346. end
  347. end
  348. def GetStringWidth(s)
  349. # Get width of a string in the current font
  350. cw=@CurrentFont['cw']
  351. w=0
  352. s.each_byte do |c|
  353. w=w+GetCharWidth(cw, c)
  354. end
  355. w*@FontSize/1000.0
  356. end
  357. def SetLineWidth(width)
  358. # Set line width
  359. @LineWidth=width
  360. out(sprintf('%.2f w',width*@k)) if @page>0
  361. end
  362. def Circle(mid_x, mid_y, radius, style='')
  363. mid_y = (@h-mid_y)*@k
  364. out(sprintf("q\n")) # postscript content in pdf
  365. # init line type etc. with /GSD gs G g (grey) RG rg (RGB) w=line witdh etc.
  366. out(sprintf("1 j\n")) # line join
  367. # translate ("move") circle to mid_y, mid_y
  368. out(sprintf("1 0 0 1 %f %f cm", mid_x, mid_y))
  369. kappa = 0.5522847498307933984022516322796
  370. # Quadrant 1
  371. x_s = 0.0 # 12 o'clock
  372. y_s = 0.0 + radius
  373. x_e = 0.0 + radius # 3 o'clock
  374. y_e = 0.0
  375. out(sprintf("%f %f m\n", x_s, y_s)) # move to 12 o'clock
  376. # cubic bezier control point 1, start height and kappa * radius to the right
  377. bx_e1 = x_s + (radius * kappa)
  378. by_e1 = y_s
  379. # cubic bezier control point 2, end and kappa * radius above
  380. bx_e2 = x_e
  381. by_e2 = y_e + (radius * kappa)
  382. # draw cubic bezier from current point to x_e/y_e with bx_e1/by_e1 and bx_e2/by_e2 as bezier control points
  383. out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
  384. # Quadrant 2
  385. x_s = x_e
  386. y_s = y_e # 3 o'clock
  387. x_e = 0.0
  388. y_e = 0.0 - radius # 6 o'clock
  389. bx_e1 = x_s # cubic bezier point 1
  390. by_e1 = y_s - (radius * kappa)
  391. bx_e2 = x_e + (radius * kappa) # cubic bezier point 2
  392. by_e2 = y_e
  393. out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
  394. # Quadrant 3
  395. x_s = x_e
  396. y_s = y_e # 6 o'clock
  397. x_e = 0.0 - radius
  398. y_e = 0.0 # 9 o'clock
  399. bx_e1 = x_s - (radius * kappa) # cubic bezier point 1
  400. by_e1 = y_s
  401. bx_e2 = x_e # cubic bezier point 2
  402. by_e2 = y_e - (radius * kappa)
  403. out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
  404. # Quadrant 4
  405. x_s = x_e
  406. y_s = y_e # 9 o'clock
  407. x_e = 0.0
  408. y_e = 0.0 + radius # 12 o'clock
  409. bx_e1 = x_s # cubic bezier point 1
  410. by_e1 = y_s + (radius * kappa)
  411. bx_e2 = x_e - (radius * kappa) # cubic bezier point 2
  412. by_e2 = y_e
  413. out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
  414. if style=='F'
  415. op='f'
  416. elsif style=='FD' or style=='DF'
  417. op='b'
  418. else
  419. op='s'
  420. end
  421. out(sprintf("#{op}\n")) # stroke circle, do not fill and close path
  422. # for filling etc. b, b*, f, f*
  423. out(sprintf("Q\n")) # finish postscript in PDF
  424. end
  425. def Line(x1, y1, x2, y2)
  426. # Draw a line
  427. out(sprintf('%.2f %.2f m %.2f %.2f l S',
  428. x1*@k,(@h-y1)*@k,x2*@k,(@h-y2)*@k))
  429. end
  430. def Rect(x, y, w, h, style='')
  431. # Draw a rectangle
  432. if style=='F'
  433. op='f'
  434. elsif style=='FD' or style=='DF'
  435. op='B'
  436. else
  437. op='S'
  438. end
  439. # x y width height re
  440. out(sprintf('%.2f %.2f %.2f %.2f re %s', x*@k,(@h-y)*@k,w*@k,-h*@k,op))
  441. end
  442. def AddFont(family, style='', file='')
  443. # Add a TrueType or Type1 font
  444. family = family.downcase
  445. family = 'helvetica' if family == 'arial'
  446. style = style.upcase
  447. style = 'BI' if style == 'IB'
  448. fontkey = family + style
  449. if @fonts.has_key?(fontkey)
  450. self.Error("Font already added: #{family} #{style}")
  451. end
  452. file = family.gsub(' ', '') + style.downcase + '.rb' if file == ''
  453. if self.class.const_defined? 'FPDF_FONTPATH'
  454. if FPDF_FONTPATH[-1,1] == '/'
  455. file = FPDF_FONTPATH + file
  456. else
  457. file = FPDF_FONTPATH + '/' + file
  458. end
  459. end
  460. # Changed from "require file" to fix bug reported by Hans Allis.
  461. load file
  462. if FontDef.desc.nil?
  463. self.Error("Could not include font definition file #{file}")
  464. end
  465. i = @fonts.length + 1
  466. @fonts[fontkey] = {'i' => i,
  467. 'type' => FontDef.type,
  468. 'name' => FontDef.name,
  469. 'desc' => FontDef.desc,
  470. 'up' => FontDef.up,
  471. 'ut' => FontDef.ut,
  472. 'cw' => FontDef.cw,
  473. 'enc' => FontDef.enc,
  474. 'file' => FontDef.file
  475. }
  476. if FontDef.diff
  477. # Search existing encodings
  478. unless @diffs.include?(FontDef.diff)
  479. @diffs.push(FontDef.diff)
  480. @fonts[fontkey]['diff'] = @diffs.length - 1
  481. end
  482. end
  483. if FontDef.file
  484. if FontDef.type == 'TrueType'
  485. @FontFiles[FontDef.file] = {'length1' => FontDef.originalsize}
  486. else
  487. @FontFiles[FontDef.file] = {'length1' => FontDef.size1, 'length2' => FontDef.size2}
  488. end
  489. end
  490. return self
  491. end
  492. def SetFont(family, style='', size=0)
  493. # Select a font; size given in points
  494. family.downcase!
  495. family=@FontFamily if family==''
  496. if family=='arial'
  497. family='helvetica'
  498. elsif family=='symbol' or family=='zapfdingbats'
  499. style=''
  500. end
  501. style.upcase!
  502. unless style.index('U').nil?
  503. @underline=true
  504. style.gsub!('U','')
  505. else
  506. @underline=false;
  507. end
  508. style='BI' if style=='IB'
  509. size=@FontSizePt if size==0
  510. # Test if font is already selected
  511. return if @FontFamily==family and
  512. @FontStyle==style and @FontSizePt==size
  513. # Test if used for the first time
  514. fontkey=family+style
  515. unless @fonts.has_key?(fontkey)
  516. if @CoreFonts.has_key?(fontkey)
  517. unless Charwidths.has_key?(fontkey)
  518. raise 'Font unavailable'
  519. end
  520. @fonts[fontkey]={
  521. 'i'=>@fonts.size,
  522. 'type'=>'core',
  523. 'name'=>@CoreFonts[fontkey],
  524. 'up'=>-100,
  525. 'ut'=>50,
  526. 'cw'=>Charwidths[fontkey]}
  527. else
  528. raise 'Font unavailable'
  529. end
  530. end
  531. #Select it
  532. @FontFamily=family
  533. @FontStyle=style;
  534. @FontSizePt=size
  535. @FontSize=size/@k;
  536. @CurrentFont=@fonts[fontkey]
  537. if @page>0
  538. out(sprintf('BT /F%d %.2f Tf ET', @CurrentFont['i'], @FontSizePt))
  539. end
  540. end
  541. def SetFontSize(size)
  542. # Set font size in points
  543. return if @FontSizePt==size
  544. @FontSizePt=size
  545. @FontSize=size/@k
  546. if @page>0
  547. out(sprintf('BT /F%d %.2f Tf ET',@CurrentFont['i'],@FontSizePt))
  548. end
  549. end
  550. def AddLink
  551. # Create a new internal link
  552. @links.push([0, 0])
  553. @links.size
  554. end
  555. def SetLink(link, y=0, page=-1)
  556. # Set destination of internal link
  557. y=@y if y==-1
  558. page=@page if page==-1
  559. @links[link]=[page, y]
  560. end
  561. def Link(x, y, w, h, link)
  562. # Put a link on the page
  563. @PageLinks[@page]=Array.new unless @PageLinks.has_key?(@Page)
  564. @PageLinks[@page].push([x*@k,@hPt-y*@k,w*@k,h*@k,link])
  565. end
  566. def Text(x, y, txt)
  567. # Output a string
  568. s=sprintf('BT %.2f %.2f Td (%s) Tj ET',x*@k,(@h-y)*@k, escape(txt));
  569. s=s+' '+dounderline(x,y,txt) if @underline and txt!=''
  570. s='q '+@TextColor+' '+s+' Q' if @ColorFlag
  571. out(s)
  572. end
  573. def AcceptPageBreak
  574. # Accept automatic page break or not
  575. @AutoPageBreak
  576. end
  577. def BreakThePage?(h)
  578. if (@y + h) > @PageBreakTrigger and !@InFooter and self.AcceptPageBreak
  579. true
  580. else
  581. false
  582. end
  583. end
  584. def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
  585. # Output a cell
  586. if self.BreakThePage?(h)
  587. # Automatic page break
  588. x=@x
  589. ws=@ws
  590. if ws>0
  591. @ws=0
  592. out('0 Tw')
  593. end
  594. self.AddPage(@CurOrientation)
  595. @x=x
  596. if ws>0
  597. @ws=ws
  598. out(sprintf('%.3f Tw',ws*@k))
  599. end
  600. end
  601. w=@w-@rMargin-@x if w==0
  602. s=''
  603. if fill==1 or border==1
  604. if fill==1
  605. op=(border==1) ? 'B' : 'f'
  606. else
  607. op='S'
  608. end
  609. s=sprintf('%.2f %.2f %.2f %.2f re %s ',@x*@k,(@h-@y)*@k,w*@k,-h*@k,op)
  610. end
  611. if border.is_a? String
  612. x=@x
  613. y=@y
  614. unless border.index('L').nil?
  615. s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
  616. x*@k,(@h-y)*@k,x*@k,(@h-(y+h))*@k)
  617. end
  618. unless border.index('T').nil?
  619. s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
  620. x*@k,(@h-y)*@k,(x+w)*@k,(@h-y)*@k)
  621. end
  622. unless border.index('R').nil?
  623. s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
  624. (x+w)*@k,(@h-y)*@k,(x+w)*@k,(@h-(y+h))*@k)
  625. end
  626. unless border.index('B').nil?
  627. s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
  628. x*@k,(@h-(y+h))*@k,(x+w)*@k,(@h-(y+h))*@k)
  629. end
  630. end
  631. if txt!=''
  632. if align=='R'
  633. dx=w-@cMargin-self.GetStringWidth(txt)
  634. elsif align=='C'
  635. dx=(w-self.GetStringWidth(txt))/2
  636. else
  637. dx=@cMargin
  638. end
  639. if @ColorFlag
  640. s=s+'q '+@TextColor+' '
  641. end
  642. s=s+sprintf('BT %.2f %.2f Td (%s) Tj ET',
  643. (@x+dx)*@k,(@h-(@y+0.5*h+0.3*@FontSize))*@k,escape(txt))
  644. s=s+' '+dounderline(@x+dx,@y+0.5*h+0.3*@FontSize,txt) if @underline
  645. s=s+' Q' if @ColorFlag
  646. if link and link != ''
  647. Link(@x+dx,@y+0.5*h-0.5*@FontSize,GetStringWidth(txt),@FontSize,link)
  648. end
  649. end
  650. out(s) if s
  651. @lasth=h
  652. if ln>0
  653. # Go to next line
  654. @y=@y+h
  655. @x=@lMargin if ln==1
  656. else
  657. @x=@x+w
  658. end
  659. end
  660. def MultiCell(w,h,txt,border=0,align='J',fill=0)
  661. # Output text with automatic or explicit line breaks
  662. cw=@CurrentFont['cw']
  663. w=@w-@rMargin-@x if w==0
  664. wmax=(w-2*@cMargin)*1000/@FontSize
  665. s=txt.gsub("\r",'')
  666. nb=s.length
  667. nb=nb-1 if nb>0 and s[nb-1].chr=="\n"
  668. b=0
  669. if border!=0
  670. if border==1
  671. border='LTRB'
  672. b='LRT'
  673. b2='LR'
  674. else
  675. b2=''
  676. b2='L' unless border.index('L').nil?
  677. b2=b2+'R' unless border.index('R').nil?
  678. b=(not border.index('T').nil?) ? (b2+'T') : b2
  679. end
  680. end
  681. sep=-1
  682. to_index=0
  683. from_j=0
  684. l=0
  685. ns=0
  686. nl=1
  687. while to_index<nb
  688. # Get next character
  689. char=s[to_index]
  690. if char=="\n"[0]
  691. # Explicit line break
  692. if @ws>0
  693. @ws=0
  694. out('0 Tw')
  695. end
  696. #Ed Moss
  697. end_i = to_index == 0 ? 0 : to_index - 1
  698. # Changed from s[from_j..to_index] to fix bug reported by Hans Allis.
  699. self.Cell(w,h,s[from_j..end_i],b,2,align,fill)
  700. #
  701. to_index=to_index+1
  702. sep=-1
  703. from_j=to_index
  704. l=0
  705. ns=0
  706. nl=nl+1
  707. b=b2 if border and nl==2
  708. else
  709. if char==' '[0]
  710. sep=to_index
  711. ls=l
  712. ns=ns+1
  713. end
  714. l=l+GetCharWidth(cw, char)
  715. if l>wmax
  716. # Automatic line break
  717. if sep==-1
  718. to_index=to_index+1 if to_index==from_j
  719. if @ws>0
  720. @ws=0
  721. out('0 Tw')
  722. end
  723. #Ed Moss
  724. self.Cell(w,h,s[from_j..to_index-1],b,2,align,fill)
  725. #
  726. else
  727. if align=='J'
  728. @ws=(ns>1) ? (wmax-ls)/1000.0*@FontSize/(ns-1) : 0
  729. out(sprintf('%.3f Tw',@ws*@k))
  730. end
  731. self.Cell(w,h,s[from_j..sep],b,2,align,fill)
  732. to_index=sep+1
  733. end
  734. sep=-1
  735. from_j=to_index
  736. l=0
  737. ns=0
  738. nl=nl+1
  739. b=b2 if border and nl==2
  740. else
  741. to_index=to_index+1
  742. end
  743. end
  744. end
  745. # Last chunk
  746. if @ws>0
  747. @ws=0
  748. out('0 Tw')
  749. end
  750. b=b+'B' if border!=0 and not border.index('B').nil?
  751. self.Cell(w,h,s[from_j..to_index],b,2,align,fill)
  752. @x=@lMargin
  753. end
  754. def Write(h,txt,link='')
  755. # Output text in flowing mode
  756. cw=@CurrentFont['cw']
  757. w=@w-@rMargin-@x
  758. wmax=(w-2*@cMargin)*1000/@FontSize
  759. s=txt.gsub("\r",'')
  760. nb=s.length
  761. sep=-1
  762. i=0
  763. j=0
  764. l=0
  765. nl=1
  766. while i<nb
  767. # Get next character
  768. c=s[i]
  769. if c=="\n"[0]
  770. # Explicit line break
  771. self.Cell(w,h,s[j,i-j],0,2,'',0,link)
  772. i=i+1
  773. sep=-1
  774. j=i
  775. l=0
  776. if nl==1
  777. @x=@lMargin
  778. w=@w-@rMargin-@x
  779. wmax=(w-2*@cMargin)*1000/@FontSize
  780. end
  781. nl=nl+1
  782. next
  783. end
  784. if c==' '[0]
  785. sep=i
  786. ls=l
  787. end
  788. l=l+GetCharWidth(cw, c);
  789. if l>wmax
  790. # Automatic line break
  791. if sep==-1
  792. if @x>@lMargin
  793. # Move to next line
  794. @x=@lMargin
  795. @y=@y+h
  796. w=@w-@rMargin-@x
  797. wmax=(w-2*@cMargin)*1000/@FontSize
  798. i=i+1
  799. nl=nl+1
  800. next
  801. end
  802. i=i+1 if i==j
  803. self.Cell(w,h,s[j,i-j],0,2,'',0,link)
  804. else
  805. self.Cell(w,h,s[j,sep-j],0,2,'',0,link)
  806. i=sep+1
  807. end
  808. sep=-1
  809. j=i
  810. l=0
  811. if nl==1
  812. @x=@lMargin
  813. w=@w-@rMargin-@x
  814. wmax=(w-2*@cMargin)*1000/@FontSize
  815. end
  816. nl=nl+1
  817. else
  818. i=i+1
  819. end
  820. end
  821. # Last chunk
  822. self.Cell(l/1000.0*@FontSize,h,s[j,i],0,0,'',0,link) if i!=j
  823. end
  824. def Image(file,x,y,w=0,h=0,type='',link='')
  825. # Put an image on the page
  826. unless @images.has_key?(file)
  827. # First use of image, get info
  828. if type==''
  829. pos=file.rindex('.')
  830. if pos.nil?
  831. self.Error('Image file has no extension and no type was '+
  832. 'specified: '+file)
  833. end
  834. type=file[pos+1..-1]
  835. end
  836. type.downcase!
  837. if type=='jpg' or type=='jpeg'
  838. info=parsejpg(file)
  839. elsif type=='png'
  840. info=parsepng(file)
  841. else
  842. self.Error('Unsupported image file type: '+type)
  843. end
  844. info['i']=@images.length+1
  845. @images[file]=info
  846. else
  847. info=@images[file]
  848. end
  849. #Ed Moss
  850. if(w==0 && h==0)
  851. #Put image at 72 dpi
  852. w=info['w']/@k;
  853. h=info['h']/@k;
  854. end
  855. #
  856. # Automatic width or height calculation
  857. w=h*info['w']/info['h'] if w==0
  858. h=w*info['h']/info['w'] if h==0
  859. out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',
  860. w*@k,h*@k,x*@k,(@h-(y+h))*@k,info['i']))
  861. Link(x,y,w,h,link) if link and link != ''
  862. end
  863. def Ln(h='')
  864. # Line feed; default value is last cell height
  865. @x=@lMargin
  866. if h.kind_of?(String)
  867. @y=@y+@lasth
  868. else
  869. @y=@y+h
  870. end
  871. end
  872. def GetX
  873. # Get x position
  874. @x
  875. end
  876. def SetX(x)
  877. # Set x position
  878. if x>=0
  879. @x=x
  880. else
  881. @x=@w+x
  882. end
  883. end
  884. def GetY
  885. # Get y position
  886. @y
  887. end
  888. def SetY(y)
  889. # Set y position and reset x
  890. @x=@lMargin
  891. if y>=0
  892. @y=y
  893. else
  894. @y=@h+y
  895. end
  896. end
  897. def SetXY(x,y)
  898. # Set x and y positions
  899. SetY(y)
  900. SetX(x)
  901. end
  902. def Output(file=nil)
  903. # Output PDF to file or return as a string
  904. # Finish document if necessary
  905. self.Close if(@state<3)
  906. if file.nil?
  907. # Return as a string
  908. return @buffer
  909. else
  910. # Save file locally
  911. open(file,'wb') do |f|
  912. f.write(@buffer)
  913. end
  914. end
  915. end
  916. private
  917. def putpages
  918. nb=@page
  919. unless @AliasNbPages.nil? or @AliasNbPages==''
  920. # Replace number of pages
  921. 1.upto(nb) do |n|
  922. @pages[n].gsub!(@AliasNbPages,nb.to_s)
  923. end
  924. end
  925. if @DefOrientation=='P'
  926. wPt=@fwPt
  927. hPt=@fhPt
  928. else
  929. wPt=@fhPt
  930. hPt=@fwPt
  931. end
  932. filter=(@compress) ? '/Filter /FlateDecode ' : ''
  933. 1.upto(nb) do |n|
  934. # Page
  935. newobj
  936. out('<</Type /Page')
  937. out('/Parent 1 0 R')
  938. unless @OrientationChanges[n].nil?
  939. out(sprintf('/MediaBox [0 0 %.2f %.2f]',hPt,wPt))
  940. end
  941. out('/Resources 2 0 R')
  942. if @PageLinks[n]
  943. # Links
  944. annots='/Annots ['
  945. @PageLinks[n].each do |pl|
  946. rect=sprintf('%.2f %.2f %.2f %.2f',
  947. pl[0],pl[1],pl[0]+pl[2],pl[1]-pl[3])
  948. annots=annots+'<</Type /Annot /Subtype /Link /Rect ['+rect+
  949. '] /Border [0 0 0] '
  950. if pl[4].kind_of?(String)
  951. annots=annots+'/A <</S /URI /URI '+textstring(pl[4])+
  952. '>>>>'
  953. else
  954. l=@links[pl[4]]
  955. h=@OrientationChanges[l[0]].nil? ? hPt : wPt
  956. annots=annots+sprintf(
  957. '/Dest [%d 0 R /XYZ 0 %.2f null]>>',
  958. 1+2*l[0],h-l[1]*@k)
  959. end
  960. end
  961. out(annots+']')
  962. end
  963. out('/Contents '+(@n+1).to_s+' 0 R>>')
  964. out('endobj')
  965. # Page content
  966. p=(@compress) ? Zlib::Deflate.deflate(@pages[n]) : @pages[n]
  967. newobj
  968. out('<<'+filter+'/Length '+p.length.to_s+'>>')
  969. putstream(p)
  970. out('endobj')
  971. end
  972. # Pages root
  973. @offsets[1]=@buffer.length
  974. out('1 0 obj')
  975. out('<</Type /Pages')
  976. kids='/Kids ['
  977. nb.times do |i|
  978. kids=kids+(3+2*i).to_s+' 0 R '
  979. end
  980. out(kids+']')
  981. out('/Count '+nb.to_s)
  982. out(sprintf('/MediaBox [0 0 %.2f %.2f]',wPt,hPt))
  983. out('>>')
  984. out('endobj')
  985. end
  986. def putfonts
  987. nf=@n
  988. @diffs.each do |diff|
  989. # Encodings
  990. newobj
  991. out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences '+
  992. '['+diff+']>>')
  993. out('endobj')
  994. end
  995. @FontFiles.each do |file, info|
  996. # Font file embedding
  997. newobj
  998. @FontFiles[file]['n'] = @n
  999. if self.class.const_defined? 'FPDF_FONTPATH' then
  1000. if FPDF_FONTPATH[-1,1] == '/' then
  1001. file = FPDF_FONTPATH + file
  1002. else
  1003. file = FPDF_FONTPATH + '/' + file
  1004. end
  1005. end
  1006. size = File.size(file)
  1007. unless File.exists?(file)
  1008. Error('Font file not found')
  1009. end
  1010. out('<</Length ' + size.to_s)
  1011. if file[-2, 2] == '.z' then
  1012. out('/Filter /FlateDecode')
  1013. end
  1014. out('/Length1 ' + info['length1'])
  1015. out('/Length2 ' + info['length2'] + ' /Length3 0') if info['length2']
  1016. out('>>')
  1017. open(file, 'rb') do |f|
  1018. putstream(f.read())
  1019. end
  1020. out('endobj')
  1021. end
  1022. file = 0
  1023. @fonts.each do |k, font|
  1024. # Font objects
  1025. @fonts[k]['n']=@n+1
  1026. type=font['type']
  1027. name=font['name']
  1028. if type=='core'
  1029. # Standard font
  1030. newobj
  1031. out('<</Type /Font')
  1032. out('/BaseFont /'+name)
  1033. out('/Subtype /Type1')
  1034. if name!='Symbol' and name!='ZapfDingbats'
  1035. out('/Encoding /WinAnsiEncoding')
  1036. end
  1037. out('>>')
  1038. out('endobj')
  1039. elsif type=='Type1' or type=='TrueType'
  1040. # Additional Type1 or TrueType font
  1041. newobj
  1042. out('<</Type /Font')
  1043. out('/BaseFont /'+name)
  1044. out('/Subtype /'+type)
  1045. out('/FirstChar 32 /LastChar 255')
  1046. out('/Widths '+(@n+1).to_s+' 0 R')
  1047. out('/FontDescriptor '+(@n+2).to_s+' 0 R')
  1048. if font['enc'] and font['enc'] != ''
  1049. unless font['diff'].nil?
  1050. out('/Encoding '+(nf+font['diff']).to_s+' 0 R')
  1051. else
  1052. out('/Encoding /WinAnsiEncoding')
  1053. end
  1054. end
  1055. out('>>')
  1056. out('endobj')
  1057. # Widths
  1058. newobj
  1059. cw=font['cw']
  1060. s='['
  1061. 32.upto(255) do |i|
  1062. s << GetCharWidth(cw, i).to_s + ' '
  1063. end
  1064. out(s+']')
  1065. out('endobj')
  1066. # Descriptor
  1067. newobj
  1068. s='<</Type /FontDescriptor /FontName /'+name
  1069. font['desc'].each

Large files files are truncated, but you can click here to view the full file