Chris@1296: #============================================================+ Chris@1296: # File name : tcpdf.rb Chris@1296: # Begin : 2002-08-03 Chris@1296: # Last Update : 2007-03-20 Chris@1296: # Author : Nicola Asuni Chris@1296: # Version : 1.53.0.TC031 Chris@1296: # License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html) Chris@1296: # Chris@1296: # Description : This is a Ruby class for generating PDF files Chris@1296: # on-the-fly without requiring external Chris@1296: # extensions. Chris@1296: # Chris@1296: # IMPORTANT: Chris@1296: # This class is an extension and improvement of the Public Domain Chris@1296: # FPDF class by Olivier Plathey (http://www.fpdf.org). Chris@1296: # Chris@1296: # Main changes by Nicola Asuni: Chris@1296: # Ruby porting; Chris@1296: # UTF-8 Unicode support; Chris@1296: # code refactoring; Chris@1296: # source code clean up; Chris@1296: # code style and formatting; Chris@1296: # source code documentation using phpDocumentor (www.phpdoc.org); Chris@1296: # All ISO page formats were included; Chris@1296: # image scale factor; Chris@1296: # includes methods to parse and printsome XHTML code, supporting the following elements: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small; Chris@1296: # includes a method to print various barcode formats using an improved version of "Generic Barcode Render Class" by Karim Mribti (http://www.mribti.com/barcode/) (require GD library: http://www.boutell.com/gd/); Chris@1296: # defines standard Header() and Footer() methods. Chris@1296: # Chris@1296: # Ported to Ruby by Ed Moss 2007-08-06 Chris@1296: # Chris@1296: #============================================================+ Chris@1296: Chris@1296: require 'tempfile' Chris@1296: require 'core/rmagick' Chris@1296: Chris@1296: # Chris@1296: # TCPDF Class. Chris@1296: # @package com.tecnick.tcpdf Chris@1296: # Chris@1296: Chris@1296: @@version = "1.53.0.TC031" Chris@1296: @@fpdf_charwidths = {} Chris@1296: Chris@1296: PDF_PRODUCER = 'TCPDF via RFPDF 1.53.0.TC031 (http://tcpdf.sourceforge.net)' Chris@1296: Chris@1296: module TCPDFFontDescriptor Chris@1296: @@descriptors = { 'freesans' => {} } Chris@1296: @@font_name = 'freesans' Chris@1296: Chris@1296: def self.font(font_name) Chris@1296: @@descriptors[font_name.gsub(".rb", "")] Chris@1296: end Chris@1296: Chris@1296: def self.define(font_name = 'freesans') Chris@1296: @@descriptors[font_name] ||= {} Chris@1296: yield @@descriptors[font_name] Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # This is a Ruby class for generating PDF files on-the-fly without requiring external extensions.
Chris@1296: # This class is an extension and improvement of the FPDF class by Olivier Plathey (http://www.fpdf.org).
Chris@1296: # This version contains some changes: [porting to Ruby, support for UTF-8 Unicode, code style and formatting, php documentation (www.phpdoc.org), ISO page formats, minor improvements, image scale factor]
Chris@1296: # TCPDF project (http://tcpdf.sourceforge.net) is based on the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).
Chris@1296: # To add your own TTF fonts please read /fonts/README.TXT Chris@1296: # @name TCPDF Chris@1296: # @package com.tecnick.tcpdf Chris@1296: # @@version 1.53.0.TC031 Chris@1296: # @author Nicola Asuni Chris@1296: # @link http://tcpdf.sourceforge.net Chris@1296: # @license http://www.gnu.org/copyleft/lesser.html LGPL Chris@1296: # Chris@1296: class TCPDF Chris@1296: include RFPDF Chris@1296: include Core::RFPDF Chris@1296: include RFPDF::Math Chris@1296: Chris@1296: def logger Chris@1296: Rails.logger Chris@1296: end Chris@1296: Chris@1296: cattr_accessor :k_cell_height_ratio Chris@1296: @@k_cell_height_ratio = 1.25 Chris@1296: Chris@1296: cattr_accessor :k_blank_image Chris@1296: @@k_blank_image = "" Chris@1296: Chris@1296: cattr_accessor :k_small_ratio Chris@1296: @@k_small_ratio = 2/3.0 Chris@1296: Chris@1296: cattr_accessor :k_path_cache Chris@1296: @@k_path_cache = Rails.root.join('tmp') Chris@1296: Chris@1296: cattr_accessor :k_path_url_cache Chris@1296: @@k_path_url_cache = Rails.root.join('tmp') Chris@1296: Chris@1296: attr_accessor :barcode Chris@1296: Chris@1296: attr_accessor :buffer Chris@1296: Chris@1296: attr_accessor :diffs Chris@1296: Chris@1296: attr_accessor :color_flag Chris@1296: Chris@1296: attr_accessor :default_table_columns Chris@1296: Chris@1296: attr_accessor :max_table_columns Chris@1296: Chris@1296: attr_accessor :default_font Chris@1296: Chris@1296: attr_accessor :draw_color Chris@1296: Chris@1296: attr_accessor :encoding Chris@1296: Chris@1296: attr_accessor :fill_color Chris@1296: Chris@1296: attr_accessor :fonts Chris@1296: Chris@1296: attr_accessor :font_family Chris@1296: Chris@1296: attr_accessor :font_files Chris@1296: Chris@1296: cattr_accessor :font_path Chris@1296: Chris@1296: attr_accessor :font_style Chris@1296: Chris@1296: attr_accessor :font_size_pt Chris@1296: Chris@1296: attr_accessor :header_width Chris@1296: Chris@1296: attr_accessor :header_logo Chris@1296: Chris@1296: attr_accessor :header_logo_width Chris@1296: Chris@1296: attr_accessor :header_title Chris@1296: Chris@1296: attr_accessor :header_string Chris@1296: Chris@1296: attr_accessor :images Chris@1296: Chris@1296: attr_accessor :img_scale Chris@1296: Chris@1296: attr_accessor :in_footer Chris@1296: Chris@1296: attr_accessor :is_unicode Chris@1296: Chris@1296: attr_accessor :lasth Chris@1296: Chris@1296: attr_accessor :links Chris@1296: Chris@1296: attr_accessor :list_ordered Chris@1296: Chris@1296: attr_accessor :list_count Chris@1296: Chris@1296: attr_accessor :li_spacer Chris@1296: Chris@1296: attr_accessor :n Chris@1296: Chris@1296: attr_accessor :offsets Chris@1296: Chris@1296: attr_accessor :orientation_changes Chris@1296: Chris@1296: attr_accessor :page Chris@1296: Chris@1296: attr_accessor :page_links Chris@1296: Chris@1296: attr_accessor :pages Chris@1296: Chris@1296: attr_accessor :pdf_version Chris@1296: Chris@1296: attr_accessor :prevfill_color Chris@1296: Chris@1296: attr_accessor :prevtext_color Chris@1296: Chris@1296: attr_accessor :print_header Chris@1296: Chris@1296: attr_accessor :print_footer Chris@1296: Chris@1296: attr_accessor :state Chris@1296: Chris@1296: attr_accessor :tableborder Chris@1296: Chris@1296: attr_accessor :tdbegin Chris@1296: Chris@1296: attr_accessor :tdwidth Chris@1296: Chris@1296: attr_accessor :tdheight Chris@1296: Chris@1296: attr_accessor :tdalign Chris@1296: Chris@1296: attr_accessor :tdfill Chris@1296: Chris@1296: attr_accessor :tempfontsize Chris@1296: Chris@1296: attr_accessor :text_color Chris@1296: Chris@1296: attr_accessor :underline Chris@1296: Chris@1296: attr_accessor :ws Chris@1296: Chris@1296: # Chris@1296: # This is the class constructor. Chris@1296: # It allows to set up the page format, the orientation and Chris@1296: # the measure unit used in all the methods (except for the font sizes). Chris@1296: # @since 1.0 Chris@1296: # @param string :orientation page orientation. Possible values are (case insensitive): Chris@1296: # @param string :unit User measure unit. Possible values are:
A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. Chris@1296: # @param mixed :format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit). Chris@1296: # @param boolean :unicode TRUE means that the input text is unicode (default = true) Chris@1296: # @param String :encoding charset encoding; default is UTF-8 Chris@1296: # Chris@1296: def initialize(orientation = 'P', unit = 'mm', format = 'A4', unicode = true, encoding = "UTF-8") Chris@1296: Chris@1296: # Set internal character encoding to ASCII# Chris@1296: #FIXME 2007-05-25 (EJM) Level=0 - Chris@1296: # if (respond_to?("mb_internal_encoding") and mb_internal_encoding()) Chris@1296: # @internal_encoding = mb_internal_encoding(); Chris@1296: # mb_internal_encoding("ASCII"); Chris@1296: # } Chris@1296: Chris@1296: #Some checks Chris@1296: dochecks(); Chris@1296: Chris@1296: #Initialization of properties Chris@1296: @barcode ||= false Chris@1296: @buffer ||= '' Chris@1296: @diffs ||= [] Chris@1296: @color_flag ||= false Chris@1296: @default_table_columns ||= 4 Chris@1296: @table_columns ||= 0 Chris@1296: @max_table_columns ||= [] Chris@1296: @tr_id ||= 0 Chris@1296: @max_td_page ||= [] Chris@1296: @max_td_y ||= [] Chris@1296: @t_columns ||= 0 Chris@1296: @default_font ||= "FreeSans" if unicode Chris@1296: @default_font ||= "Helvetica" Chris@1296: @draw_color ||= '0 G' Chris@1296: @encoding ||= "UTF-8" Chris@1296: @fill_color ||= '0 g' Chris@1296: @fonts ||= {} Chris@1296: @font_family ||= '' Chris@1296: @font_files ||= {} Chris@1296: @font_style ||= '' Chris@1296: @font_size ||= 12 Chris@1296: @font_size_pt ||= 12 Chris@1296: @header_width ||= 0 Chris@1296: @header_logo ||= "" Chris@1296: @header_logo_width ||= 30 Chris@1296: @header_title ||= "" Chris@1296: @header_string ||= "" Chris@1296: @images ||= {} Chris@1296: @img_scale ||= 1 Chris@1296: @in_footer ||= false Chris@1296: @is_unicode = unicode Chris@1296: @lasth ||= 0 Chris@1296: @links ||= [] Chris@1296: @list_ordered ||= [] Chris@1296: @list_count ||= [] Chris@1296: @li_spacer ||= "" Chris@1296: @li_count ||= 0 Chris@1296: @spacer ||= "" Chris@1296: @quote_count ||= 0 Chris@1296: @prevquote_count ||= 0 Chris@1296: @quote_top ||= [] Chris@1296: @quote_page ||= [] Chris@1296: @n ||= 2 Chris@1296: @offsets ||= [] Chris@1296: @orientation_changes ||= [] Chris@1296: @page ||= 0 Chris@1296: @page_links ||= {} Chris@1296: @pages ||= [] Chris@1296: @pdf_version ||= "1.3" Chris@1296: @prevfill_color ||= [255,255,255] Chris@1296: @prevtext_color ||= [0,0,0] Chris@1296: @print_header ||= false Chris@1296: @print_footer ||= false Chris@1296: @state ||= 0 Chris@1296: @tableborder ||= 0 Chris@1296: @tdbegin ||= false Chris@1296: @tdtext ||= '' Chris@1296: @tdwidth ||= 0 Chris@1296: @tdheight ||= 0 Chris@1296: @tdalign ||= "L" Chris@1296: @tdfill ||= 0 Chris@1296: @tempfontsize ||= 10 Chris@1296: @text_color ||= '0 g' Chris@1296: @underline ||= false Chris@1296: @deleted ||= false Chris@1296: @ws ||= 0 Chris@1296: Chris@1296: #Standard Unicode fonts Chris@1296: @core_fonts = { Chris@1296: 'courier'=>'Courier', Chris@1296: 'courierB'=>'Courier-Bold', Chris@1296: 'courierI'=>'Courier-Oblique', Chris@1296: 'courierBI'=>'Courier-BoldOblique', Chris@1296: 'helvetica'=>'Helvetica', Chris@1296: 'helveticaB'=>'Helvetica-Bold', Chris@1296: 'helveticaI'=>'Helvetica-Oblique', Chris@1296: 'helveticaBI'=>'Helvetica-BoldOblique', Chris@1296: 'times'=>'Times-Roman', Chris@1296: 'timesB'=>'Times-Bold', Chris@1296: 'timesI'=>'Times-Italic', Chris@1296: 'timesBI'=>'Times-BoldItalic', Chris@1296: 'symbol'=>'Symbol', Chris@1296: 'zapfdingbats'=>'ZapfDingbats'} Chris@1296: Chris@1296: #Scale factor Chris@1296: case unit.downcase Chris@1296: when 'pt' ; @k=1 Chris@1296: when 'mm' ; @k=72/25.4 Chris@1296: when 'cm' ; @k=72/2.54 Chris@1296: when 'in' ; @k=72 Chris@1296: else Error("Incorrect unit: #{unit}") Chris@1296: end Chris@1296: Chris@1296: #Page format Chris@1296: if format.is_a?(String) Chris@1296: # Page formats (45 standard ISO paper formats and 4 american common formats). Chris@1296: # Paper cordinates are calculated in this way: (inches# 72) where (1 inch = 2.54 cm) Chris@1296: case (format.upcase) Chris@1296: when '4A0' ; format = [4767.87,6740.79] Chris@1296: when '2A0' ; format = [3370.39,4767.87] Chris@1296: when 'A0' ; format = [2383.94,3370.39] Chris@1296: when 'A1' ; format = [1683.78,2383.94] Chris@1296: when 'A2' ; format = [1190.55,1683.78] Chris@1296: when 'A3' ; format = [841.89,1190.55] Chris@1296: when 'A4' ; format = [595.28,841.89] # ; default Chris@1296: when 'A5' ; format = [419.53,595.28] Chris@1296: when 'A6' ; format = [297.64,419.53] Chris@1296: when 'A7' ; format = [209.76,297.64] Chris@1296: when 'A8' ; format = [147.40,209.76] Chris@1296: when 'A9' ; format = [104.88,147.40] Chris@1296: when 'A10' ; format = [73.70,104.88] Chris@1296: when 'B0' ; format = [2834.65,4008.19] Chris@1296: when 'B1' ; format = [2004.09,2834.65] Chris@1296: when 'B2' ; format = [1417.32,2004.09] Chris@1296: when 'B3' ; format = [1000.63,1417.32] Chris@1296: when 'B4' ; format = [708.66,1000.63] Chris@1296: when 'B5' ; format = [498.90,708.66] Chris@1296: when 'B6' ; format = [354.33,498.90] Chris@1296: when 'B7' ; format = [249.45,354.33] Chris@1296: when 'B8' ; format = [175.75,249.45] Chris@1296: when 'B9' ; format = [124.72,175.75] Chris@1296: when 'B10' ; format = [87.87,124.72] Chris@1296: when 'C0' ; format = [2599.37,3676.54] Chris@1296: when 'C1' ; format = [1836.85,2599.37] Chris@1296: when 'C2' ; format = [1298.27,1836.85] Chris@1296: when 'C3' ; format = [918.43,1298.27] Chris@1296: when 'C4' ; format = [649.13,918.43] Chris@1296: when 'C5' ; format = [459.21,649.13] Chris@1296: when 'C6' ; format = [323.15,459.21] Chris@1296: when 'C7' ; format = [229.61,323.15] Chris@1296: when 'C8' ; format = [161.57,229.61] Chris@1296: when 'C9' ; format = [113.39,161.57] Chris@1296: when 'C10' ; format = [79.37,113.39] Chris@1296: when 'RA0' ; format = [2437.80,3458.27] Chris@1296: when 'RA1' ; format = [1729.13,2437.80] Chris@1296: when 'RA2' ; format = [1218.90,1729.13] Chris@1296: when 'RA3' ; format = [864.57,1218.90] Chris@1296: when 'RA4' ; format = [609.45,864.57] Chris@1296: when 'SRA0' ; format = [2551.18,3628.35] Chris@1296: when 'SRA1' ; format = [1814.17,2551.18] Chris@1296: when 'SRA2' ; format = [1275.59,1814.17] Chris@1296: when 'SRA3' ; format = [907.09,1275.59] Chris@1296: when 'SRA4' ; format = [637.80,907.09] Chris@1296: when 'LETTER' ; format = [612.00,792.00] Chris@1296: when 'LEGAL' ; format = [612.00,1008.00] Chris@1296: when 'EXECUTIVE' ; format = [521.86,756.00] Chris@1296: when 'FOLIO' ; format = [612.00,936.00] Chris@1296: #else then Error("Unknown page format: #{format}" Chris@1296: end Chris@1296: @fw_pt = format[0] Chris@1296: @fh_pt = format[1] Chris@1296: else Chris@1296: @fw_pt = format[0]*@k Chris@1296: @fh_pt = format[1]*@k Chris@1296: end Chris@1296: Chris@1296: @fw = @fw_pt/@k Chris@1296: @fh = @fh_pt/@k Chris@1296: Chris@1296: #Page orientation Chris@1296: orientation = orientation.downcase Chris@1296: if orientation == 'p' or orientation == 'portrait' Chris@1296: @def_orientation = 'P' Chris@1296: @w_pt = @fw_pt Chris@1296: @h_pt = @fh_pt Chris@1296: elsif orientation == 'l' or orientation == 'landscape' Chris@1296: @def_orientation = 'L' Chris@1296: @w_pt = @fh_pt Chris@1296: @h_pt = @fw_pt Chris@1296: else Chris@1296: Error("Incorrect orientation: #{orientation}") Chris@1296: end Chris@1296: Chris@1296: @fw = @w_pt/@k Chris@1296: @fh = @h_pt/@k Chris@1296: Chris@1296: @cur_orientation = @def_orientation Chris@1296: @w = @w_pt/@k Chris@1296: @h = @h_pt/@k Chris@1296: #Page margins (1 cm) Chris@1296: margin = 28.35/@k Chris@1296: SetMargins(margin, margin) Chris@1296: #Interior cell margin (1 mm) Chris@1296: @c_margin = margin / 10 Chris@1296: #Line width (0.2 mm) Chris@1296: @line_width = 0.567 / @k Chris@1296: #Automatic page break Chris@1296: SetAutoPageBreak(true, 2 * margin) Chris@1296: #Full width display mode Chris@1296: SetDisplayMode('fullwidth') Chris@1296: #Compression Chris@1296: SetCompression(true) Chris@1296: #Set default PDF version number Chris@1296: @pdf_version = "1.3" Chris@1296: Chris@1296: @encoding = encoding Chris@1296: @b = 0 Chris@1296: @i = 0 Chris@1296: @u = 0 Chris@1296: @href = '' Chris@1296: @fontlist = ["arial", "times", "courier", "helvetica", "symbol"] Chris@1296: @issetfont = false Chris@1296: @issetcolor = false Chris@1296: Chris@1296: SetFillColor(200, 200, 200, true) Chris@1296: SetTextColor(0, 0, 0, true) Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Set the image scale. Chris@1296: # @param float :scale image scale. Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.5.2 Chris@1296: # Chris@1296: def SetImageScale(scale) Chris@1296: @img_scale = scale; Chris@1296: end Chris@1296: alias_method :set_image_scale, :SetImageScale Chris@1296: Chris@1296: # Chris@1296: # Returns the image scale. Chris@1296: # @return float image scale. Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.5.2 Chris@1296: # Chris@1296: def GetImageScale() Chris@1296: return @img_scale; Chris@1296: end Chris@1296: alias_method :get_image_scale, :GetImageScale Chris@1296: Chris@1296: # Chris@1296: # Returns the page width in units. Chris@1296: # @return int page width. Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.5.2 Chris@1296: # Chris@1296: def GetPageWidth() Chris@1296: return @w; Chris@1296: end Chris@1296: alias_method :get_page_width, :GetPageWidth Chris@1296: Chris@1296: # Chris@1296: # Returns the page height in units. Chris@1296: # @return int page height. Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.5.2 Chris@1296: # Chris@1296: def GetPageHeight() Chris@1296: return @h; Chris@1296: end Chris@1296: alias_method :get_page_height, :GetPageHeight Chris@1296: Chris@1296: # Chris@1296: # Returns the page break margin. Chris@1296: # @return int page break margin. Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.5.2 Chris@1296: # Chris@1296: def GetBreakMargin() Chris@1296: return @b_margin; Chris@1296: end Chris@1296: alias_method :get_break_margin, :GetBreakMargin Chris@1296: Chris@1296: # Chris@1296: # Returns the scale factor (number of points in user unit). Chris@1296: # @return int scale factor. Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.5.2 Chris@1296: # Chris@1296: def GetScaleFactor() Chris@1296: return @k; Chris@1296: end Chris@1296: alias_method :get_scale_factor, :GetScaleFactor Chris@1296: Chris@1296: # Chris@1296: # Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them. Chris@1296: # @param float :left Left margin. Chris@1296: # @param float :top Top margin. Chris@1296: # @param float :right Right margin. Default value is the left one. Chris@1296: # @since 1.0 Chris@1296: # @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() Chris@1296: # Chris@1296: def SetMargins(left, top, right=-1) Chris@1296: #Set left, top and right margins Chris@1296: @l_margin = left Chris@1296: @t_margin = top Chris@1296: if (right == -1) Chris@1296: right = left Chris@1296: end Chris@1296: @r_margin = right Chris@1296: end Chris@1296: alias_method :set_margins, :SetMargins Chris@1296: Chris@1296: # Chris@1296: # Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. Chris@1296: # @param float :margin The margin. Chris@1296: # @since 1.4 Chris@1296: # @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() Chris@1296: # Chris@1296: def SetLeftMargin(margin) Chris@1296: #Set left margin Chris@1296: @l_margin = margin Chris@1296: if ((@page>0) and (@x < margin)) Chris@1296: @x = margin Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_left_margin, :SetLeftMargin Chris@1296: Chris@1296: # Chris@1296: # Defines the top margin. The method can be called before creating the first page. Chris@1296: # @param float :margin The margin. Chris@1296: # @since 1.5 Chris@1296: # @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() Chris@1296: # Chris@1296: def SetTopMargin(margin) Chris@1296: #Set top margin Chris@1296: @t_margin = margin Chris@1296: end Chris@1296: alias_method :set_top_margin, :SetTopMargin Chris@1296: Chris@1296: # Chris@1296: # Defines the right margin. The method can be called before creating the first page. Chris@1296: # @param float :margin The margin. Chris@1296: # @since 1.5 Chris@1296: # @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() Chris@1296: # Chris@1296: def SetRightMargin(margin) Chris@1296: #Set right margin Chris@1296: @r_margin = margin Chris@1296: end Chris@1296: alias_method :set_right_margin, :SetRightMargin Chris@1296: Chris@1296: # Chris@1296: # Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. Chris@1296: # @param boolean :auto Boolean indicating if mode should be on or off. Chris@1296: # @param float :margin Distance from the bottom of the page. Chris@1296: # @since 1.0 Chris@1296: # @see Cell(), MultiCell(), AcceptPageBreak() Chris@1296: # Chris@1296: def SetAutoPageBreak(auto, margin=0) Chris@1296: #Set auto page break mode and triggering margin Chris@1296: @auto_page_break = auto Chris@1296: @b_margin = margin Chris@1296: @page_break_trigger = @h - margin Chris@1296: end Chris@1296: alias_method :set_auto_page_break, :SetAutoPageBreak Chris@1296: Chris@1296: # Chris@1296: # Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display. Chris@1296: # @param mixed :zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. Chris@1296: # @param string :layout The page layout. Possible values are: Chris@1296: # @since 1.2 Chris@1296: # Chris@1296: def SetDisplayMode(zoom, layout = 'continuous') Chris@1296: #Set display mode in viewer Chris@1296: if (zoom == 'fullpage' or zoom == 'fullwidth' or zoom == 'real' or zoom == 'default' or !zoom.is_a?(String)) Chris@1296: @zoom_mode = zoom Chris@1296: else Chris@1296: Error("Incorrect zoom display mode: #{zoom}") Chris@1296: end Chris@1296: if (layout == 'single' or layout == 'continuous' or layout == 'two' or layout == 'default') Chris@1296: @layout_mode = layout Chris@1296: else Chris@1296: Error("Incorrect layout display mode: #{layout}") Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_display_mode, :SetDisplayMode Chris@1296: Chris@1296: # Chris@1296: # Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. Chris@1296: # Note: the Zlib extension is required for this feature. If not present, compression will be turned off. Chris@1296: # @param boolean :compress Boolean indicating if compression must be enabled. Chris@1296: # @since 1.4 Chris@1296: # Chris@1296: def SetCompression(compress) Chris@1296: #Set page compression Chris@1296: if (respond_to?('gzcompress')) Chris@1296: @compress = compress Chris@1296: else Chris@1296: @compress = false Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_compression, :SetCompression Chris@1296: Chris@1296: # Chris@1296: # Defines the title of the document. Chris@1296: # @param string :title The title. Chris@1296: # @since 1.2 Chris@1296: # @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() Chris@1296: # Chris@1296: def SetTitle(title) Chris@1296: #Title of document Chris@1296: @title = title Chris@1296: end Chris@1296: alias_method :set_title, :SetTitle Chris@1296: Chris@1296: # Chris@1296: # Defines the subject of the document. Chris@1296: # @param string :subject The subject. Chris@1296: # @since 1.2 Chris@1296: # @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() Chris@1296: # Chris@1296: def SetSubject(subject) Chris@1296: #Subject of document Chris@1296: @subject = subject Chris@1296: end Chris@1296: alias_method :set_subject, :SetSubject Chris@1296: Chris@1296: # Chris@1296: # Defines the author of the document. Chris@1296: # @param string :author The name of the author. Chris@1296: # @since 1.2 Chris@1296: # @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() Chris@1296: # Chris@1296: def SetAuthor(author) Chris@1296: #Author of document Chris@1296: @author = author Chris@1296: end Chris@1296: alias_method :set_author, :SetAuthor Chris@1296: Chris@1296: # Chris@1296: # Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. Chris@1296: # @param string :keywords The list of keywords. Chris@1296: # @since 1.2 Chris@1296: # @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() Chris@1296: # Chris@1296: def SetKeywords(keywords) Chris@1296: #Keywords of document Chris@1296: @keywords = keywords Chris@1296: end Chris@1296: alias_method :set_keywords, :SetKeywords Chris@1296: Chris@1296: # Chris@1296: # Defines the creator of the document. This is typically the name of the application that generates the PDF. Chris@1296: # @param string :creator The name of the creator. Chris@1296: # @since 1.2 Chris@1296: # @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() Chris@1296: # Chris@1296: def SetCreator(creator) Chris@1296: #Creator of document Chris@1296: @creator = creator Chris@1296: end Chris@1296: alias_method :set_creator, :SetCreator Chris@1296: Chris@1296: # Chris@1296: # Defines an alias for the total number of pages. It will be substituted as the document is closed.
Chris@1296: # Example:
Chris@1296: #
Chris@1296: 	# class PDF extends TCPDF {
Chris@1296: 	# 	def Footer()
Chris@1296: 	# 		#Go to 1.5 cm from bottom
Chris@1296: 	# 		SetY(-15);
Chris@1296: 	# 		#Select Arial italic 8
Chris@1296: 	# 		SetFont('Arial','I',8);
Chris@1296: 	# 		#Print current and total page numbers
Chris@1296: 	# 		Cell(0,10,'Page '.PageNo().'/{nb}',0,0,'C');
Chris@1296: 	# 	end
Chris@1296: 	# }
Chris@1296: 	# :pdf=new PDF();
Chris@1296: 	# :pdf->alias_nb_pages();
Chris@1296: 	# 
Chris@1296: # @param string :alias The alias. Default valuenb}. Chris@1296: # @since 1.4 Chris@1296: # @see PageNo(), Footer() Chris@1296: # Chris@1296: def AliasNbPages(alias_nb ='{nb}') Chris@1296: #Define an alias for total number of pages Chris@1296: @alias_nb_pages = escapetext(alias_nb) Chris@1296: end Chris@1296: alias_method :alias_nb_pages, :AliasNbPages Chris@1296: Chris@1296: # Chris@1296: # This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. Chris@1296: # 2004-06-11 :: Nicola Asuni : changed bold tag with strong Chris@1296: # @param string :msg The error message Chris@1296: # @since 1.0 Chris@1296: # Chris@1296: def Error(msg) Chris@1296: #Fatal error Chris@1296: raise ("TCPDF error: #{msg}") Chris@1296: end Chris@1296: alias_method :error, :Error Chris@1296: Chris@1296: # Chris@1296: # This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically. Chris@1296: # Note: no page is created by this method Chris@1296: # @since 1.0 Chris@1296: # @see AddPage(), Close() Chris@1296: # Chris@1296: def Open() Chris@1296: #Begin document Chris@1296: @state = 1 Chris@1296: end Chris@1296: # alias_method :open, :Open Chris@1296: Chris@1296: # Chris@1296: # Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document. Chris@1296: # @since 1.0 Chris@1296: # @see Open(), Output() Chris@1296: # Chris@1296: def Close() Chris@1296: #Terminate document Chris@1296: if (@state==3) Chris@1296: return; Chris@1296: end Chris@1296: if (@page==0) Chris@1296: AddPage(); Chris@1296: end Chris@1296: #Page footer Chris@1296: @in_footer=true; Chris@1296: Footer(); Chris@1296: @in_footer=false; Chris@1296: #Close page Chris@1296: endpage(); Chris@1296: #Close document Chris@1296: enddoc(); Chris@1296: end Chris@1296: # alias_method :close, :Close Chris@1296: Chris@1296: # Chris@1296: # Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header. Chris@1296: # The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width. Chris@1296: # The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. Chris@1296: # @param string :orientation Page orientation. Possible values are (case insensitive): The default value is the one passed to the constructor. Chris@1296: # @since 1.0 Chris@1296: # @see TCPDF(), Header(), Footer(), SetMargins() Chris@1296: # Chris@1296: def AddPage(orientation='') Chris@1296: #Start a new page Chris@1296: if (@state==0) Chris@1296: Open(); Chris@1296: end Chris@1296: family=@font_family; Chris@1296: style=@font_style + (@underline ? 'U' : '') + (@deleted ? 'D' : ''); Chris@1296: size=@font_size_pt; Chris@1296: lw=@line_width; Chris@1296: dc=@draw_color; Chris@1296: fc=@fill_color; Chris@1296: tc=@text_color; Chris@1296: cf=@color_flag; Chris@1296: if (@page>0) Chris@1296: #Page footer Chris@1296: @in_footer=true; Chris@1296: Footer(); Chris@1296: @in_footer=false; Chris@1296: #Close page Chris@1296: endpage(); Chris@1296: end Chris@1296: #Start new page Chris@1296: beginpage(orientation); Chris@1296: #Set line cap style to square Chris@1296: out('2 J'); Chris@1296: #Set line width Chris@1296: @line_width = lw; Chris@1296: out(sprintf('%.2f w', lw*@k)); Chris@1296: #Set font Chris@1296: if (family) Chris@1296: SetFont(family, style, size); Chris@1296: end Chris@1296: #Set colors Chris@1296: @draw_color = dc; Chris@1296: if (dc!='0 G') Chris@1296: out(dc); Chris@1296: end Chris@1296: @fill_color = fc; Chris@1296: if (fc!='0 g') Chris@1296: out(fc); Chris@1296: end Chris@1296: @text_color = tc; Chris@1296: @color_flag = cf; Chris@1296: #Page header Chris@1296: Header(); Chris@1296: #Restore line width Chris@1296: if (@line_width != lw) Chris@1296: @line_width = lw; Chris@1296: out(sprintf('%.2f w', lw*@k)); Chris@1296: end Chris@1296: #Restore font Chris@1296: if (family) Chris@1296: SetFont(family, style, size); Chris@1296: end Chris@1296: #Restore colors Chris@1296: if (@draw_color != dc) Chris@1296: @draw_color = dc; Chris@1296: out(dc); Chris@1296: end Chris@1296: if (@fill_color != fc) Chris@1296: @fill_color = fc; Chris@1296: out(fc); Chris@1296: end Chris@1296: @text_color = tc; Chris@1296: @color_flag = cf; Chris@1296: end Chris@1296: alias_method :add_page, :AddPage Chris@1296: Chris@1296: # Chris@1296: # Rotate object. Chris@1296: # @param float :angle angle in degrees for counter-clockwise rotation Chris@1296: # @param int :x abscissa of the rotation center. Default is current x position Chris@1296: # @param int :y ordinate of the rotation center. Default is current y position Chris@1296: # Chris@1296: def Rotate(angle, x="", y="") Chris@1296: Chris@1296: if (x == '') Chris@1296: x = @x; Chris@1296: end Chris@1296: Chris@1296: if (y == '') Chris@1296: y = @y; Chris@1296: end Chris@1296: Chris@1296: if (@rtl) Chris@1296: x = @w - x; Chris@1296: angle = -@angle; Chris@1296: end Chris@1296: Chris@1296: y = (@h - y) * @k; Chris@1296: x *= @k; Chris@1296: Chris@1296: # calculate elements of transformation matrix Chris@1296: tm = [] Chris@1296: tm[0] = ::Math::cos(deg2rad(angle)); Chris@1296: tm[1] = ::Math::sin(deg2rad(angle)); Chris@1296: tm[2] = -tm[1]; Chris@1296: tm[3] = tm[0]; Chris@1296: tm[4] = x + tm[1] * y - tm[0] * x; Chris@1296: tm[5] = y - tm[0] * y - tm[1] * x; Chris@1296: Chris@1296: # generate the transformation matrix Chris@1296: Transform(tm); Chris@1296: end Chris@1296: alias_method :rotate, :Rotate Chris@1296: Chris@1296: # Chris@1296: # Starts a 2D tranformation saving current graphic state. Chris@1296: # This function must be called before scaling, mirroring, translation, rotation and skewing. Chris@1296: # Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. Chris@1296: # Chris@1296: def StartTransform Chris@1296: out('q'); Chris@1296: end Chris@1296: alias_method :start_transform, :StartTransform Chris@1296: Chris@1296: # Chris@1296: # Stops a 2D tranformation restoring previous graphic state. Chris@1296: # This function must be called after scaling, mirroring, translation, rotation and skewing. Chris@1296: # Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. Chris@1296: # Chris@1296: def StopTransform Chris@1296: out('Q'); Chris@1296: end Chris@1296: alias_method :stop_transform, :StopTransform Chris@1296: Chris@1296: # Chris@1296: # Apply graphic transformations. Chris@1296: # @since 2.1.000 (2008-01-07) Chris@1296: # @see StartTransform(), StopTransform() Chris@1296: # Chris@1296: def Transform(tm) Chris@1296: x = out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', tm[0], tm[1], tm[2], tm[3], tm[4], tm[5])); Chris@1296: end Chris@1296: alias_method :transform, :Transform Chris@1296: Chris@1296: # Chris@1296: # Set header data. Chris@1296: # @param string :ln header image logo Chris@1296: # @param string :lw header image logo width in mm Chris@1296: # @param string :ht string to print as title on document header Chris@1296: # @param string :hs string to print on document header Chris@1296: # Chris@1296: def SetHeaderData(ln="", lw=0, ht="", hs="") Chris@1296: @header_logo = ln || "" Chris@1296: @header_logo_width = lw || 0 Chris@1296: @header_title = ht || "" Chris@1296: @header_string = hs || "" Chris@1296: end Chris@1296: alias_method :set_header_data, :SetHeaderData Chris@1296: Chris@1296: # Chris@1296: # Set header margin. Chris@1296: # (minimum distance between header and top page margin) Chris@1296: # @param int :hm distance in millimeters Chris@1296: # Chris@1296: def SetHeaderMargin(hm=10) Chris@1296: @header_margin = hm; Chris@1296: end Chris@1296: alias_method :set_header_margin, :SetHeaderMargin Chris@1296: Chris@1296: # Chris@1296: # Set footer margin. Chris@1296: # (minimum distance between footer and bottom page margin) Chris@1296: # @param int :fm distance in millimeters Chris@1296: # Chris@1296: def SetFooterMargin(fm=10) Chris@1296: @footer_margin = fm; Chris@1296: end Chris@1296: alias_method :set_footer_margin, :SetFooterMargin Chris@1296: Chris@1296: # Chris@1296: # Set a flag to print page header. Chris@1296: # @param boolean :val set to true to print the page header (default), false otherwise. Chris@1296: # Chris@1296: def SetPrintHeader(val=true) Chris@1296: @print_header = val; Chris@1296: end Chris@1296: alias_method :set_print_header, :SetPrintHeader Chris@1296: Chris@1296: # Chris@1296: # Set a flag to print page footer. Chris@1296: # @param boolean :value set to true to print the page footer (default), false otherwise. Chris@1296: # Chris@1296: def SetPrintFooter(val=true) Chris@1296: @print_footer = val; Chris@1296: end Chris@1296: alias_method :set_print_footer, :SetPrintFooter Chris@1296: Chris@1296: # Chris@1296: # This method is used to render the page header. Chris@1296: # It is automatically called by AddPage() and could be overwritten in your own inherited class. Chris@1296: # Chris@1296: def Header() Chris@1296: if (@print_header) Chris@1296: if (@original_l_margin.nil?) Chris@1296: @original_l_margin = @l_margin; Chris@1296: end Chris@1296: if (@original_r_margin.nil?) Chris@1296: @original_r_margin = @r_margin; Chris@1296: end Chris@1296: Chris@1296: #set current position Chris@1296: SetXY(@original_l_margin, @header_margin); Chris@1296: Chris@1296: if ((@header_logo) and (@header_logo != @@k_blank_image)) Chris@1296: Image(@header_logo, @original_l_margin, @header_margin, @header_logo_width); Chris@1296: else Chris@1296: @img_rb_y = GetY(); Chris@1296: end Chris@1296: Chris@1296: cell_height = ((@@k_cell_height_ratio * @header_font[2]) / @k).round(2) Chris@1296: Chris@1296: header_x = @original_l_margin + (@header_logo_width * 1.05); #set left margin for text data cell Chris@1296: Chris@1296: # header title Chris@1296: SetFont(@header_font[0], 'B', @header_font[2] + 1); Chris@1296: SetX(header_x); Chris@1296: Cell(@header_width, cell_height, @header_title, 0, 1, 'L'); Chris@1296: Chris@1296: # header string Chris@1296: SetFont(@header_font[0], @header_font[1], @header_font[2]); Chris@1296: SetX(header_x); Chris@1296: MultiCell(@header_width, cell_height, @header_string, 0, 'L', 0); Chris@1296: Chris@1296: # print an ending header line Chris@1296: if (@header_width) Chris@1296: #set style for cell border Chris@1296: SetLineWidth(0.3); Chris@1296: SetDrawColor(0, 0, 0); Chris@1296: SetY(1 + (@img_rb_y > GetY() ? @img_rb_y : GetY())); Chris@1296: SetX(@original_l_margin); Chris@1296: Cell(0, 0, '', 'T', 0, 'C'); Chris@1296: end Chris@1296: Chris@1296: #restore position Chris@1296: SetXY(@original_l_margin, @t_margin); Chris@1296: end Chris@1296: end Chris@1296: alias_method :header, :Header Chris@1296: Chris@1296: # Chris@1296: # This method is used to render the page footer. Chris@1296: # It is automatically called by AddPage() and could be overwritten in your own inherited class. Chris@1296: # Chris@1296: def Footer() Chris@1296: if (@print_footer) Chris@1296: Chris@1296: if (@original_l_margin.nil?) Chris@1296: @original_l_margin = @l_margin; Chris@1296: end Chris@1296: if (@original_r_margin.nil?) Chris@1296: @original_r_margin = @r_margin; Chris@1296: end Chris@1296: Chris@1296: #set font Chris@1296: SetFont(@footer_font[0], @footer_font[1] , @footer_font[2]); Chris@1296: #set style for cell border Chris@1296: line_width = 0.3; Chris@1296: SetLineWidth(line_width); Chris@1296: SetDrawColor(0, 0, 0); Chris@1296: Chris@1296: footer_height = ((@@k_cell_height_ratio * @footer_font[2]) / @k).round; #footer height, was , 2) Chris@1296: #get footer y position Chris@1296: footer_y = @h - @footer_margin - footer_height; Chris@1296: #set current position Chris@1296: SetXY(@original_l_margin, footer_y); Chris@1296: Chris@1296: #print document barcode Chris@1296: if (@barcode) Chris@1296: Ln(); Chris@1296: barcode_width = ((@w - @original_l_margin - @original_r_margin)).round; #max width Chris@1296: writeBarcode(@original_l_margin, footer_y + line_width, barcode_width, footer_height - line_width, "C128B", false, false, 2, @barcode); Chris@1296: end Chris@1296: Chris@1296: SetXY(@original_l_margin, footer_y); Chris@1296: Chris@1296: #Print page number Chris@1296: Cell(0, footer_height, @l['w_page'] + " " + PageNo().to_s + ' / {nb}', 'T', 0, 'R'); Chris@1296: end Chris@1296: end Chris@1296: alias_method :footer, :Footer Chris@1296: Chris@1296: # Chris@1296: # Returns the current page number. Chris@1296: # @return int page number Chris@1296: # @since 1.0 Chris@1296: # @see alias_nb_pages() Chris@1296: # Chris@1296: def PageNo() Chris@1296: #Get current page number Chris@1296: return @page; Chris@1296: end Chris@1296: alias_method :page_no, :PageNo Chris@1296: Chris@1296: # Chris@1296: # Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. Chris@1296: # @param int :r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 Chris@1296: # @param int :g Green component (between 0 and 255) Chris@1296: # @param int :b Blue component (between 0 and 255) Chris@1296: # @since 1.3 Chris@1296: # @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() Chris@1296: # Chris@1296: def SetDrawColor(r, g=-1, b=-1) Chris@1296: #Set color for all stroking operations Chris@1296: if ((r==0 and g==0 and b==0) or g==-1) Chris@1296: @draw_color=sprintf('%.3f G', r/255.0); Chris@1296: else Chris@1296: @draw_color=sprintf('%.3f %.3f %.3f RG', r/255.0, g/255.0, b/255.0); Chris@1296: end Chris@1296: if (@page>0) Chris@1296: out(@draw_color); Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_draw_color, :SetDrawColor Chris@1296: Chris@1296: # Chris@1296: # Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. Chris@1296: # @param int :r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 Chris@1296: # @param int :g Green component (between 0 and 255) Chris@1296: # @param int :b Blue component (between 0 and 255) Chris@1296: # @param boolean :storeprev if true stores the RGB array on :prevfill_color variable. Chris@1296: # @since 1.3 Chris@1296: # @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() Chris@1296: # Chris@1296: def SetFillColor(r, g=-1, b=-1, storeprev=false) Chris@1296: #Set color for all filling operations Chris@1296: if ((r==0 and g==0 and b==0) or g==-1) Chris@1296: @fill_color=sprintf('%.3f g', r/255.0); Chris@1296: else Chris@1296: @fill_color=sprintf('%.3f %.3f %.3f rg', r/255.0, g/255.0, b/255.0); Chris@1296: end Chris@1296: @color_flag=(@fill_color!=@text_color); Chris@1296: if (@page>0) Chris@1296: out(@fill_color); Chris@1296: end Chris@1296: if (storeprev) Chris@1296: # store color as previous value Chris@1296: @prevfill_color = [r, g, b] Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_fill_color, :SetFillColor Chris@1296: Chris@1296: # This hasn't been ported from tcpdf, it's a variation on SetTextColor for setting cmyk colors Chris@1296: def SetCmykFillColor(c, m, y, k, storeprev=false) Chris@1296: #Set color for all filling operations Chris@1296: @fill_color=sprintf('%.3f %.3f %.3f %.3f k', c, m, y, k); Chris@1296: @color_flag=(@fill_color!=@text_color); Chris@1296: if (storeprev) Chris@1296: # store color as previous value Chris@1296: @prevtext_color = [c, m, y, k] Chris@1296: end Chris@1296: if (@page>0) Chris@1296: out(@fill_color); Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_cmyk_fill_color, :SetCmykFillColor Chris@1296: Chris@1296: # Chris@1296: # Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. Chris@1296: # @param int :r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 Chris@1296: # @param int :g Green component (between 0 and 255) Chris@1296: # @param int :b Blue component (between 0 and 255) Chris@1296: # @param boolean :storeprev if true stores the RGB array on :prevtext_color variable. Chris@1296: # @since 1.3 Chris@1296: # @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() Chris@1296: # Chris@1296: def SetTextColor(r, g=-1, b=-1, storeprev=false) Chris@1296: #Set color for text Chris@1296: if ((r==0 and :g==0 and :b==0) or :g==-1) Chris@1296: @text_color=sprintf('%.3f g', r/255.0); Chris@1296: else Chris@1296: @text_color=sprintf('%.3f %.3f %.3f rg', r/255.0, g/255.0, b/255.0); Chris@1296: end Chris@1296: @color_flag=(@fill_color!=@text_color); Chris@1296: if (storeprev) Chris@1296: # store color as previous value Chris@1296: @prevtext_color = [r, g, b] Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_text_color, :SetTextColor Chris@1296: Chris@1296: # This hasn't been ported from tcpdf, it's a variation on SetTextColor for setting cmyk colors Chris@1296: def SetCmykTextColor(c, m, y, k, storeprev=false) Chris@1296: #Set color for text Chris@1296: @text_color=sprintf('%.3f %.3f %.3f %.3f k', c, m, y, k); Chris@1296: @color_flag=(@fill_color!=@text_color); Chris@1296: if (storeprev) Chris@1296: # store color as previous value Chris@1296: @prevtext_color = [c, m, y, k] Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_cmyk_text_color, :SetCmykTextColor Chris@1296: Chris@1296: # Chris@1296: # Returns the length of a string in user unit. A font must be selected.
Chris@1296: # Support UTF-8 Unicode [Nicola Asuni, 2005-01-02] Chris@1296: # @param string :s The string whose length is to be computed Chris@1296: # @return int Chris@1296: # @since 1.2 Chris@1296: # Chris@1296: def GetStringWidth(s) Chris@1296: #Get width of a string in the current font Chris@1296: s = s.to_s; Chris@1296: cw = @current_font['cw'] Chris@1296: w = 0; Chris@1296: if (@is_unicode) Chris@1296: unicode = UTF8StringToArray(s); Chris@1296: unicode.each do |char| Chris@1296: if (!cw[char].nil?) Chris@1296: w += cw[char]; Chris@1296: # This should not happen. UTF8StringToArray should guarentee the array is ascii values. Chris@1296: # elsif (c!cw[char[0]].nil?) Chris@1296: # w += cw[char[0]]; Chris@1296: # elsif (!cw[char.chr].nil?) Chris@1296: # w += cw[char.chr]; Chris@1296: elsif (!@current_font['desc']['MissingWidth'].nil?) Chris@1296: w += @current_font['desc']['MissingWidth']; # set default size Chris@1296: else Chris@1296: w += 500; Chris@1296: end Chris@1296: end Chris@1296: else Chris@1296: s.each_byte do |c| Chris@1296: if cw[c.chr] Chris@1296: w += cw[c.chr]; Chris@1296: elsif cw[?c.chr] Chris@1296: w += cw[?c.chr] Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: return (w * @font_size / 1000.0); Chris@1296: end Chris@1296: alias_method :get_string_width, :GetStringWidth Chris@1296: Chris@1296: # Chris@1296: # Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. Chris@1296: # @param float :width The width. Chris@1296: # @since 1.0 Chris@1296: # @see Line(), Rect(), Cell(), MultiCell() Chris@1296: # Chris@1296: def SetLineWidth(width) Chris@1296: #Set line width Chris@1296: @line_width = width; Chris@1296: if (@page>0) Chris@1296: out(sprintf('%.2f w', width*@k)); Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_line_width, :SetLineWidth Chris@1296: Chris@1296: # Chris@1296: # Draws a line between two points. Chris@1296: # @param float :x1 Abscissa of first point Chris@1296: # @param float :y1 Ordinate of first point Chris@1296: # @param float :x2 Abscissa of second point Chris@1296: # @param float :y2 Ordinate of second point Chris@1296: # @since 1.0 Chris@1296: # @see SetLineWidth(), SetDrawColor() Chris@1296: # Chris@1296: def Line(x1, y1, x2, y2) Chris@1296: #Draw a line Chris@1296: out(sprintf('%.2f %.2f m %.2f %.2f l S', x1 * @k, (@h - y1) * @k, x2 * @k, (@h - y2) * @k)); Chris@1296: end Chris@1296: alias_method :line, :Line Chris@1296: Chris@1296: def Circle(mid_x, mid_y, radius, style='') Chris@1296: mid_y = (@h-mid_y)*@k Chris@1296: out(sprintf("q\n")) # postscript content in pdf Chris@1296: # init line type etc. with /GSD gs G g (grey) RG rg (RGB) w=line witdh etc. Chris@1296: out(sprintf("1 j\n")) # line join Chris@1296: # translate ("move") circle to mid_y, mid_y Chris@1296: out(sprintf("1 0 0 1 %f %f cm", mid_x, mid_y)) Chris@1296: kappa = 0.5522847498307933984022516322796 Chris@1296: # Quadrant 1 Chris@1296: x_s = 0.0 # 12 o'clock Chris@1296: y_s = 0.0 + radius Chris@1296: x_e = 0.0 + radius # 3 o'clock Chris@1296: y_e = 0.0 Chris@1296: out(sprintf("%f %f m\n", x_s, y_s)) # move to 12 o'clock Chris@1296: # cubic bezier control point 1, start height and kappa * radius to the right Chris@1296: bx_e1 = x_s + (radius * kappa) Chris@1296: by_e1 = y_s Chris@1296: # cubic bezier control point 2, end and kappa * radius above Chris@1296: bx_e2 = x_e Chris@1296: by_e2 = y_e + (radius * kappa) Chris@1296: # 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 Chris@1296: out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e)) Chris@1296: # Quadrant 2 Chris@1296: x_s = x_e Chris@1296: y_s = y_e # 3 o'clock Chris@1296: x_e = 0.0 Chris@1296: y_e = 0.0 - radius # 6 o'clock Chris@1296: bx_e1 = x_s # cubic bezier point 1 Chris@1296: by_e1 = y_s - (radius * kappa) Chris@1296: bx_e2 = x_e + (radius * kappa) # cubic bezier point 2 Chris@1296: by_e2 = y_e Chris@1296: out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e)) Chris@1296: # Quadrant 3 Chris@1296: x_s = x_e Chris@1296: y_s = y_e # 6 o'clock Chris@1296: x_e = 0.0 - radius Chris@1296: y_e = 0.0 # 9 o'clock Chris@1296: bx_e1 = x_s - (radius * kappa) # cubic bezier point 1 Chris@1296: by_e1 = y_s Chris@1296: bx_e2 = x_e # cubic bezier point 2 Chris@1296: by_e2 = y_e - (radius * kappa) Chris@1296: out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e)) Chris@1296: # Quadrant 4 Chris@1296: x_s = x_e Chris@1296: y_s = y_e # 9 o'clock Chris@1296: x_e = 0.0 Chris@1296: y_e = 0.0 + radius # 12 o'clock Chris@1296: bx_e1 = x_s # cubic bezier point 1 Chris@1296: by_e1 = y_s + (radius * kappa) Chris@1296: bx_e2 = x_e - (radius * kappa) # cubic bezier point 2 Chris@1296: by_e2 = y_e Chris@1296: out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e)) Chris@1296: if style=='F' Chris@1296: op='f' Chris@1296: elsif style=='FD' or style=='DF' Chris@1296: op='b' Chris@1296: else Chris@1296: op='s' Chris@1296: end Chris@1296: out(sprintf("#{op}\n")) # stroke circle, do not fill and close path Chris@1296: # for filling etc. b, b*, f, f* Chris@1296: out(sprintf("Q\n")) # finish postscript in PDF Chris@1296: end Chris@1296: alias_method :circle, :Circle Chris@1296: Chris@1296: # Chris@1296: # Outputs a rectangle. It can be drawn (border only), filled (with no border) or both. Chris@1296: # @param float :x Abscissa of upper-left corner Chris@1296: # @param float :y Ordinate of upper-left corner Chris@1296: # @param float :w Width Chris@1296: # @param float :h Height Chris@1296: # @param string :style Style of rendering. Possible values are: Chris@1296: # @since 1.0 Chris@1296: # @see SetLineWidth(), SetDrawColor(), SetFillColor() Chris@1296: # Chris@1296: def Rect(x, y, w, h, style='') Chris@1296: #Draw a rectangle Chris@1296: if (style=='F') Chris@1296: op='f'; Chris@1296: elsif (style=='FD' or style=='DF') Chris@1296: op='B'; Chris@1296: else Chris@1296: op='S'; Chris@1296: end Chris@1296: out(sprintf('%.2f %.2f %.2f %.2f re %s', x * @k, (@h - y) * @k, w * @k, -h * @k, op)); Chris@1296: end Chris@1296: alias_method :rect, :Rect Chris@1296: Chris@1296: # Chris@1296: # Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.rb utility. The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by FPDF_FONTPATH if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. Chris@1296: # Support UTF-8 Unicode [Nicola Asuni, 2005-01-02]. Chris@1296: # Example:
Chris@1296: #
Chris@1296: 	# :pdf->AddFont('Comic','I');
Chris@1296: 	# # is equivalent to:
Chris@1296: 	# :pdf->AddFont('Comic','I','comici.rb');
Chris@1296: 	# 
Chris@1296: # @param string :family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. Chris@1296: # @param string :style Font style. Possible values are (case insensitive): Chris@1296: # @param string :file The font definition file. By default, the name is built from the family and style, in lower case with no space. Chris@1296: # @since 1.5 Chris@1296: # @see SetFont() Chris@1296: # Chris@1296: def AddFont(family, style='', file='') Chris@1296: if (family.empty?) Chris@1296: return; Chris@1296: end Chris@1296: Chris@1296: #Add a TrueType or Type1 font Chris@1296: family = family.downcase Chris@1296: if ((!@is_unicode) and (family == 'arial')) Chris@1296: family = 'helvetica'; Chris@1296: end Chris@1296: Chris@1296: style=style.upcase Chris@1296: style=style.gsub('U',''); Chris@1296: style=style.gsub('D',''); Chris@1296: if (style == 'IB') Chris@1296: style = 'BI'; Chris@1296: end Chris@1296: Chris@1296: fontkey = family + style; Chris@1296: # check if the font has been already added Chris@1296: if !@fonts[fontkey].nil? Chris@1296: return; Chris@1296: end Chris@1296: Chris@1296: if (file=='') Chris@1296: file = family.gsub(' ', '') + style.downcase + '.rb'; Chris@1296: end Chris@1296: font_file_name = getfontpath(file) Chris@1296: if (font_file_name.nil?) Chris@1296: # try to load the basic file without styles Chris@1296: file = family.gsub(' ', '') + '.rb'; Chris@1296: font_file_name = getfontpath(file) Chris@1296: end Chris@1296: if font_file_name.nil? Chris@1296: Error("Could not find font #{file}.") Chris@1296: end Chris@1296: require(getfontpath(file)) Chris@1296: font_desc = TCPDFFontDescriptor.font(file) Chris@1296: Chris@1296: if (font_desc[:name].nil? and @@fpdf_charwidths.nil?) Chris@1296: Error('Could not include font definition file'); Chris@1296: end Chris@1296: Chris@1296: i = @fonts.length+1; Chris@1296: if (@is_unicode) Chris@1296: @fonts[fontkey] = {'i' => i, 'type' => font_desc[:type], 'name' => font_desc[:name], 'desc' => font_desc[:desc], 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'enc' => font_desc[:enc], 'file' => font_desc[:file], 'ctg' => font_desc[:ctg], 'cMap' => font_desc[:cMap], 'registry' => font_desc[:registry]} Chris@1296: @@fpdf_charwidths[fontkey] = font_desc[:cw]; Chris@1296: else Chris@1296: @fonts[fontkey]={'i' => i, 'type'=>'core', 'name'=>@core_fonts[fontkey], 'up'=>-100, 'ut'=>50, 'cw' => font_desc[:cw]} Chris@1296: @@fpdf_charwidths[fontkey] = font_desc[:cw]; Chris@1296: end Chris@1296: Chris@1296: if (!font_desc[:diff].nil? and (!font_desc[:diff].empty?)) Chris@1296: #Search existing encodings Chris@1296: d=0; Chris@1296: nb=@diffs.length; Chris@1296: 1.upto(nb) do |i| Chris@1296: if (@diffs[i]== font_desc[:diff]) Chris@1296: d = i; Chris@1296: break; Chris@1296: end Chris@1296: end Chris@1296: if (d==0) Chris@1296: d = nb+1; Chris@1296: @diffs[d] = font_desc[:diff]; Chris@1296: end Chris@1296: @fonts[fontkey]['diff'] = d; Chris@1296: end Chris@1296: if (font_desc[:file] and font_desc[:file].length > 0) Chris@1296: if (font_desc[:type] == "TrueType") or (font_desc[:type] == "TrueTypeUnicode") Chris@1296: @font_files[font_desc[:file]] = {'length1' => font_desc[:originalsize]} Chris@1296: else Chris@1296: @font_files[font_desc[:file]] = {'length1' => font_desc[:size1], 'length2' => font_desc[:size2]} Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: alias_method :add_font, :AddFont Chris@1296: Chris@1296: # Chris@1296: # Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid. Chris@1296: # The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). Chris@1296: # The method can be called before the first page is created and the font is retained from page to page. Chris@1296: # If you just wish to change the current font size, it is simpler to call SetFontSize(). Chris@1296: # Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:
Chris@1296: # Example for the last case (note the trailing slash):
Chris@1296: #
Chris@1296: 	# define('FPDF_FONTPATH','/home/www/font/');
Chris@1296: 	# require('tcpdf.rb');
Chris@1296: 	#
Chris@1296: 	# #Times regular 12
Chris@1296: 	# :pdf->SetFont('Times');
Chris@1296: 	# #Arial bold 14
Chris@1296: 	# :pdf->SetFont('Arial','B',14);
Chris@1296: 	# #Removes bold
Chris@1296: 	# :pdf->SetFont('');
Chris@1296: 	# #Times bold, italic and underlined 14
Chris@1296: 	# :pdf->SetFont('Times','BIUD');
Chris@1296: 	# 

Chris@1296: # If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated. Chris@1296: # @param string :family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):It is also possible to pass an empty string. In that case, the current family is retained. Chris@1296: # @param string :style Font style. Possible values are (case insensitive):or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats Chris@1296: # @param float :size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 Chris@1296: # @since 1.0 Chris@1296: # @see AddFont(), SetFontSize(), Cell(), MultiCell(), Write() Chris@1296: # Chris@1296: def SetFont(family, style='', size=0) Chris@1296: # save previous values Chris@1296: @prevfont_family = @font_family; Chris@1296: @prevfont_style = @font_style; Chris@1296: Chris@1296: family=family.downcase; Chris@1296: if (family=='') Chris@1296: family=@font_family; Chris@1296: end Chris@1296: if ((!@is_unicode) and (family == 'arial')) Chris@1296: family = 'helvetica'; Chris@1296: elsif ((family=="symbol") or (family=="zapfdingbats")) Chris@1296: style=''; Chris@1296: end Chris@1296: Chris@1296: style=style.upcase; Chris@1296: Chris@1296: if (style.include?('U')) Chris@1296: @underline=true; Chris@1296: style= style.gsub('U',''); Chris@1296: else Chris@1296: @underline=false; Chris@1296: end Chris@1296: if (style.include?('D')) Chris@1296: @deleted=true; Chris@1296: style= style.gsub('D',''); Chris@1296: else Chris@1296: @deleted=false; Chris@1296: end Chris@1296: if (style=='IB') Chris@1296: style='BI'; Chris@1296: end Chris@1296: if (size==0) Chris@1296: size=@font_size_pt; Chris@1296: end Chris@1296: Chris@1296: # try to add font (if not already added) Chris@1296: AddFont(family, style); Chris@1296: Chris@1296: #Test if font is already selected Chris@1296: if ((@font_family == family) and (@font_style == style) and (@font_size_pt == size)) Chris@1296: return; Chris@1296: end Chris@1296: Chris@1296: fontkey = family + style; Chris@1296: style = '' if (@fonts[fontkey].nil? and !@fonts[family].nil?) Chris@1296: Chris@1296: #Test if used for the first time Chris@1296: if (@fonts[fontkey].nil?) Chris@1296: #Check if one of the standard fonts Chris@1296: if (!@core_fonts[fontkey].nil?) Chris@1296: if @@fpdf_charwidths[fontkey].nil? Chris@1296: #Load metric file Chris@1296: file = family; Chris@1296: if ((family!='symbol') and (family!='zapfdingbats')) Chris@1296: file += style.downcase; Chris@1296: end Chris@1296: if (getfontpath(file + '.rb').nil?) Chris@1296: # try to load the basic file without styles Chris@1296: file = family; Chris@1296: fontkey = family; Chris@1296: end Chris@1296: require(getfontpath(file + '.rb')); Chris@1296: font_desc = TCPDFFontDescriptor.font(file) Chris@1296: if ((@is_unicode and ctg.nil?) or ((!@is_unicode) and (@@fpdf_charwidths[fontkey].nil?)) ) Chris@1296: Error("Could not include font metric file [" + fontkey + "]: " + getfontpath(file + ".rb")); Chris@1296: end Chris@1296: end Chris@1296: i = @fonts.length + 1; Chris@1296: Chris@1296: if (@is_unicode) Chris@1296: @fonts[fontkey] = {'i' => i, 'type' => font_desc[:type], 'name' => font_desc[:name], 'desc' => font_desc[:desc], 'up' => font_desc[:up], 'ut' => font_desc[:ut], 'cw' => font_desc[:cw], 'enc' => font_desc[:enc], 'file' => font_desc[:file], 'ctg' => font_desc[:ctg]} Chris@1296: @@fpdf_charwidths[fontkey] = font_desc[:cw]; Chris@1296: else Chris@1296: @fonts[fontkey] = {'i' => i, 'type'=>'core', 'name'=>@core_fonts[fontkey], 'up'=>-100, 'ut'=>50, 'cw' => font_desc[:cw]} Chris@1296: @@fpdf_charwidths[fontkey] = font_desc[:cw]; Chris@1296: end Chris@1296: else Chris@1296: Error('Undefined font: ' + family + ' ' + style); Chris@1296: end Chris@1296: end Chris@1296: #Select it Chris@1296: @font_family = family; Chris@1296: @font_style = style; Chris@1296: @font_size_pt = size; Chris@1296: @font_size = size / @k; Chris@1296: @current_font = @fonts[fontkey]; # was & may need deep copy? Chris@1296: if (@page>0) Chris@1296: out(sprintf('BT /F%d %.2f Tf ET', @current_font['i'], @font_size_pt)); Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_font, :SetFont Chris@1296: Chris@1296: # Chris@1296: # Defines the size of the current font. Chris@1296: # @param float :size The size (in points) Chris@1296: # @since 1.0 Chris@1296: # @see SetFont() Chris@1296: # Chris@1296: def SetFontSize(size) Chris@1296: #Set font size in points Chris@1296: if (@font_size_pt== size) Chris@1296: return; Chris@1296: end Chris@1296: @font_size_pt = size; Chris@1296: @font_size = size.to_f / @k; Chris@1296: if (@page > 0) Chris@1296: out(sprintf('BT /F%d %.2f Tf ET', @current_font['i'], @font_size_pt)); Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_font_size, :SetFontSize Chris@1296: Chris@1296: # Chris@1296: # Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.
Chris@1296: # The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). Chris@1296: # @since 1.5 Chris@1296: # @see Cell(), Write(), Image(), Link(), SetLink() Chris@1296: # Chris@1296: def AddLink() Chris@1296: #Create a new internal link Chris@1296: n=@links.length+1; Chris@1296: @links[n]=[0,0]; Chris@1296: return n; Chris@1296: end Chris@1296: alias_method :add_link, :AddLink Chris@1296: Chris@1296: # Chris@1296: # Defines the page and position a link points to Chris@1296: # @param int :link The link identifier returned by AddLink() Chris@1296: # @param float :y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) Chris@1296: # @param int :page Number of target page; -1 indicates the current page. This is the default value Chris@1296: # @since 1.5 Chris@1296: # @see AddLink() Chris@1296: # Chris@1296: def SetLink(link, y=0, page=-1) Chris@1296: #Set destination of internal link Chris@1296: if (y==-1) Chris@1296: y=@y; Chris@1296: end Chris@1296: if (page==-1) Chris@1296: page=@page; Chris@1296: end Chris@1296: @links[link] = [page, y] Chris@1296: end Chris@1296: alias_method :set_link, :SetLink Chris@1296: Chris@1296: # Chris@1296: # Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. Chris@1296: # @param float :x Abscissa of the upper-left corner of the rectangle Chris@1296: # @param float :y Ordinate of the upper-left corner of the rectangle Chris@1296: # @param float :w Width of the rectangle Chris@1296: # @param float :h Height of the rectangle Chris@1296: # @param mixed :link URL or identifier returned by AddLink() Chris@1296: # @since 1.5 Chris@1296: # @see AddLink(), Cell(), Write(), Image() Chris@1296: # Chris@1296: def Link(x, y, w, h, link) Chris@1296: #Put a link on the page Chris@1296: @page_links ||= Array.new Chris@1296: @page_links[@page] ||= Array.new Chris@1296: @page_links[@page].push([x * @k, @h_pt - y * @k, w * @k, h*@k, link]); Chris@1296: end Chris@1296: alias_method :link, :Link Chris@1296: Chris@1296: # Chris@1296: # Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text. Chris@1296: # @param float :x Abscissa of the origin Chris@1296: # @param float :y Ordinate of the origin Chris@1296: # @param string :txt String to print Chris@1296: # @since 1.0 Chris@1296: # @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() Chris@1296: # Chris@1296: def Text(x, y, txt) Chris@1296: #Output a string Chris@1296: s=sprintf('BT %.2f %.2f Td (%s) Tj ET', x * @k, (@h-y) * @k, escapetext(txt)); Chris@1296: if (@underline and (txt!='')) Chris@1296: s += ' ' + dolinetxt(x, y, txt); Chris@1296: end Chris@1296: if (@color_flag) Chris@1296: s='q ' + @text_color + ' ' + s + ' Q'; Chris@1296: end Chris@1296: out(s); Chris@1296: end Chris@1296: alias_method :text, :Text Chris@1296: Chris@1296: # Chris@1296: # Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().
Chris@1296: # This method is called automatically and should not be called directly by the application.
Chris@1296: # Example:
Chris@1296: # The method is overriden in an inherited class in order to obtain a 3 column layout:
Chris@1296: #
Chris@1296: 	# class PDF extends TCPDF {
Chris@1296: 	# 	var :col=0;
Chris@1296: 	#
Chris@1296: 	# 	def SetCol(col)
Chris@1296: 	# 		#Move position to a column
Chris@1296: 	# 		@col = col;
Chris@1296: 	# 		:x=10+:col*65;
Chris@1296: 	# 		SetLeftMargin(x);
Chris@1296: 	# 		SetX(x);
Chris@1296: 	# 	end
Chris@1296: 	#
Chris@1296: 	# 	def AcceptPageBreak()
Chris@1296: 	# 		if (@col<2)
Chris@1296: 	# 			#Go to next column
Chris@1296: 	# 			SetCol(@col+1);
Chris@1296: 	# 			SetY(10);
Chris@1296: 	# 			return false;
Chris@1296: 	# 		end
Chris@1296: 	# 		else
Chris@1296: 	# 			#Go back to first column and issue page break
Chris@1296: 	# 			SetCol(0);
Chris@1296: 	# 			return true;
Chris@1296: 	# 		end
Chris@1296: 	# 	end
Chris@1296: 	# }
Chris@1296: 	#
Chris@1296: 	# :pdf=new PDF();
Chris@1296: 	# :pdf->Open();
Chris@1296: 	# :pdf->AddPage();
Chris@1296: 	# :pdf->SetFont('Arial','',12);
Chris@1296: 	# for(i=1;:i<=300;:i++)
Chris@1296: 	#     :pdf->Cell(0,5,"Line :i",0,1);
Chris@1296: 	# }
Chris@1296: 	# :pdf->Output();
Chris@1296: 	# 
Chris@1296: # @return boolean Chris@1296: # @since 1.4 Chris@1296: # @see SetAutoPageBreak() Chris@1296: # Chris@1296: def AcceptPageBreak() Chris@1296: #Accept automatic page break or not Chris@1296: return @auto_page_break; Chris@1296: end Chris@1296: alias_method :accept_page_break, :AcceptPageBreak Chris@1296: Chris@1296: def BreakThePage?(h) Chris@1296: if ((@y + h) > @page_break_trigger and !@in_footer and AcceptPageBreak()) Chris@1296: true Chris@1296: else Chris@1296: false Chris@1296: end Chris@1296: end Chris@1296: alias_method :break_the_page?, :BreakThePage? Chris@1296: # Chris@1296: # Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.
Chris@1296: # If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. Chris@1296: # @param float :w Cell width. If 0, the cell extends up to the right margin. Chris@1296: # @param float :h Cell height. Default value: 0. Chris@1296: # @param string :txt String to print. Default value: empty string. Chris@1296: # @param mixed :border Indicates if borders must be drawn around the cell. The value can be either a number:or a string containing some or all of the following characters (in any order): Chris@1296: # @param int :ln Indicates where the current position should go after the call. Possible values are: Chris@1296: # Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. Chris@1296: # @param string :align Allows to center or align the text. Possible values are: Chris@1296: # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # @param mixed :link URL or identifier returned by AddLink(). Chris@1296: # @since 1.0 Chris@1296: # @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() Chris@1296: # Chris@1296: def Cell(w, h=0, txt='', border=0, ln=0, align='', fill=0, link=nil) Chris@1296: #Output a cell Chris@1296: k=@k; Chris@1296: if ((@y + h) > @page_break_trigger and !@in_footer and AcceptPageBreak()) Chris@1296: #Automatic page break Chris@1296: if @pages[@page+1].nil? Chris@1296: x = @x; Chris@1296: ws = @ws; Chris@1296: if (ws > 0) Chris@1296: @ws = 0; Chris@1296: out('0 Tw'); Chris@1296: end Chris@1296: AddPage(@cur_orientation); Chris@1296: @x = x; Chris@1296: if (ws > 0) Chris@1296: @ws = ws; Chris@1296: out(sprintf('%.3f Tw', ws * k)); Chris@1296: end Chris@1296: else Chris@1296: @page += 1; Chris@1296: @y=@t_margin; Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: if (w == 0) Chris@1296: w = @w - @r_margin - @x; Chris@1296: end Chris@1296: s = ''; Chris@1296: if ((fill.to_i == 1) or (border.to_i == 1)) Chris@1296: if (fill.to_i == 1) Chris@1296: op = (border.to_i == 1) ? 'B' : 'f'; Chris@1296: else Chris@1296: op = 'S'; Chris@1296: end Chris@1296: s = sprintf('%.2f %.2f %.2f %.2f re %s ', @x * k, (@h - @y) * k, w * k, -h * k, op); Chris@1296: end Chris@1296: if (border.is_a?(String)) Chris@1296: x=@x; Chris@1296: y=@y; Chris@1296: if (border.include?('L')) Chris@1296: s<0) Chris@1296: # Go to next line Chris@1296: @y += h; Chris@1296: if (ln == 1) Chris@1296: @x = @l_margin; Chris@1296: end Chris@1296: else Chris@1296: @x += w; Chris@1296: end Chris@1296: end Chris@1296: alias_method :cell, :Cell Chris@1296: Chris@1296: # Chris@1296: # This method allows printing text with line breaks. They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.
Chris@1296: # Text can be aligned, centered or justified. The cell block can be framed and the background painted. Chris@1296: # @param float :w Width of cells. If 0, they extend up to the right margin of the page. Chris@1296: # @param float :h Height of cells. Chris@1296: # @param string :txt String to print Chris@1296: # @param mixed :border Indicates if borders must be drawn around the cell block. The value can be either a number:or a string containing some or all of the following characters (in any order): Chris@1296: # @param string :align Allows to center or align the text. Possible values are: Chris@1296: # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # @param int :ln Indicates where the current position should go after the call. Possible values are: Chris@1296: # @since 1.3 Chris@1296: # @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() Chris@1296: # Chris@1296: def MultiCell(w, h, txt, border=0, align='J', fill=0, ln=1) Chris@1296: Chris@1296: # save current position Chris@1296: prevx = @x; Chris@1296: prevy = @y; Chris@1296: prevpage = @page; Chris@1296: Chris@1296: #Output text with automatic or explicit line breaks Chris@1296: Chris@1296: if (w == 0) Chris@1296: w = @w - @r_margin - @x; Chris@1296: end Chris@1296: Chris@1296: wmax = (w - 3 * @c_margin); Chris@1296: Chris@1296: s = txt.gsub("\r", ''); # remove carriage returns Chris@1296: nb = s.length; Chris@1296: Chris@1296: b=0; Chris@1296: if (border) Chris@1296: if (border==1) Chris@1296: border='LTRB'; Chris@1296: b='LRT'; Chris@1296: b2='LR'; Chris@1296: elsif border.is_a?(String) Chris@1296: b2=''; Chris@1296: if (border.include?('L')) Chris@1296: b2<<'L'; Chris@1296: end Chris@1296: if (border.include?('R')) Chris@1296: b2<<'R'; Chris@1296: end Chris@1296: b=(border.include?('T')) ? b2 + 'T' : b2; Chris@1296: end Chris@1296: end Chris@1296: sep=-1; Chris@1296: to_index=0; Chris@1296: from_j=0; Chris@1296: l=0; Chris@1296: ns=0; Chris@1296: nl=1; Chris@1296: Chris@1296: while to_index < nb Chris@1296: #Get next character Chris@1296: c = s[to_index]; Chris@1296: if c == "\n"[0] Chris@1296: #Explicit line break Chris@1296: if @ws > 0 Chris@1296: @ws = 0 Chris@1296: out('0 Tw') Chris@1296: end Chris@1296: #Ed Moss - change begin Chris@1296: end_i = to_index == 0 ? 0 : to_index - 1 Chris@1296: # Changed from s[from_j..to_index] to fix bug reported by Hans Allis. Chris@1296: from_j = to_index == 0 ? 1 : from_j Chris@1296: Cell(w, h, s[from_j..end_i], b, 2, align, fill) Chris@1296: #change end Chris@1296: to_index += 1 Chris@1296: sep=-1 Chris@1296: from_j=to_index Chris@1296: l=0 Chris@1296: ns=0 Chris@1296: nl += 1 Chris@1296: b = b2 if border and nl==2 Chris@1296: next Chris@1296: end Chris@1296: if (c == " "[0]) Chris@1296: sep = to_index; Chris@1296: ls = l; Chris@1296: ns += 1; Chris@1296: end Chris@1296: Chris@1296: l = GetStringWidth(s[from_j, to_index - from_j]); Chris@1296: Chris@1296: if (l > wmax) Chris@1296: #Automatic line break Chris@1296: if (sep == -1) Chris@1296: if (to_index == from_j) Chris@1296: to_index += 1; Chris@1296: end Chris@1296: if (@ws > 0) Chris@1296: @ws = 0; Chris@1296: out('0 Tw'); Chris@1296: end Chris@1296: Cell(w, h, s[from_j..to_index-1], b, 2, align, fill) # my FPDF version Chris@1296: else Chris@1296: if (align=='J' || align=='justify' || align=='justified') Chris@1296: @ws = (ns>1) ? (wmax-ls)/(ns-1) : 0; Chris@1296: out(sprintf('%.3f Tw', @ws * @k)); Chris@1296: end Chris@1296: Cell(w, h, s[from_j..sep], b, 2, align, fill); Chris@1296: to_index = sep + 1; Chris@1296: end Chris@1296: sep=-1; Chris@1296: from_j = to_index; Chris@1296: l=0; Chris@1296: ns=0; Chris@1296: nl += 1; Chris@1296: if (border and (nl==2)) Chris@1296: b = b2; Chris@1296: end Chris@1296: else Chris@1296: to_index += 1; Chris@1296: end Chris@1296: end Chris@1296: #Last chunk Chris@1296: if (@ws>0) Chris@1296: @ws=0; Chris@1296: out('0 Tw'); Chris@1296: end Chris@1296: if (border.is_a?(String) and border.include?('B')) Chris@1296: b<<'B'; Chris@1296: end Chris@1296: Cell(w, h, s[from_j, to_index-from_j], b, 2, align, fill); Chris@1296: Chris@1296: # move cursor to specified position Chris@1296: # since 2007-03-03 Chris@1296: if (ln == 1) Chris@1296: # go to the beginning of the next line Chris@1296: @x = @l_margin; Chris@1296: elsif (ln == 0) Chris@1296: # go to the top-right of the cell Chris@1296: @page = prevpage; Chris@1296: @y = prevy; Chris@1296: @x = prevx + w; Chris@1296: elsif (ln == 2) Chris@1296: # go to the bottom-left of the cell Chris@1296: @x = prevx; Chris@1296: end Chris@1296: end Chris@1296: alias_method :multi_cell, :MultiCell Chris@1296: Chris@1296: # Chris@1296: # This method prints text from the current position. When the right margin is reached (or the \n character is met) a line break occurs and text continues from the left margin. Upon method exit, the current position is left just at the end of the text. It is possible to put a link on the text.
Chris@1296: # Example:
Chris@1296: #
Chris@1296: 	# #Begin with regular font
Chris@1296: 	# :pdf->SetFont('Arial','',14);
Chris@1296: 	# :pdf->Write(5,'Visit ');
Chris@1296: 	# #Then put a blue underlined link
Chris@1296: 	# :pdf->SetTextColor(0,0,255);
Chris@1296: 	# :pdf->SetFont('','U');
Chris@1296: 	# :pdf->Write(5,'www.tecnick.com','http://www.tecnick.com');
Chris@1296: 	# 
Chris@1296: # @param float :h Line height Chris@1296: # @param string :txt String to print Chris@1296: # @param mixed :link URL or identifier returned by AddLink() Chris@1296: # @param int :fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # @since 1.5 Chris@1296: # @see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() Chris@1296: # Chris@1296: def Write(h, txt, link=nil, fill=0) Chris@1296: Chris@1296: #Output text in flowing mode Chris@1296: w = @w - @r_margin - @x; Chris@1296: wmax = (w - 3 * @c_margin); Chris@1296: Chris@1296: s = txt.gsub("\r", ''); Chris@1296: nb = s.length; Chris@1296: Chris@1296: # handle single space character Chris@1296: if ((nb==1) and (s == " ")) Chris@1296: @x += GetStringWidth(s); Chris@1296: return; Chris@1296: end Chris@1296: Chris@1296: sep=-1; Chris@1296: i=0; Chris@1296: j=0; Chris@1296: l=0; Chris@1296: nl=1; Chris@1296: while(i wmax) Chris@1296: #Automatic line break (word wrapping) Chris@1296: if (sep == -1) Chris@1296: if (@x > @l_margin) Chris@1296: #Move to next line Chris@1296: @x = @l_margin; Chris@1296: @y += h; Chris@1296: w=@w - @r_margin - @x; Chris@1296: wmax=(w - 3 * @c_margin); Chris@1296: i += 1 Chris@1296: nl += 1 Chris@1296: next Chris@1296: end Chris@1296: if (i == j) Chris@1296: i += 1 Chris@1296: end Chris@1296: Cell(w, h, s[j, (i-1)], 0, 2, '', fill, link); Chris@1296: else Chris@1296: Cell(w, h, s[j, (sep-j)], 0, 2, '', fill, link); Chris@1296: i = sep+1; Chris@1296: end Chris@1296: sep = -1; Chris@1296: j = i; Chris@1296: l = 0; Chris@1296: if (nl==1) Chris@1296: @x = @l_margin; Chris@1296: w = @w - @r_margin - @x; Chris@1296: wmax = (w - 3 * @c_margin); Chris@1296: end Chris@1296: nl += 1; Chris@1296: else Chris@1296: i += 1; Chris@1296: end Chris@1296: end Chris@1296: #Last chunk Chris@1296: if (i != j) Chris@1296: Cell(GetStringWidth(s[j..i]), h, s[j..i], 0, 0, '', fill, link); Chris@1296: end Chris@1296: end Chris@1296: alias_method :write, :Write Chris@1296: Chris@1296: # Chris@1296: # Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways: Chris@1296: # Supported formats are JPEG and PNG. Chris@1296: # For JPEG, all flavors are allowed: Chris@1296: # For PNG, are allowed: Chris@1296: # but are not supported: Chris@1296: # If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).
Chris@1296: # The format can be specified explicitly or inferred from the file extension.
Chris@1296: # It is possible to put a link on the image.
Chris@1296: # Remark: if an image is used several times, only one copy will be embedded in the file.
Chris@1296: # @param string :file Name of the file containing the image. Chris@1296: # @param float :x Abscissa of the upper-left corner. Chris@1296: # @param float :y Ordinate of the upper-left corner. Chris@1296: # @param float :w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. Chris@1296: # @param float :h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. Chris@1296: # @param string :type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension. Chris@1296: # @param mixed :link URL or identifier returned by AddLink(). Chris@1296: # @since 1.1 Chris@1296: # @see AddLink() Chris@1296: # Chris@1296: def Image(file, x, y, w=0, h=0, type='', link=nil) Chris@1296: #Put an image on the page Chris@1296: if (@images[file].nil?) Chris@1296: #First use of image, get info Chris@1296: if (type == '') Chris@1296: pos = File::basename(file).rindex('.'); Chris@1296: if (pos.nil? or pos == 0) Chris@1296: Error('Image file has no extension and no type was specified: ' + file); Chris@1296: end Chris@1296: pos = file.rindex('.'); Chris@1296: type = file[pos+1..-1]; Chris@1296: end Chris@1296: type.downcase! Chris@1296: if (type == 'jpg' or type == 'jpeg') Chris@1296: info=parsejpg(file); Chris@1296: elsif (type == 'png') Chris@1296: info=parsepng(file); Chris@1296: elsif (type == 'gif') Chris@1296: tmpFile = imageToPNG(file); Chris@1296: info=parsepng(tmpFile.path); Chris@1296: tmpFile.delete Chris@1296: else Chris@1296: #Allow for additional formats Chris@1296: mtd='parse' + type; Chris@1296: if (!self.respond_to?(mtd)) Chris@1296: Error('Unsupported image type: ' + type); Chris@1296: end Chris@1296: info=send(mtd, file); Chris@1296: end Chris@1296: info['i']=@images.length+1; Chris@1296: @images[file] = info; Chris@1296: else Chris@1296: info=@images[file]; Chris@1296: end Chris@1296: #Automatic width and height calculation if needed Chris@1296: if ((w == 0) and (h == 0)) Chris@1296: rescale_x = (@w - @r_margin - x) / (info['w'] / (@img_scale * @k)) Chris@1296: rescale_x = 1 if rescale_x >= 1 Chris@1296: if (y + info['h'] * rescale_x / (@img_scale * @k) > @page_break_trigger and !@in_footer and AcceptPageBreak()) Chris@1296: #Automatic page break Chris@1296: if @pages[@page+1].nil? Chris@1296: ws = @ws; Chris@1296: if (ws > 0) Chris@1296: @ws = 0; Chris@1296: out('0 Tw'); Chris@1296: end Chris@1296: AddPage(@cur_orientation); Chris@1296: if (ws > 0) Chris@1296: @ws = ws; Chris@1296: out(sprintf('%.3f Tw', ws * @k)); Chris@1296: end Chris@1296: else Chris@1296: @page += 1; Chris@1296: end Chris@1296: y=@t_margin; Chris@1296: end Chris@1296: rescale_y = (@page_break_trigger - y) / (info['h'] / (@img_scale * @k)) Chris@1296: rescale_y = 1 if rescale_y >= 1 Chris@1296: rescale = rescale_y >= rescale_x ? rescale_x : rescale_y Chris@1296: Chris@1296: #Put image at 72 dpi Chris@1296: # 2004-06-14 :: Nicola Asuni, scale factor where added Chris@1296: w = info['w'] * rescale / (@img_scale * @k); Chris@1296: h = info['h'] * rescale / (@img_scale * @k); Chris@1296: elsif (w == 0) Chris@1296: w = h * info['w'] / info['h']; Chris@1296: elsif (h == 0) Chris@1296: h = w * info['h'] / info['w']; Chris@1296: end Chris@1296: out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', w*@k, h*@k, x*@k, (@h-(y+h))*@k, info['i'])); Chris@1296: if (link) Chris@1296: Link(x, y, w, h, link); Chris@1296: end Chris@1296: Chris@1296: #2002-07-31 - Nicola Asuni Chris@1296: # set right-bottom corner coordinates Chris@1296: @img_rb_x = x + w; Chris@1296: @img_rb_y = y + h; Chris@1296: end Chris@1296: alias_method :image, :Image Chris@1296: Chris@1296: # Chris@1296: # Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. Chris@1296: # @param float :h The height of the break. By default, the value equals the height of the last printed cell. Chris@1296: # @since 1.0 Chris@1296: # @see Cell() Chris@1296: # Chris@1296: def Ln(h='') Chris@1296: #Line feed; default value is last cell height Chris@1296: @x=@l_margin; Chris@1296: if (h.is_a?(String)) Chris@1296: @y += @lasth; Chris@1296: else Chris@1296: @y += h; Chris@1296: end Chris@1296: Chris@1296: k=@k; Chris@1296: if (@y > @page_break_trigger and !@in_footer and AcceptPageBreak()) Chris@1296: #Automatic page break Chris@1296: if @pages[@page+1].nil? Chris@1296: x = @x; Chris@1296: ws = @ws; Chris@1296: if (ws > 0) Chris@1296: @ws = 0; Chris@1296: out('0 Tw'); Chris@1296: end Chris@1296: AddPage(@cur_orientation); Chris@1296: @x = x; Chris@1296: if (ws > 0) Chris@1296: @ws = ws; Chris@1296: out(sprintf('%.3f Tw', ws * k)); Chris@1296: end Chris@1296: else Chris@1296: @page += 1; Chris@1296: @y=@t_margin; Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: end Chris@1296: alias_method :ln, :Ln Chris@1296: Chris@1296: # Chris@1296: # Returns the abscissa of the current position. Chris@1296: # @return float Chris@1296: # @since 1.2 Chris@1296: # @see SetX(), GetY(), SetY() Chris@1296: # Chris@1296: def GetX() Chris@1296: #Get x position Chris@1296: return @x; Chris@1296: end Chris@1296: alias_method :get_x, :GetX Chris@1296: Chris@1296: # Chris@1296: # Defines the abscissa of the current position. If the passed value is negative, it is relative to the right of the page. Chris@1296: # @param float :x The value of the abscissa. Chris@1296: # @since 1.2 Chris@1296: # @see GetX(), GetY(), SetY(), SetXY() Chris@1296: # Chris@1296: def SetX(x) Chris@1296: #Set x position Chris@1296: if (x>=0) Chris@1296: @x = x; Chris@1296: else Chris@1296: @x=@w+x; Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_x, :SetX Chris@1296: Chris@1296: # Chris@1296: # Returns the ordinate of the current position. Chris@1296: # @return float Chris@1296: # @since 1.0 Chris@1296: # @see SetY(), GetX(), SetX() Chris@1296: # Chris@1296: def GetY() Chris@1296: #Get y position Chris@1296: return @y; Chris@1296: end Chris@1296: alias_method :get_y, :GetY Chris@1296: Chris@1296: # Chris@1296: # Moves the current abscissa back to the left margin and sets the ordinate. If the passed value is negative, it is relative to the bottom of the page. Chris@1296: # @param float :y The value of the ordinate. Chris@1296: # @since 1.0 Chris@1296: # @see GetX(), GetY(), SetY(), SetXY() Chris@1296: # Chris@1296: def SetY(y) Chris@1296: #Set y position and reset x Chris@1296: @x=@l_margin; Chris@1296: if (y>=0) Chris@1296: @y = y; Chris@1296: else Chris@1296: @y=@h+y; Chris@1296: end Chris@1296: end Chris@1296: alias_method :set_y, :SetY Chris@1296: Chris@1296: # Chris@1296: # Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page. Chris@1296: # @param float :x The value of the abscissa Chris@1296: # @param float :y The value of the ordinate Chris@1296: # @since 1.2 Chris@1296: # @see SetX(), SetY() Chris@1296: # Chris@1296: def SetXY(x, y) Chris@1296: #Set x and y positions Chris@1296: SetY(y); Chris@1296: SetX(x); Chris@1296: end Chris@1296: alias_method :set_xy, :SetXY Chris@1296: Chris@1296: # Chris@1296: # Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.
Chris@1296: # The method first calls Close() if necessary to terminate the document. Chris@1296: # @param string :name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf. Chris@1296: # @param string :dest Destination where to send the document. It can take one of the following values:If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.
Chris@1296: # @since 1.0 Chris@1296: # @see Close() Chris@1296: # Chris@1296: def Output(name='', dest='') Chris@1296: #Output PDF to some destination Chris@1296: #Finish document if necessary Chris@1296: if (@state < 3) Chris@1296: Close(); Chris@1296: end Chris@1296: #Normalize parameters Chris@1296: # Boolean no longer supported Chris@1296: # if (dest.is_a?(Boolean)) Chris@1296: # dest = dest ? 'D' : 'F'; Chris@1296: # end Chris@1296: dest = dest.upcase Chris@1296: if (dest=='') Chris@1296: if (name=='') Chris@1296: name='doc.pdf'; Chris@1296: dest='I'; Chris@1296: else Chris@1296: dest='F'; Chris@1296: end Chris@1296: end Chris@1296: case (dest) Chris@1296: when 'I' Chris@1296: # This is PHP specific code Chris@1296: ##Send to standard output Chris@1296: # if (ob_get_contents()) Chris@1296: # Error('Some data has already been output, can\'t send PDF file'); Chris@1296: # end Chris@1296: # if (php_sapi_name()!='cli') Chris@1296: # #We send to a browser Chris@1296: # header('Content-Type: application/pdf'); Chris@1296: # if (headers_sent()) Chris@1296: # Error('Some data has already been output to browser, can\'t send PDF file'); Chris@1296: # end Chris@1296: # header('Content-Length: ' + @buffer.length); Chris@1296: # header('Content-disposition: inline; filename="' + name + '"'); Chris@1296: # end Chris@1296: return @buffer; Chris@1296: Chris@1296: when 'D' Chris@1296: # PHP specific Chris@1296: #Download file Chris@1296: # if (ob_get_contents()) Chris@1296: # Error('Some data has already been output, can\'t send PDF file'); Chris@1296: # end Chris@1296: # if (!_SERVER['HTTP_USER_AGENT'].nil? && SERVER['HTTP_USER_AGENT'].include?('MSIE')) Chris@1296: # header('Content-Type: application/force-download'); Chris@1296: # else Chris@1296: # header('Content-Type: application/octet-stream'); Chris@1296: # end Chris@1296: # if (headers_sent()) Chris@1296: # Error('Some data has already been output to browser, can\'t send PDF file'); Chris@1296: # end Chris@1296: # header('Content-Length: '+ @buffer.length); Chris@1296: # header('Content-disposition: attachment; filename="' + name + '"'); Chris@1296: return @buffer; Chris@1296: Chris@1296: when 'F' Chris@1296: open(name,'wb') do |f| Chris@1296: f.write(@buffer) Chris@1296: end Chris@1296: # PHP code Chris@1296: # #Save to local file Chris@1296: # f=open(name,'wb'); Chris@1296: # if (!f) Chris@1296: # Error('Unable to create output file: ' + name); Chris@1296: # end Chris@1296: # fwrite(f,@buffer,@buffer.length); Chris@1296: # f.close Chris@1296: Chris@1296: when 'S' Chris@1296: #Return as a string Chris@1296: return @buffer; Chris@1296: else Chris@1296: Error('Incorrect output destination: ' + dest); Chris@1296: Chris@1296: end Chris@1296: return ''; Chris@1296: end Chris@1296: alias_method :output, :Output Chris@1296: Chris@1296: # Protected methods Chris@1296: Chris@1296: # Chris@1296: # Check for locale-related bug Chris@1296: # @access protected Chris@1296: # Chris@1296: def dochecks() Chris@1296: #Check for locale-related bug Chris@1296: if (1.1==1) Chris@1296: Error('Don\'t alter the locale before including class file'); Chris@1296: end Chris@1296: #Check for decimal separator Chris@1296: if (sprintf('%.1f',1.0)!='1.0') Chris@1296: setlocale(LC_NUMERIC,'C'); Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Return fonts path Chris@1296: # @access protected Chris@1296: # Chris@1296: def getfontpath(file) Chris@1296: # Is it in the @@font_path? Chris@1296: if @@font_path Chris@1296: fpath = File.join @@font_path, file Chris@1296: if File.exists?(fpath) Chris@1296: return fpath Chris@1296: end Chris@1296: end Chris@1296: # Is it in this plugin's font folder? Chris@1296: fpath = File.join File.dirname(__FILE__), 'fonts', file Chris@1296: if File.exists?(fpath) Chris@1296: return fpath Chris@1296: end Chris@1296: # Could not find it. Chris@1296: nil Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Start document Chris@1296: # @access protected Chris@1296: # Chris@1296: def begindoc() Chris@1296: #Start document Chris@1296: @state=1; Chris@1296: out('%PDF-1.3'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putpages Chris@1296: # @access protected Chris@1296: # Chris@1296: def putpages() Chris@1296: nb = @page; Chris@1296: if (@alias_nb_pages) Chris@1296: nbstr = UTF8ToUTF16BE(nb.to_s, false); Chris@1296: #Replace number of pages Chris@1296: 1.upto(nb) do |n| Chris@1296: @pages[n].gsub!(@alias_nb_pages, nbstr) Chris@1296: end Chris@1296: end Chris@1296: if @def_orientation=='P' Chris@1296: w_pt=@fw_pt Chris@1296: h_pt=@fh_pt Chris@1296: else Chris@1296: w_pt=@fh_pt Chris@1296: h_pt=@fw_pt Chris@1296: end Chris@1296: filter=(@compress) ? '/Filter /FlateDecode ' : '' Chris@1296: 1.upto(nb) do |n| Chris@1296: #Page Chris@1296: newobj Chris@1296: out('<>>>'; Chris@1296: else Chris@1296: l=@links[pl[4]]; Chris@1296: h=!@orientation_changes[l[0]].nil? ? w_pt : h_pt; Chris@1296: annots<>',1+2*l[0], h-l[1]*@k); Chris@1296: end Chris@1296: end Chris@1296: out(annots + ']'); Chris@1296: end Chris@1296: out('/Contents ' + (@n+1).to_s + ' 0 R>>'); Chris@1296: out('endobj'); Chris@1296: #Page content Chris@1296: p=(@compress) ? gzcompress(@pages[n]) : @pages[n]; Chris@1296: newobj(); Chris@1296: out('<<' + filter + '/Length '+ p.length.to_s + '>>'); Chris@1296: putstream(p); Chris@1296: out('endobj'); Chris@1296: end Chris@1296: #Pages root Chris@1296: @offsets[1]=@buffer.length; Chris@1296: out('1 0 obj'); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Adds fonts Chris@1296: # putfonts Chris@1296: # @access protected Chris@1296: # Chris@1296: def putfonts() Chris@1296: nf=@n; Chris@1296: @diffs.each do |diff| Chris@1296: #Encodings Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: end Chris@1296: @font_files.each do |file, info| Chris@1296: #Font file embedding Chris@1296: newobj(); Chris@1296: @font_files[file]['n']=@n; Chris@1296: font=''; Chris@1296: open(getfontpath(file),'rb') do |f| Chris@1296: font = f.read(); Chris@1296: end Chris@1296: compressed=(file[-2,2]=='.z'); Chris@1296: if (!compressed && !info['length2'].nil?) Chris@1296: header=((font[0][0])==128); Chris@1296: if (header) Chris@1296: #Strip first binary header Chris@1296: font=font[6]; Chris@1296: end Chris@1296: if header && (font[info['length1']][0] == 128) Chris@1296: #Strip second binary header Chris@1296: font=font[0..info['length1']] + font[info['length1']+6]; Chris@1296: end Chris@1296: end Chris@1296: out('<>'); Chris@1296: open(getfontpath(file),'rb') do |f| Chris@1296: putstream(font) Chris@1296: end Chris@1296: out('endobj'); Chris@1296: end Chris@1296: @fonts.each do |k, font| Chris@1296: #Font objects Chris@1296: @fonts[k]['n']=@n+1; Chris@1296: type = font['type']; Chris@1296: name = font['name']; Chris@1296: if (type=='core') Chris@1296: #Standard font Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: elsif type == 'Type0' Chris@1296: putType0(font) Chris@1296: elsif (type=='Type1' || type=='TrueType') Chris@1296: #Additional Type1 or TrueType font Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: #Widths Chris@1296: newobj(); Chris@1296: cw=font['cw']; # & Chris@1296: s='['; Chris@1296: 32.upto(255) do |i| Chris@1296: s << cw[i.chr] + ' '; Chris@1296: end Chris@1296: out(s + ']'); Chris@1296: out('endobj'); Chris@1296: #Descriptor Chris@1296: newobj(); Chris@1296: s='<>'); Chris@1296: out('endobj'); Chris@1296: else Chris@1296: #Allow for additional types Chris@1296: mtd='put' + type.downcase; Chris@1296: if (!self.respond_to?(mtd)) Chris@1296: Error('Unsupported font type: ' + type) Chris@1296: else Chris@1296: self.send(mtd,font) Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: def putType0(font) Chris@1296: #Type0 Chris@1296: newobj(); Chris@1296: out('<>') Chris@1296: out('endobj') Chris@1296: #CIDFont Chris@1296: newobj() Chris@1296: out('<>') Chris@1296: out('/FontDescriptor '+(@n+1).to_s+' 0 R') Chris@1296: w='/W [1 [' Chris@1296: font['cw'].keys.sort.each {|key| Chris@1296: w+=font['cw'][key].to_s + " " Chris@1296: # ActionController::Base::logger.debug key.to_s Chris@1296: # ActionController::Base::logger.debug font['cw'][key].to_s Chris@1296: } Chris@1296: out(w+'] 231 325 500 631 [500] 326 389 500]') Chris@1296: out('>>') Chris@1296: out('endobj') Chris@1296: #Font descriptor Chris@1296: newobj() Chris@1296: out('<>') Chris@1296: out('endobj') Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putimages Chris@1296: # @access protected Chris@1296: # Chris@1296: def putimages() Chris@1296: filter=(@compress) ? '/Filter /FlateDecode ' : ''; Chris@1296: @images.each do |file, info| # was while(list(file, info)=each(@images)) Chris@1296: newobj(); Chris@1296: @images[file]['n']=@n; Chris@1296: out('<>'); Chris@1296: putstream(info['data']); Chris@1296: @images[file]['data']=nil Chris@1296: out('endobj'); Chris@1296: #Palette Chris@1296: if (info['cs']=='Indexed') Chris@1296: newobj(); Chris@1296: pal=(@compress) ? gzcompress(info['pal']) : info['pal']; Chris@1296: out('<<' + filter + '/Length ' + pal.length.to_s + '>>'); Chris@1296: putstream(pal); Chris@1296: out('endobj'); Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putxobjectdict Chris@1296: # @access protected Chris@1296: # Chris@1296: def putxobjectdict() Chris@1296: @images.each_value do |image| Chris@1296: out('/I' + image['i'].to_s + ' ' + image['n'].to_s + ' 0 R'); Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putresourcedict Chris@1296: # @access protected Chris@1296: # Chris@1296: def putresourcedict() Chris@1296: out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); Chris@1296: out('/Font <<'); Chris@1296: @fonts.each_value do |font| Chris@1296: out('/F' + font['i'].to_s + ' ' + font['n'].to_s + ' 0 R'); Chris@1296: end Chris@1296: out('>>'); Chris@1296: out('/XObject <<'); Chris@1296: putxobjectdict(); Chris@1296: out('>>'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putresources Chris@1296: # @access protected Chris@1296: # Chris@1296: def putresources() Chris@1296: putfonts(); Chris@1296: putimages(); Chris@1296: #Resource dictionary Chris@1296: @offsets[2]=@buffer.length; Chris@1296: out('2 0 obj'); Chris@1296: out('<<'); Chris@1296: putresourcedict(); Chris@1296: out('>>'); Chris@1296: out('endobj'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putinfo Chris@1296: # @access protected Chris@1296: # Chris@1296: def putinfo() Chris@1296: out('/Producer ' + textstring(PDF_PRODUCER)); Chris@1296: if (!@title.nil?) Chris@1296: out('/Title ' + textstring(@title)); Chris@1296: end Chris@1296: if (!@subject.nil?) Chris@1296: out('/Subject ' + textstring(@subject)); Chris@1296: end Chris@1296: if (!@author.nil?) Chris@1296: out('/Author ' + textstring(@author)); Chris@1296: end Chris@1296: if (!@keywords.nil?) Chris@1296: out('/Keywords ' + textstring(@keywords)); Chris@1296: end Chris@1296: if (!@creator.nil?) Chris@1296: out('/Creator ' + textstring(@creator)); Chris@1296: end Chris@1296: out('/CreationDate ' + textstring('D:' + Time.now.strftime('%Y%m%d%H%M%S'))); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putcatalog Chris@1296: # @access protected Chris@1296: # Chris@1296: def putcatalog() Chris@1296: out('/Type /Catalog'); Chris@1296: out('/Pages 1 0 R'); Chris@1296: if (@zoom_mode=='fullpage') Chris@1296: out('/OpenAction [3 0 R /Fit]'); Chris@1296: elsif (@zoom_mode=='fullwidth') Chris@1296: out('/OpenAction [3 0 R /FitH null]'); Chris@1296: elsif (@zoom_mode=='real') Chris@1296: out('/OpenAction [3 0 R /XYZ null null 1]'); Chris@1296: elsif (!@zoom_mode.is_a?(String)) Chris@1296: out('/OpenAction [3 0 R /XYZ null null ' + (@zoom_mode/100) + ']'); Chris@1296: end Chris@1296: if (@layout_mode=='single') Chris@1296: out('/PageLayout /SinglePage'); Chris@1296: elsif (@layout_mode=='continuous') Chris@1296: out('/PageLayout /OneColumn'); Chris@1296: elsif (@layout_mode=='two') Chris@1296: out('/PageLayout /TwoColumnLeft'); Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # puttrailer Chris@1296: # @access protected Chris@1296: # Chris@1296: def puttrailer() Chris@1296: out('/Size ' + (@n+1).to_s); Chris@1296: out('/Root ' + @n.to_s + ' 0 R'); Chris@1296: out('/Info ' + (@n-1).to_s + ' 0 R'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # putheader Chris@1296: # @access protected Chris@1296: # Chris@1296: def putheader() Chris@1296: out('%PDF-' + @pdf_version); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # enddoc Chris@1296: # @access protected Chris@1296: # Chris@1296: def enddoc() Chris@1296: putheader(); Chris@1296: putpages(); Chris@1296: putresources(); Chris@1296: #Info Chris@1296: newobj(); Chris@1296: out('<<'); Chris@1296: putinfo(); Chris@1296: out('>>'); Chris@1296: out('endobj'); Chris@1296: #Catalog Chris@1296: newobj(); Chris@1296: out('<<'); Chris@1296: putcatalog(); Chris@1296: out('>>'); Chris@1296: out('endobj'); Chris@1296: #Cross-ref Chris@1296: o=@buffer.length; Chris@1296: out('xref'); Chris@1296: out('0 ' + (@n+1).to_s); Chris@1296: out('0000000000 65535 f '); Chris@1296: 1.upto(@n) do |i| Chris@1296: out(sprintf('%010d 00000 n ',@offsets[i])); Chris@1296: end Chris@1296: #Trailer Chris@1296: out('trailer'); Chris@1296: out('<<'); Chris@1296: puttrailer(); Chris@1296: out('>>'); Chris@1296: out('startxref'); Chris@1296: out(o); Chris@1296: out('%%EOF'); Chris@1296: @state=3; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # beginpage Chris@1296: # @access protected Chris@1296: # Chris@1296: def beginpage(orientation) Chris@1296: @page += 1; Chris@1296: @pages[@page]=''; Chris@1296: @state=2; Chris@1296: @x=@l_margin; Chris@1296: @y=@t_margin; Chris@1296: @font_family=''; Chris@1296: #Page orientation Chris@1296: if (orientation.empty?) Chris@1296: orientation=@def_orientation; Chris@1296: else Chris@1296: orientation.upcase! Chris@1296: if (orientation!=@def_orientation) Chris@1296: @orientation_changes[@page]=true; Chris@1296: end Chris@1296: end Chris@1296: if (orientation!=@cur_orientation) Chris@1296: #Change orientation Chris@1296: if (orientation=='P') Chris@1296: @w_pt=@fw_pt; Chris@1296: @h_pt=@fh_pt; Chris@1296: @w=@fw; Chris@1296: @h=@fh; Chris@1296: else Chris@1296: @w_pt=@fh_pt; Chris@1296: @h_pt=@fw_pt; Chris@1296: @w=@fh; Chris@1296: @h=@fw; Chris@1296: end Chris@1296: @page_break_trigger=@h-@b_margin; Chris@1296: @cur_orientation = orientation; Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # End of page contents Chris@1296: # @access protected Chris@1296: # Chris@1296: def endpage() Chris@1296: @state=1; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Begin a new object Chris@1296: # @access protected Chris@1296: # Chris@1296: def newobj() Chris@1296: @n += 1; Chris@1296: @offsets[@n]=@buffer.length; Chris@1296: out(@n.to_s + ' 0 obj'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Underline and Deleted text Chris@1296: # @access protected Chris@1296: # Chris@1296: def dolinetxt(x, y, txt) Chris@1296: up = @current_font['up']; Chris@1296: ut = @current_font['ut']; Chris@1296: w = GetStringWidth(txt) + @ws * txt.count(' '); Chris@1296: sprintf('%.2f %.2f %.2f %.2f re f', x * @k, (@h - (y - up / 1000.0 * @font_size)) * @k, w * @k, -ut / 1000.0 * @font_size_pt); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Extract info from a JPEG file Chris@1296: # @access protected Chris@1296: # Chris@1296: def parsejpg(file) Chris@1296: a=getimagesize(file); Chris@1296: if (a.empty?) Chris@1296: Error('Missing or incorrect image file: ' + file); Chris@1296: end Chris@1296: if (!a[2].nil? and a[2]!='JPEG') Chris@1296: Error('Not a JPEG file: ' + file); Chris@1296: end Chris@1296: if (a['channels'].nil? or a['channels']==3) Chris@1296: colspace='DeviceRGB'; Chris@1296: elsif (a['channels']==4) Chris@1296: colspace='DeviceCMYK'; Chris@1296: else Chris@1296: colspace='DeviceGray'; Chris@1296: end Chris@1296: bpc=!a['bits'].nil? ? a['bits'] : 8; Chris@1296: #Read whole file Chris@1296: data=''; Chris@1296: Chris@1296: open(file,'rb') do |f| Chris@1296: data< a[0],'h' => a[1],'cs' => colspace,'bpc' => bpc,'f'=>'DCTDecode','data' => data} Chris@1296: end Chris@1296: Chris@1296: def imageToPNG(file) Chris@1296: return unless Object.const_defined?(:Magick) Chris@1296: Chris@1296: img = Magick::ImageList.new(file) Chris@1296: img.format = 'PNG' # convert to PNG from gif Chris@1296: img.opacity = 0 # PNG alpha channel delete Chris@1296: Chris@1296: #use a temporary file.... Chris@1296: tmpFile = Tempfile.new(['', '_' + File::basename(file) + '.png'], @@k_path_cache); Chris@1296: tmpFile.binmode Chris@1296: tmpFile.print img.to_blob Chris@1296: tmpFile Chris@1296: ensure Chris@1296: tmpFile.close Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Extract info from a PNG file Chris@1296: # @access protected Chris@1296: # Chris@1296: def parsepng(file) Chris@1296: f=open(file,'rb'); Chris@1296: #Check signature Chris@1296: if (f.read(8)!=137.chr + 'PNG' + 13.chr + 10.chr + 26.chr + 10.chr) Chris@1296: Error('Not a PNG file: ' + file); Chris@1296: end Chris@1296: #Read header chunk Chris@1296: f.read(4); Chris@1296: if (f.read(4)!='IHDR') Chris@1296: Error('Incorrect PNG file: ' + file); Chris@1296: end Chris@1296: w=freadint(f); Chris@1296: h=freadint(f); Chris@1296: bpc=f.read(1).unpack('C')[0]; Chris@1296: if (bpc>8) Chris@1296: Error('16-bit depth not supported: ' + file); Chris@1296: end Chris@1296: ct=f.read(1).unpack('C')[0]; Chris@1296: if (ct==0) Chris@1296: colspace='DeviceGray'; Chris@1296: elsif (ct==2) Chris@1296: colspace='DeviceRGB'; Chris@1296: elsif (ct==3) Chris@1296: colspace='Indexed'; Chris@1296: else Chris@1296: Error('Alpha channel not supported: ' + file); Chris@1296: end Chris@1296: if (f.read(1).unpack('C')[0] != 0) Chris@1296: Error('Unknown compression method: ' + file); Chris@1296: end Chris@1296: if (f.read(1).unpack('C')[0] != 0) Chris@1296: Error('Unknown filter method: ' + file); Chris@1296: end Chris@1296: if (f.read(1).unpack('C')[0] != 0) Chris@1296: Error('Interlacing not supported: ' + file); Chris@1296: end Chris@1296: f.read(4); Chris@1296: parms='/DecodeParms <>'; Chris@1296: #Scan chunks looking for palette, transparency and image data Chris@1296: pal=''; Chris@1296: trns=''; Chris@1296: data=''; Chris@1296: begin Chris@1296: n=freadint(f); Chris@1296: type=f.read(4); Chris@1296: if (type=='PLTE') Chris@1296: #Read palette Chris@1296: pal=f.read( n); Chris@1296: f.read(4); Chris@1296: elsif (type=='tRNS') Chris@1296: #Read transparency info Chris@1296: t=f.read( n); Chris@1296: if (ct==0) Chris@1296: trns = t[1].unpack('C')[0] Chris@1296: elsif (ct==2) Chris@1296: trns = t[[1].unpack('C')[0], t[3].unpack('C')[0], t[5].unpack('C')[0]] Chris@1296: else Chris@1296: pos=t.index(0.chr); Chris@1296: unless (pos.nil?) Chris@1296: trns = [pos] Chris@1296: end Chris@1296: end Chris@1296: f.read(4); Chris@1296: elsif (type=='IDAT') Chris@1296: #Read image data block Chris@1296: data< w, 'h' => h, 'cs' => colspace, 'bpc' => bpc, 'f'=>'FlateDecode', 'parms' => parms, 'pal' => pal, 'trns' => trns, 'data' => data} Chris@1296: ensure Chris@1296: f.close Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Read a 4-byte integer from file Chris@1296: # @access protected Chris@1296: # Chris@1296: def freadint(f) Chris@1296: # Read a 4-byte integer from file Chris@1296: a = f.read(4).unpack('N') Chris@1296: return a[0] Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Format a text string Chris@1296: # @access protected Chris@1296: # Chris@1296: def textstring(s) Chris@1296: if (@is_unicode) Chris@1296: #Convert string to UTF-16BE Chris@1296: s = UTF8ToUTF16BE(s, true); Chris@1296: end Chris@1296: return '(' + escape(s) + ')'; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Format a text string Chris@1296: # @access protected Chris@1296: # Chris@1296: def escapetext(s) Chris@1296: if (@is_unicode) Chris@1296: #Convert string to UTF-16BE Chris@1296: s = UTF8ToUTF16BE(s, false); Chris@1296: end Chris@1296: return escape(s); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Add \ before \, ( and ) Chris@1296: # @access protected Chris@1296: # Chris@1296: def escape(s) Chris@1296: # Add \ before \, ( and ) Chris@1296: s.gsub('\\','\\\\\\').gsub('(','\\(').gsub(')','\\)').gsub(13.chr, '\r') Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Chris@1296: # @access protected Chris@1296: # Chris@1296: def putstream(s) Chris@1296: out('stream'); Chris@1296: out(s); Chris@1296: out('endstream'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Add a line to the document Chris@1296: # @access protected Chris@1296: # Chris@1296: def out(s) Chris@1296: if (@state==2) Chris@1296: @pages[@page] << s.to_s + "\n"; Chris@1296: else Chris@1296: @buffer << s.to_s + "\n"; Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Adds unicode fonts.
Chris@1296: # Based on PDF Reference 1.3 (section 5) Chris@1296: # @access protected Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.52.0.TC005 (2005-01-05) Chris@1296: # Chris@1296: def puttruetypeunicode(font) Chris@1296: # Type0 Font Chris@1296: # A composite font composed of other fonts, organized hierarchically Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: Chris@1296: # CIDFontType2 Chris@1296: # A CIDFont whose glyph descriptions are based on TrueType font technology Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: Chris@1296: # ToUnicode Chris@1296: # is a stream object that contains the definition of the CMap Chris@1296: # (PDF Reference 1.3 chap. 5.9) Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('stream'); Chris@1296: out('/CIDInit /ProcSet findresource begin'); Chris@1296: out('12 dict begin'); Chris@1296: out('begincmap'); Chris@1296: out('/CIDSystemInfo'); Chris@1296: out('<> def'); Chris@1296: out('/CMapName /Adobe-Identity-UCS def'); Chris@1296: out('/CMapType 2 def'); Chris@1296: out('1 begincodespacerange'); Chris@1296: out('<0000> '); Chris@1296: out('endcodespacerange'); Chris@1296: out('1 beginbfrange'); Chris@1296: out('<0000> <0000>'); Chris@1296: out('endbfrange'); Chris@1296: out('endcmap'); Chris@1296: out('CMapName currentdict /CMap defineresource pop'); Chris@1296: out('end'); Chris@1296: out('end'); Chris@1296: out('endstream'); Chris@1296: out('endobj'); Chris@1296: Chris@1296: # CIDSystemInfo dictionary Chris@1296: # A dictionary containing entries that define the character collection of the CIDFont. Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: Chris@1296: # Font descriptor Chris@1296: # A font descriptor describing the CIDFont default metrics other than its glyph widths Chris@1296: newobj(); Chris@1296: out('<>'); Chris@1296: out('endobj'); Chris@1296: Chris@1296: # Embed CIDToGIDMap Chris@1296: # A specification of the mapping from CIDs to glyph indices Chris@1296: newobj(); Chris@1296: ctgfile = getfontpath(font['ctg']) Chris@1296: if (!ctgfile) Chris@1296: Error('Font file not found: ' + ctgfile); Chris@1296: end Chris@1296: size = File.size(ctgfile); Chris@1296: out('<>'); Chris@1296: open(ctgfile, "rb") do |f| Chris@1296: putstream(f.read()) Chris@1296: end Chris@1296: out('endobj'); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Converts UTF-8 strings to codepoints array.
Chris@1296: # Invalid byte sequences will be replaced with 0xFFFD (replacement character)
Chris@1296: # Based on: http://www.faqs.org/rfcs/rfc3629.html Chris@1296: #
Chris@1296: 	# 	  Char. number range  |        UTF-8 octet sequence
Chris@1296: 	#       (hexadecimal)    |              (binary)
Chris@1296: 	#    --------------------+-----------------------------------------------
Chris@1296: 	#    0000 0000-0000 007F | 0xxxxxxx
Chris@1296: 	#    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
Chris@1296: 	#    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
Chris@1296: 	#    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Chris@1296: 	#    ---------------------------------------------------------------------
Chris@1296: 	#
Chris@1296: 	#   ABFN notation:
Chris@1296: 	#   ---------------------------------------------------------------------
Chris@1296: 	#   UTF8-octets =#( UTF8-char )
Chris@1296: 	#   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
Chris@1296: 	#   UTF8-1      = %x00-7F
Chris@1296: 	#   UTF8-2      = %xC2-DF UTF8-tail
Chris@1296: 	#
Chris@1296: 	#   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
Chris@1296: 	#                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
Chris@1296: 	#   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
Chris@1296: 	#                 %xF4 %x80-8F 2( UTF8-tail )
Chris@1296: 	#   UTF8-tail   = %x80-BF
Chris@1296: 	#   ---------------------------------------------------------------------
Chris@1296: 	# 
Chris@1296: # @param string :str string to process. Chris@1296: # @return array containing codepoints (UTF-8 characters values) Chris@1296: # @access protected Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.53.0.TC005 (2005-01-05) Chris@1296: # Chris@1296: def UTF8StringToArray(str) Chris@1296: if (!@is_unicode) Chris@1296: return str; # string is not in unicode Chris@1296: end Chris@1296: Chris@1296: unicode = [] # array containing unicode values Chris@1296: bytes = [] # array containing single character byte sequences Chris@1296: numbytes = 1; # number of octetc needed to represent the UTF-8 character Chris@1296: Chris@1296: str = str.to_s; # force :str to be a string Chris@1296: Chris@1296: str.each_byte do |char| Chris@1296: if (bytes.length == 0) # get starting octect Chris@1296: if (char <= 0x7F) Chris@1296: unicode << char # use the character "as is" because is ASCII Chris@1296: numbytes = 1 Chris@1296: elsif ((char >> 0x05) == 0x06) # 2 bytes character (0x06 = 110 BIN) Chris@1296: bytes << ((char - 0xC0) << 0x06) Chris@1296: numbytes = 2 Chris@1296: elsif ((char >> 0x04) == 0x0E) # 3 bytes character (0x0E = 1110 BIN) Chris@1296: bytes << ((char - 0xE0) << 0x0C) Chris@1296: numbytes = 3 Chris@1296: elsif ((char >> 0x03) == 0x1E) # 4 bytes character (0x1E = 11110 BIN) Chris@1296: bytes << ((char - 0xF0) << 0x12) Chris@1296: numbytes = 4 Chris@1296: else Chris@1296: # use replacement character for other invalid sequences Chris@1296: unicode << 0xFFFD Chris@1296: bytes = [] Chris@1296: numbytes = 1 Chris@1296: end Chris@1296: elsif ((char >> 0x06) == 0x02) # bytes 2, 3 and 4 must start with 0x02 = 10 BIN Chris@1296: bytes << (char - 0x80) Chris@1296: if (bytes.length == numbytes) Chris@1296: # compose UTF-8 bytes to a single unicode value Chris@1296: char = bytes[0] Chris@1296: 1.upto(numbytes-1) do |j| Chris@1296: char += (bytes[j] << ((numbytes - j - 1) * 0x06)) Chris@1296: end Chris@1296: if (((char >= 0xD800) and (char <= 0xDFFF)) or (char >= 0x10FFFF)) Chris@1296: # The definition of UTF-8 prohibits encoding character numbers between Chris@1296: # U+D800 and U+DFFF, which are reserved for use with the UTF-16 Chris@1296: # encoding form (as surrogate pairs) and do not directly represent Chris@1296: # characters Chris@1296: unicode << 0xFFFD; # use replacement character Chris@1296: else Chris@1296: unicode << char; # add char to array Chris@1296: end Chris@1296: # reset data for next char Chris@1296: bytes = [] Chris@1296: numbytes = 1; Chris@1296: end Chris@1296: else Chris@1296: # use replacement character for other invalid sequences Chris@1296: unicode << 0xFFFD; Chris@1296: bytes = [] Chris@1296: numbytes = 1; Chris@1296: end Chris@1296: end Chris@1296: return unicode; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Converts UTF-8 strings to UTF16-BE.
Chris@1296: # Based on: http://www.faqs.org/rfcs/rfc2781.html Chris@1296: #
Chris@1296: 	#   Encoding UTF-16:
Chris@1296: 	# 
Chris@1296: 		#   Encoding of a single character from an ISO 10646 character value to
Chris@1296: 	#    UTF-16 proceeds as follows. Let U be the character number, no greater
Chris@1296: 	#    than 0x10FFFF.
Chris@1296: 	# 
Chris@1296: 	#    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
Chris@1296: 	#       terminate.
Chris@1296: 	# 
Chris@1296: 	#    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
Chris@1296: 	#       U' must be less than or equal to 0xFFFFF. That is, U' can be
Chris@1296: 	#       represented in 20 bits.
Chris@1296: 	# 
Chris@1296: 	#    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
Chris@1296: 	#       0xDC00, respectively. These integers each have 10 bits free to
Chris@1296: 	#       encode the character value, for a total of 20 bits.
Chris@1296: 	# 
Chris@1296: 	#    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
Chris@1296: 	#       bits of W1 and the 10 low-order bits of U' to the 10 low-order
Chris@1296: 	#       bits of W2. Terminate.
Chris@1296: 	# 
Chris@1296: 	#    Graphically, steps 2 through 4 look like:
Chris@1296: 	#    U' = yyyyyyyyyyxxxxxxxxxx
Chris@1296: 	#    W1 = 110110yyyyyyyyyy
Chris@1296: 	#    W2 = 110111xxxxxxxxxx
Chris@1296: 	# 
Chris@1296: # @param string :str string to process. Chris@1296: # @param boolean :setbom if true set the Byte Order Mark (BOM = 0xFEFF) Chris@1296: # @return string Chris@1296: # @access protected Chris@1296: # @author Nicola Asuni Chris@1296: # @since 1.53.0.TC005 (2005-01-05) Chris@1296: # @uses UTF8StringToArray Chris@1296: # Chris@1296: def UTF8ToUTF16BE(str, setbom=true) Chris@1296: if (!@is_unicode) Chris@1296: return str; # string is not in unicode Chris@1296: end Chris@1296: outstr = ""; # string to be returned Chris@1296: unicode = UTF8StringToArray(str); # array containing UTF-8 unicode values Chris@1296: numitems = unicode.length; Chris@1296: Chris@1296: if (setbom) Chris@1296: outstr << "\xFE\xFF"; # Byte Order Mark (BOM) Chris@1296: end Chris@1296: unicode.each do |char| Chris@1296: if (char == 0xFFFD) Chris@1296: outstr << "\xFF\xFD"; # replacement character Chris@1296: elsif (char < 0x10000) Chris@1296: outstr << (char >> 0x08).chr; Chris@1296: outstr << (char & 0xFF).chr; Chris@1296: else Chris@1296: char -= 0x10000; Chris@1296: w1 = 0xD800 | (char >> 0x10); Chris@1296: w2 = 0xDC00 | (char & 0x3FF); Chris@1296: outstr << (w1 >> 0x08).chr; Chris@1296: outstr << (w1 & 0xFF).chr; Chris@1296: outstr << (w2 >> 0x08).chr; Chris@1296: outstr << (w2 & 0xFF).chr; Chris@1296: end Chris@1296: end Chris@1296: return outstr; Chris@1296: end Chris@1296: Chris@1296: # ==================================================== Chris@1296: Chris@1296: # Chris@1296: # Set header font. Chris@1296: # @param array :font font Chris@1296: # @since 1.1 Chris@1296: # Chris@1296: def SetHeaderFont(font) Chris@1296: @header_font = font; Chris@1296: end Chris@1296: alias_method :set_header_font, :SetHeaderFont Chris@1296: Chris@1296: # Chris@1296: # Set footer font. Chris@1296: # @param array :font font Chris@1296: # @since 1.1 Chris@1296: # Chris@1296: def SetFooterFont(font) Chris@1296: @footer_font = font; Chris@1296: end Chris@1296: alias_method :set_footer_font, :SetFooterFont Chris@1296: Chris@1296: # Chris@1296: # Set language array. Chris@1296: # @param array :language Chris@1296: # @since 1.1 Chris@1296: # Chris@1296: def SetLanguageArray(language) Chris@1296: @l = language; Chris@1296: end Chris@1296: alias_method :set_language_array, :SetLanguageArray Chris@1296: # Chris@1296: # Set document barcode. Chris@1296: # @param string :bc barcode Chris@1296: # Chris@1296: def SetBarcode(bc="") Chris@1296: @barcode = bc; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Print Barcode. Chris@1296: # @param int :x x position in user units Chris@1296: # @param int :y y position in user units Chris@1296: # @param int :w width in user units Chris@1296: # @param int :h height position in user units Chris@1296: # @param string :type type of barcode (I25, C128A, C128B, C128C, C39) Chris@1296: # @param string :style barcode style Chris@1296: # @param string :font font for text Chris@1296: # @param int :xres x resolution Chris@1296: # @param string :code code to print Chris@1296: # Chris@1296: def writeBarcode(x, y, w, h, type, style, font, xres, code) Chris@1296: require(File.dirname(__FILE__) + "/barcode/barcode.rb"); Chris@1296: require(File.dirname(__FILE__) + "/barcode/i25object.rb"); Chris@1296: require(File.dirname(__FILE__) + "/barcode/c39object.rb"); Chris@1296: require(File.dirname(__FILE__) + "/barcode/c128aobject.rb"); Chris@1296: require(File.dirname(__FILE__) + "/barcode/c128bobject.rb"); Chris@1296: require(File.dirname(__FILE__) + "/barcode/c128cobject.rb"); Chris@1296: Chris@1296: if (code.empty?) Chris@1296: return; Chris@1296: end Chris@1296: Chris@1296: if (style.empty?) Chris@1296: style = BCS_ALIGN_LEFT; Chris@1296: style |= BCS_IMAGE_PNG; Chris@1296: style |= BCS_TRANSPARENT; Chris@1296: #:style |= BCS_BORDER; Chris@1296: #:style |= BCS_DRAW_TEXT; Chris@1296: #:style |= BCS_STRETCH_TEXT; Chris@1296: #:style |= BCS_REVERSE_COLOR; Chris@1296: end Chris@1296: if (font.empty?) then font = BCD_DEFAULT_FONT; end Chris@1296: if (xres.empty?) then xres = BCD_DEFAULT_XRES; end Chris@1296: Chris@1296: scale_factor = 1.5 * xres * @k; Chris@1296: bc_w = (w * scale_factor).round #width in points Chris@1296: bc_h = (h * scale_factor).round #height in points Chris@1296: Chris@1296: case (type.upcase) Chris@1296: when "I25" Chris@1296: obj = I25Object.new(bc_w, bc_h, style, code); Chris@1296: when "C128A" Chris@1296: obj = C128AObject.new(bc_w, bc_h, style, code); Chris@1296: when "C128B" Chris@1296: obj = C128BObject.new(bc_w, bc_h, style, code); Chris@1296: when "C128C" Chris@1296: obj = C128CObject.new(bc_w, bc_h, style, code); Chris@1296: when "C39" Chris@1296: obj = C39Object.new(bc_w, bc_h, style, code); Chris@1296: end Chris@1296: Chris@1296: obj.SetFont(font); Chris@1296: obj.DrawObject(xres); Chris@1296: Chris@1296: #use a temporary file.... Chris@1296: tmpName = tempnam(@@k_path_cache,'img'); Chris@1296: imagepng(obj.getImage(), tmpName); Chris@1296: Image(tmpName, x, y, w, h, 'png'); Chris@1296: obj.DestroyObject(); Chris@1296: obj = nil Chris@1296: unlink(tmpName); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Returns the PDF data. Chris@1296: # Chris@1296: def GetPDFData() Chris@1296: if (@state < 3) Chris@1296: Close(); Chris@1296: end Chris@1296: return @buffer; Chris@1296: end Chris@1296: Chris@1296: # --- HTML PARSER FUNCTIONS --- Chris@1296: Chris@1296: # Chris@1296: # Allows to preserve some HTML formatting.
Chris@1296: # Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, ins, del, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small Chris@1296: # @param string :html text to display Chris@1296: # @param boolean :ln if true add a new line after text (default = true) Chris@1296: # @param int :fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # Chris@1296: def writeHTML(html, ln=true, fill=0, h=0) Chris@1296: Chris@1296: @lasth = h if h > 0 Chris@1296: if (@lasth == 0) Chris@1296: #set row height Chris@1296: @lasth = @font_size * @@k_cell_height_ratio; Chris@1296: end Chris@1296: Chris@1296: @href = nil Chris@1296: @style = ""; Chris@1296: @t_cells = [[]]; Chris@1296: @table_id = 0; Chris@1296: Chris@1296: # pre calculate Chris@1296: html.split(/(<[^>]+>)/).each do |element| Chris@1296: if "<" == element[0,1] Chris@1296: #Tag Chris@1296: if (element[1, 1] == '/') Chris@1296: closedHTMLTagCalc(element[2..-2].downcase); Chris@1296: else Chris@1296: #Extract attributes Chris@1296: # get tag name Chris@1296: tag = element.scan(/([a-zA-Z0-9]*)/).flatten.delete_if {|x| x.length == 0} Chris@1296: tag = tag[0].to_s.downcase; Chris@1296: Chris@1296: # get attributes Chris@1296: attr_array = element.scan(/([^=\s]*)=["\']?([^"\']*)["\']?/) Chris@1296: attrs = {} Chris@1296: attr_array.each do |name, value| Chris@1296: attrs[name.downcase] = value; Chris@1296: end Chris@1296: openHTMLTagCalc(tag, attrs); Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: @table_id = 0; Chris@1296: Chris@1296: html.split(/(<[A-Za-z!?\/][^>]*?>)/).each do |element| Chris@1296: if "<" == element[0,1] Chris@1296: #Tag Chris@1296: if (element[1, 1] == '/') Chris@1296: closedHTMLTagHandler(element[2..-2].downcase); Chris@1296: else Chris@1296: #Extract attributes Chris@1296: # get tag name Chris@1296: tag = element.scan(/([a-zA-Z0-9]*)/).flatten.delete_if {|x| x.length == 0} Chris@1296: tag = tag[0].to_s.downcase; Chris@1296: Chris@1296: # get attributes Chris@1296: attr_array = element.scan(/([^=\s]*)=["\']?([^"\']*)["\']?/) Chris@1296: attrs = {} Chris@1296: attr_array.each do |name, value| Chris@1296: attrs[name.downcase] = value; Chris@1296: end Chris@1296: openHTMLTagHandler(tag, attrs, fill); Chris@1296: end Chris@1296: Chris@1296: else Chris@1296: #Text Chris@1296: if (@tdbegin) Chris@1296: element.gsub!(/[\t\r\n\f]/, ""); Chris@1296: @tdtext << element.gsub(/ /, " "); Chris@1296: elsif (@href) Chris@1296: element.gsub!(/[\t\r\n\f]/, ""); Chris@1296: addHtmlLink(@href, element, fill); Chris@1296: elsif (@pre_state == true and element.length > 0) Chris@1296: Write(@lasth, unhtmlentities(element), '', fill); Chris@1296: elsif (element.strip.length > 0) Chris@1296: element.gsub!(/[\t\r\n\f]/, ""); Chris@1296: element.gsub!(/ /, " "); Chris@1296: Write(@lasth, unhtmlentities(element), '', fill); Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: if (ln) Chris@1296: Ln(@lasth); Chris@1296: end Chris@1296: end Chris@1296: alias_method :write_html, :writeHTML Chris@1296: Chris@1296: # Chris@1296: # Prints a cell (rectangular area) with optional borders, background color and html text string. The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.
Chris@1296: # If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. Chris@1296: # @param float :w Cell width. If 0, the cell extends up to the right margin. Chris@1296: # @param float :h Cell minimum height. The cell extends automatically if needed. Chris@1296: # @param float :x upper-left corner X coordinate Chris@1296: # @param float :y upper-left corner Y coordinate Chris@1296: # @param string :html html text to print. Default value: empty string. Chris@1296: # @param mixed :border Indicates if borders must be drawn around the cell. The value can be either a number:
  • 0: no border (default)
  • 1: frame
or a string containing some or all of the following characters (in any order):
  • L: left
  • T: top
  • R: right
  • B: bottom
Chris@1296: # @param int :ln Indicates where the current position should go after the call. Possible values are:
  • 0: to the right
  • 1: to the beginning of the next line
  • 2: below
Chris@1296: # Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. Chris@1296: # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # @see Cell() Chris@1296: # Chris@1296: def writeHTMLCell(w, h, x, y, html='', border=0, ln=1, fill=0) Chris@1296: Chris@1296: if (@lasth == 0) Chris@1296: #set row height Chris@1296: @lasth = @font_size * @@k_cell_height_ratio; Chris@1296: end Chris@1296: Chris@1296: if (x == 0) Chris@1296: x = GetX(); Chris@1296: end Chris@1296: if (y == 0) Chris@1296: y = GetY(); Chris@1296: end Chris@1296: Chris@1296: # get current page number Chris@1296: pagenum = @page; Chris@1296: Chris@1296: SetX(x); Chris@1296: SetY(y); Chris@1296: Chris@1296: if (w == 0) Chris@1296: w = @fw - x - @r_margin; Chris@1296: end Chris@1296: Chris@1296: b=0; Chris@1296: if (border) Chris@1296: if (border==1) Chris@1296: border='LTRB'; Chris@1296: b='LRT'; Chris@1296: b2='LR'; Chris@1296: elsif border.is_a?(String) Chris@1296: b2=''; Chris@1296: if (border.include?('L')) Chris@1296: b2<<'L'; Chris@1296: end Chris@1296: if (border.include?('R')) Chris@1296: b2<<'R'; Chris@1296: end Chris@1296: b=(border.include?('T')) ? b2 + 'T' : b2; Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # store original margin values Chris@1296: l_margin = @l_margin; Chris@1296: r_margin = @r_margin; Chris@1296: Chris@1296: # set new margin values Chris@1296: SetLeftMargin(x); Chris@1296: SetRightMargin(@fw - x - w); Chris@1296: Chris@1296: # calculate remaining vertical space on page Chris@1296: restspace = GetPageHeight() - GetY() - GetBreakMargin(); Chris@1296: Chris@1296: writeHTML(html, true, fill); # write html text Chris@1296: SetX(x) Chris@1296: Chris@1296: currentY = GetY(); Chris@1296: @auto_page_break = false; Chris@1296: # check if a new page has been created Chris@1296: if (@page > pagenum) Chris@1296: # design a cell around the text on first page Chris@1296: currentpage = @page; Chris@1296: @page = pagenum; Chris@1296: SetY(GetPageHeight() - restspace - GetBreakMargin()); Chris@1296: SetX(x) Chris@1296: Cell(w, restspace - 1, "", b, 0, 'L', 0); Chris@1296: b = b2; Chris@1296: @page += 1; Chris@1296: while @page < currentpage Chris@1296: SetY(@t_margin); # put cursor at the beginning of text Chris@1296: SetX(x) Chris@1296: Cell(w, @page_break_trigger - @t_margin, "", b, 0, 'L', 0); Chris@1296: @page += 1; Chris@1296: end Chris@1296: if (border.is_a?(String) and border.include?('B')) Chris@1296: b<<'B'; Chris@1296: end Chris@1296: # design a cell around the text on last page Chris@1296: SetY(@t_margin); # put cursor at the beginning of text Chris@1296: SetX(x) Chris@1296: Cell(w, currentY - @t_margin, "", b, 0, 'L', 0); Chris@1296: else Chris@1296: SetY(y); # put cursor at the beginning of text Chris@1296: # design a cell around the text Chris@1296: SetX(x) Chris@1296: Cell(w, [h, (currentY - y)].max, "", border, 0, 'L', 0); Chris@1296: end Chris@1296: @auto_page_break = true; Chris@1296: Chris@1296: # restore original margin values Chris@1296: SetLeftMargin(l_margin); Chris@1296: SetRightMargin(r_margin); Chris@1296: Chris@1296: @lasth = h Chris@1296: Chris@1296: # move cursor to specified position Chris@1296: if (ln == 0) Chris@1296: # go to the top-right of the cell Chris@1296: @x = x + w; Chris@1296: @y = y; Chris@1296: elsif (ln == 1) Chris@1296: # go to the beginning of the next line Chris@1296: @x = @l_margin; Chris@1296: @y = currentY; Chris@1296: elsif (ln == 2) Chris@1296: # go to the bottom-left of the cell (below) Chris@1296: @x = x; Chris@1296: @y = currentY; Chris@1296: end Chris@1296: end Chris@1296: alias_method :write_html_cell, :writeHTMLCell Chris@1296: Chris@1296: # Chris@1296: # Check html table tag position. Chris@1296: # Chris@1296: # @param array :table potision array Chris@1296: # @param int :current tr tag id number Chris@1296: # @param int :current td tag id number Chris@1296: # @access private Chris@1296: # @return int : next td_id position. Chris@1296: # value 0 mean that can use position. Chris@1296: # Chris@1296: def checkTableBlockingCellPosition(table, tr_id, td_id ) Chris@1296: 0.upto(tr_id) do |j| Chris@1296: 0.upto(@t_cells[table][j].size - 1) do |i| Chris@1296: if @t_cells[table][j][i]['i0'] <= td_id and td_id <= @t_cells[table][j][i]['i1'] Chris@1296: if @t_cells[table][j][i]['j0'] <= tr_id and tr_id <= @t_cells[table][j][i]['j1'] Chris@1296: return @t_cells[table][j][i]['i1'] - td_id + 1; Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: return 0; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Calculate opening tags. Chris@1296: # Chris@1296: # html table cell array : @t_cells Chris@1296: # Chris@1296: # i0: table cell start position Chris@1296: # i1: table cell end position Chris@1296: # j0: table row start position Chris@1296: # j1: table row end position Chris@1296: # Chris@1296: # +------+ Chris@1296: # |i0,j0 | Chris@1296: # | i1,j1| Chris@1296: # +------+ Chris@1296: # Chris@1296: # example html: Chris@1296: # Chris@1296: # Chris@1296: # Chris@1296: # Chris@1296: # Chris@1296: #
Chris@1296: # Chris@1296: # i: 0 1 2 Chris@1296: # j+----+----+----+ Chris@1296: # :|0,0 |1,0 |2,0 | Chris@1296: # 0| 0,0| 1,0| 2,0| Chris@1296: # +----+----+----+ Chris@1296: # |0,1 |2,1 | Chris@1296: # 1| 1,1| 2,1| Chris@1296: # +----+----+----+ Chris@1296: # |0,2 |1,2 |2,2 | Chris@1296: # 2| | 1,2| 2,2| Chris@1296: # + +----+----+ Chris@1296: # | |1,3 |2,3 | Chris@1296: # 3| 0,3| 1,3| 2,3| Chris@1296: # +----+----+----+ Chris@1296: # Chris@1296: # html table cell array : Chris@1296: # [[[i0=>0,j0=>0,i1=>0,j1=>0],[i0=>1,j0=>0,i1=>1,j1=>0],[i0=>2,j0=>0,i1=>2,j1=>0]], Chris@1296: # [[i0=>0,j0=>1,i1=>1,j1=>1],[i0=>2,j0=>1,i1=>2,j1=>1]], Chris@1296: # [[i0=>0,j0=>2,i1=>0,j1=>3],[i0=>1,j0=>2,i1=>1,j1=>2],[i0=>2,j0=>2,i1=>2,j1=>2]] Chris@1296: # [[i0=>1,j0=>3,i1=>1,j1=>3],[i0=>2,j0=>3,i1=>2,j1=>3]]] Chris@1296: # Chris@1296: # @param string :tag tag name (in upcase) Chris@1296: # @param string :attr tag attribute (in upcase) Chris@1296: # @access private Chris@1296: # Chris@1296: def openHTMLTagCalc(tag, attrs) Chris@1296: #Opening tag Chris@1296: case (tag) Chris@1296: when 'table' Chris@1296: @max_table_columns[@table_id] = 0; Chris@1296: @t_columns = 0; Chris@1296: @tr_id = -1; Chris@1296: when 'tr' Chris@1296: if @max_table_columns[@table_id] < @t_columns Chris@1296: @max_table_columns[@table_id] = @t_columns; Chris@1296: end Chris@1296: @t_columns = 0; Chris@1296: @tr_id += 1; Chris@1296: @td_id = -1; Chris@1296: @t_cells[@table_id].push [] Chris@1296: when 'td', 'th' Chris@1296: @td_id += 1; Chris@1296: if attrs['colspan'].nil? or attrs['colspan'] == '' Chris@1296: colspan = 1; Chris@1296: else Chris@1296: colspan = attrs['colspan'].to_i; Chris@1296: end Chris@1296: if attrs['rowspan'].nil? or attrs['rowspan'] == '' Chris@1296: rowspan = 1; Chris@1296: else Chris@1296: rowspan = attrs['rowspan'].to_i; Chris@1296: end Chris@1296: Chris@1296: i = 0; Chris@1296: while true Chris@1296: next_i_distance = checkTableBlockingCellPosition(@table_id, @tr_id, @td_id + i); Chris@1296: if next_i_distance == 0 Chris@1296: @t_cells[@table_id][@tr_id].push "i0"=>@td_id + i, "j0"=>@tr_id, "i1"=>(@td_id + i + colspan - 1), "j1"=>@tr_id + rowspan - 1 Chris@1296: break; Chris@1296: end Chris@1296: i += next_i_distance; Chris@1296: end Chris@1296: Chris@1296: @t_columns += colspan; Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Calculate closing tags. Chris@1296: # @param string :tag tag name (in upcase) Chris@1296: # @access private Chris@1296: # Chris@1296: def closedHTMLTagCalc(tag) Chris@1296: #Closing tag Chris@1296: case (tag) Chris@1296: when 'table' Chris@1296: if @max_table_columns[@table_id] < @t_columns Chris@1296: @max_table_columns[@table_id] = @t_columns; Chris@1296: end Chris@1296: @table_id += 1; Chris@1296: @t_cells.push [] Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Convert to accessible file path Chris@1296: # @param string :attrname image file name Chris@1296: # Chris@1296: def getImageFilename( attrname ) Chris@1296: nil Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Process opening tags. Chris@1296: # @param string :tag tag name (in upcase) Chris@1296: # @param string :attr tag attribute (in upcase) Chris@1296: # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # @access private Chris@1296: # Chris@1296: def openHTMLTagHandler(tag, attrs, fill=0) Chris@1296: #Opening tag Chris@1296: case (tag) Chris@1296: when 'pre' Chris@1296: @pre_state = true; Chris@1296: @l_margin += 5; Chris@1296: @r_margin += 5; Chris@1296: @x += 5; Chris@1296: Chris@1296: when 'table' Chris@1296: Ln(); Chris@1296: if @default_table_columns < @max_table_columns[@table_id] Chris@1296: @table_columns = @max_table_columns[@table_id]; Chris@1296: else Chris@1296: @table_columns = @default_table_columns; Chris@1296: end Chris@1296: @l_margin += 5; Chris@1296: @r_margin += 5; Chris@1296: @x += 5; Chris@1296: Chris@1296: if attrs['border'].nil? or attrs['border'] == '' Chris@1296: @tableborder = 0; Chris@1296: else Chris@1296: @tableborder = attrs['border']; Chris@1296: end Chris@1296: @tr_id = -1; Chris@1296: @max_td_page[0] = @page; Chris@1296: @max_td_y[0] = @y; Chris@1296: Chris@1296: when 'tr', 'td', 'th' Chris@1296: if tag == 'th' Chris@1296: SetStyle('b', true); Chris@1296: @tdalign = "C"; Chris@1296: end Chris@1296: if ((!attrs['width'].nil?) and (attrs['width'] != '')) Chris@1296: @tdwidth = (attrs['width'].to_i/4); Chris@1296: else Chris@1296: @tdwidth = ((@w - @l_margin - @r_margin) / @table_columns); Chris@1296: end Chris@1296: Chris@1296: if tag == 'tr' Chris@1296: @tr_id += 1; Chris@1296: @td_id = -1; Chris@1296: else Chris@1296: @td_id += 1; Chris@1296: @x = @l_margin + @tdwidth * @t_cells[@table_id][@tr_id][@td_id]['i0']; Chris@1296: end Chris@1296: Chris@1296: if attrs['colspan'].nil? or attrs['border'] == '' Chris@1296: @colspan = 1; Chris@1296: else Chris@1296: @colspan = attrs['colspan'].to_i; Chris@1296: end Chris@1296: @tdwidth *= @colspan; Chris@1296: if ((!attrs['height'].nil?) and (attrs['height'] != '')) Chris@1296: @tdheight=(attrs['height'].to_i / @k); Chris@1296: else Chris@1296: @tdheight = @lasth; Chris@1296: end Chris@1296: if ((!attrs['align'].nil?) and (attrs['align'] != '')) Chris@1296: case (attrs['align']) Chris@1296: when 'center' Chris@1296: @tdalign = "C"; Chris@1296: when 'right' Chris@1296: @tdalign = "R"; Chris@1296: when 'left' Chris@1296: @tdalign = "L"; Chris@1296: end Chris@1296: end Chris@1296: if ((!attrs['bgcolor'].nil?) and (attrs['bgcolor'] != '')) Chris@1296: coul = convertColorHexToDec(attrs['bgcolor']); Chris@1296: SetFillColor(coul['R'], coul['G'], coul['B']); Chris@1296: @tdfill=1; Chris@1296: end Chris@1296: @tdbegin=true; Chris@1296: Chris@1296: when 'hr' Chris@1296: margin = 1; Chris@1296: if ((!attrs['width'].nil?) and (attrs['width'] != '')) Chris@1296: hrWidth = attrs['width']; Chris@1296: else Chris@1296: hrWidth = @w - @l_margin - @r_margin - margin; Chris@1296: end Chris@1296: SetLineWidth(0.2); Chris@1296: Line(@x + margin, @y, @x + hrWidth, @y); Chris@1296: Ln(); Chris@1296: Chris@1296: when 'strong' Chris@1296: SetStyle('b', true); Chris@1296: Chris@1296: when 'em' Chris@1296: SetStyle('i', true); Chris@1296: Chris@1296: when 'ins' Chris@1296: SetStyle('u', true); Chris@1296: Chris@1296: when 'del' Chris@1296: SetStyle('d', true); Chris@1296: Chris@1296: when 'b', 'i', 'u' Chris@1296: SetStyle(tag, true); Chris@1296: Chris@1296: when 'a' Chris@1296: @href = attrs['href']; Chris@1296: Chris@1296: when 'img' Chris@1296: if (!attrs['src'].nil?) Chris@1296: # Don't generates image inside table tag Chris@1296: if (@tdbegin) Chris@1296: @tdtext << attrs['src']; Chris@1296: return Chris@1296: end Chris@1296: # Only generates image include a pdf if RMagick is avalaible Chris@1296: unless Object.const_defined?(:Magick) Chris@1296: Write(@lasth, attrs['src'], '', fill); Chris@1296: return Chris@1296: end Chris@1296: file = getImageFilename(attrs['src']) Chris@1296: if (file.nil?) Chris@1296: Write(@lasth, attrs['src'], '', fill); Chris@1296: return Chris@1296: end Chris@1296: Chris@1296: if (attrs['width'].nil?) Chris@1296: attrs['width'] = 0; Chris@1296: end Chris@1296: if (attrs['height'].nil?) Chris@1296: attrs['height'] = 0; Chris@1296: end Chris@1296: Chris@1296: begin Chris@1296: Image(file, GetX(),GetY(), pixelsToMillimeters(attrs['width']), pixelsToMillimeters(attrs['height'])); Chris@1296: #SetX(@img_rb_x); Chris@1296: SetY(@img_rb_y); Chris@1296: rescue => err Chris@1296: logger.error "pdf: Image: error: #{err.message}" Chris@1296: Write(@lasth, attrs['src'], '', fill); Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: when 'ul', 'ol' Chris@1296: if @li_count == 0 Chris@1296: Ln() if @prevquote_count == @quote_count; # insert Ln for keeping quote lines Chris@1296: @prevquote_count = @quote_count; Chris@1296: end Chris@1296: if @li_state == true Chris@1296: Ln(); Chris@1296: @li_state = false; Chris@1296: end Chris@1296: if tag == 'ul' Chris@1296: @list_ordered[@li_count] = false; Chris@1296: else Chris@1296: @list_ordered[@li_count] = true; Chris@1296: end Chris@1296: @list_count[@li_count] = 0; Chris@1296: @li_count += 1 Chris@1296: Chris@1296: when 'li' Chris@1296: Ln() if @li_state == true Chris@1296: if (@list_ordered[@li_count - 1]) Chris@1296: @list_count[@li_count - 1] += 1; Chris@1296: @li_spacer = " " * @li_count + (@list_count[@li_count - 1]).to_s + ". "; Chris@1296: else Chris@1296: #unordered list simbol Chris@1296: @li_spacer = " " * @li_count + "- "; Chris@1296: end Chris@1296: Write(@lasth, @spacer + @li_spacer, '', fill); Chris@1296: @li_state = true; Chris@1296: Chris@1296: when 'blockquote' Chris@1296: if (@quote_count == 0) Chris@1296: SetStyle('i', true); Chris@1296: @l_margin += 5; Chris@1296: else Chris@1296: @l_margin += 5 / 2; Chris@1296: end Chris@1296: @x = @l_margin; Chris@1296: @quote_top[@quote_count] = @y; Chris@1296: @quote_page[@quote_count] = @page; Chris@1296: @quote_count += 1 Chris@1296: when 'br' Chris@1296: if @tdbegin Chris@1296: @tdtext << "\n" Chris@1296: return Chris@1296: end Chris@1296: Ln(); Chris@1296: Chris@1296: if (@li_spacer.length > 0) Chris@1296: @x += GetStringWidth(@li_spacer); Chris@1296: end Chris@1296: Chris@1296: when 'p' Chris@1296: Ln(); Chris@1296: 0.upto(@quote_count - 1) do |i| Chris@1296: if @quote_page[i] == @page; Chris@1296: if @quote_top[i] == @y - @lasth; # fix start line Chris@1296: @quote_top[i] = @y; Chris@1296: end Chris@1296: else Chris@1296: if @quote_page[i] == @page - 1; Chris@1296: @quote_page[i] = @page; # fix start line Chris@1296: @quote_top[i] = @t_margin; Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: when 'sup' Chris@1296: currentfont_size = @font_size; Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetFontSize(@font_size_pt * @@k_small_ratio); Chris@1296: SetXY(GetX(), GetY() - ((currentfont_size - @font_size)*(@@k_small_ratio))); Chris@1296: Chris@1296: when 'sub' Chris@1296: currentfont_size = @font_size; Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetFontSize(@font_size_pt * @@k_small_ratio); Chris@1296: SetXY(GetX(), GetY() + ((currentfont_size - @font_size)*(@@k_small_ratio))); Chris@1296: Chris@1296: when 'small' Chris@1296: currentfont_size = @font_size; Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetFontSize(@font_size_pt * @@k_small_ratio); Chris@1296: SetXY(GetX(), GetY() + ((currentfont_size - @font_size)/3)); Chris@1296: Chris@1296: when 'font' Chris@1296: if (!attrs['color'].nil? and attrs['color']!='') Chris@1296: coul = convertColorHexToDec(attrs['color']); Chris@1296: SetTextColor(coul['R'], coul['G'], coul['B']); Chris@1296: @issetcolor=true; Chris@1296: end Chris@1296: if (!attrs['face'].nil? and @fontlist.include?(attrs['face'].downcase)) Chris@1296: SetFont(attrs['face'].downcase); Chris@1296: @issetfont=true; Chris@1296: end Chris@1296: if (!attrs['size'].nil?) Chris@1296: headsize = attrs['size'].to_i; Chris@1296: else Chris@1296: headsize = 0; Chris@1296: end Chris@1296: currentfont_size = @font_size; Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetFontSize(@font_size_pt + headsize); Chris@1296: @lasth = @font_size * @@k_cell_height_ratio; Chris@1296: Chris@1296: when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' Chris@1296: Ln(); Chris@1296: headsize = (4 - tag[1,1].to_f) * 2 Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetFontSize(@font_size_pt + headsize); Chris@1296: SetStyle('b', true); Chris@1296: @lasth = @font_size * @@k_cell_height_ratio; Chris@1296: Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Process closing tags. Chris@1296: # @param string :tag tag name (in upcase) Chris@1296: # @access private Chris@1296: # Chris@1296: def closedHTMLTagHandler(tag) Chris@1296: #Closing tag Chris@1296: case (tag) Chris@1296: when 'pre' Chris@1296: @pre_state = false; Chris@1296: @l_margin -= 5; Chris@1296: @r_margin -= 5; Chris@1296: @x = @l_margin; Chris@1296: Ln(); Chris@1296: Chris@1296: when 'td','th' Chris@1296: base_page = @page; Chris@1296: base_x = @x; Chris@1296: base_y = @y; Chris@1296: Chris@1296: MultiCell(@tdwidth, @tdheight, unhtmlentities(@tdtext.strip), @tableborder, @tdalign, @tdfill, 1); Chris@1296: tr_end = @t_cells[@table_id][@tr_id][@td_id]['j1'] + 1; Chris@1296: if @max_td_page[tr_end].nil? or (@max_td_page[tr_end] < @page) Chris@1296: @max_td_page[tr_end] = @page Chris@1296: @max_td_y[tr_end] = @y Chris@1296: elsif (@max_td_page[tr_end] == @page) Chris@1296: @max_td_y[tr_end] = @y if @max_td_y[tr_end].nil? or (@max_td_y[tr_end] < @y) Chris@1296: end Chris@1296: Chris@1296: @page = base_page; Chris@1296: @x = base_x + @tdwidth; Chris@1296: @y = base_y; Chris@1296: @tdtext = ''; Chris@1296: @tdbegin = false; Chris@1296: @tdwidth = 0; Chris@1296: @tdheight = 0; Chris@1296: @tdalign = "L"; Chris@1296: SetStyle('b', false); Chris@1296: @tdfill = 0; Chris@1296: SetFillColor(@prevfill_color[0], @prevfill_color[1], @prevfill_color[2]); Chris@1296: Chris@1296: when 'tr' Chris@1296: @y = @max_td_y[@tr_id + 1]; Chris@1296: @x = @l_margin; Chris@1296: @page = @max_td_page[@tr_id + 1]; Chris@1296: Chris@1296: when 'table' Chris@1296: # Write Table Line Chris@1296: width = (@w - @l_margin - @r_margin) / @table_columns; Chris@1296: 0.upto(@t_cells[@table_id].size - 1) do |j| Chris@1296: 0.upto(@t_cells[@table_id][j].size - 1) do |i| Chris@1296: @page = @max_td_page[j] Chris@1296: i0=@t_cells[@table_id][j][i]['i0']; Chris@1296: j0=@t_cells[@table_id][j][i]['j0']; Chris@1296: i1=@t_cells[@table_id][j][i]['i1']; Chris@1296: j1=@t_cells[@table_id][j][i]['j1']; Chris@1296: Chris@1296: Line(@l_margin + width * i0, @max_td_y[j0], @l_margin + width * (i1+1), @max_td_y[j0]) # top Chris@1296: if ( @page == @max_td_page[j1 + 1]) Chris@1296: Line(@l_margin + width * i0, @max_td_y[j0], @l_margin + width * i0, @max_td_y[j1+1]) # left Chris@1296: Line(@l_margin + width * (i1+1), @max_td_y[j0], @l_margin + width * (i1+1), @max_td_y[j1+1]) # right Chris@1296: else Chris@1296: Line(@l_margin + width * i0, @max_td_y[j0], @l_margin + width * i0, @page_break_trigger) # left Chris@1296: Line(@l_margin + width * (i1+1), @max_td_y[j0], @l_margin + width * (i1+1), @page_break_trigger) # right Chris@1296: @page += 1; Chris@1296: while @page < @max_td_page[j1 + 1] Chris@1296: Line(@l_margin + width * i0, @t_margin, @l_margin + width * i0, @page_break_trigger) # left Chris@1296: Line(@l_margin + width * (i1+1), @t_margin, @l_margin + width * (i1+1), @page_break_trigger) # right Chris@1296: @page += 1; Chris@1296: end Chris@1296: Line(@l_margin + width * i0, @t_margin, @l_margin + width * i0, @max_td_y[j1+1]) # left Chris@1296: Line(@l_margin + width * (i1+1), @t_margin, @l_margin + width * (i1+1), @max_td_y[j1+1]) # right Chris@1296: end Chris@1296: Line(@l_margin + width * i0, @max_td_y[j1+1], @l_margin + width * (i1+1), @max_td_y[j1+1]) # bottom Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: @l_margin -= 5; Chris@1296: @r_margin -= 5; Chris@1296: @tableborder=0; Chris@1296: @table_id += 1; Chris@1296: Chris@1296: when 'strong' Chris@1296: SetStyle('b', false); Chris@1296: Chris@1296: when 'em' Chris@1296: SetStyle('i', false); Chris@1296: Chris@1296: when 'ins' Chris@1296: SetStyle('u', false); Chris@1296: Chris@1296: when 'del' Chris@1296: SetStyle('d', false); Chris@1296: Chris@1296: when 'b', 'i', 'u' Chris@1296: SetStyle(tag, false); Chris@1296: Chris@1296: when 'a' Chris@1296: @href = nil; Chris@1296: Chris@1296: when 'p' Chris@1296: Ln(); Chris@1296: Chris@1296: when 'sup' Chris@1296: currentfont_size = @font_size; Chris@1296: SetFontSize(@tempfontsize); Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetXY(GetX(), GetY() - ((currentfont_size - @font_size)*(@@k_small_ratio))); Chris@1296: Chris@1296: when 'sub' Chris@1296: currentfont_size = @font_size; Chris@1296: SetFontSize(@tempfontsize); Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetXY(GetX(), GetY() + ((currentfont_size - @font_size)*(@@k_small_ratio))); Chris@1296: Chris@1296: when 'small' Chris@1296: currentfont_size = @font_size; Chris@1296: SetFontSize(@tempfontsize); Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetXY(GetX(), GetY() - ((@font_size - currentfont_size)/3)); Chris@1296: Chris@1296: when 'font' Chris@1296: if (@issetcolor == true) Chris@1296: SetTextColor(@prevtext_color[0], @prevtext_color[1], @prevtext_color[2]); Chris@1296: end Chris@1296: if (@issetfont) Chris@1296: @font_family = @prevfont_family; Chris@1296: @font_style = @prevfont_style; Chris@1296: SetFont(@font_family); Chris@1296: @issetfont = false; Chris@1296: end Chris@1296: currentfont_size = @font_size; Chris@1296: SetFontSize(@tempfontsize); Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: #@text_color = @prevtext_color; Chris@1296: @lasth = @font_size * @@k_cell_height_ratio; Chris@1296: Chris@1296: when 'blockquote' Chris@1296: @quote_count -= 1 Chris@1296: if (@quote_page[@quote_count] == @page) Chris@1296: Line(@l_margin - 1, @quote_top[@quote_count], @l_margin - 1, @y) # quoto line Chris@1296: else Chris@1296: cur_page = @page; Chris@1296: cur_y = @y; Chris@1296: @page = @quote_page[@quote_count]; Chris@1296: if (@quote_top[@quote_count] < @page_break_trigger) Chris@1296: Line(@l_margin - 1, @quote_top[@quote_count], @l_margin - 1, @page_break_trigger) # quoto line Chris@1296: end Chris@1296: @page += 1; Chris@1296: while @page < cur_page Chris@1296: Line(@l_margin - 1, @t_margin, @l_margin - 1, @page_break_trigger) # quoto line Chris@1296: @page += 1; Chris@1296: end Chris@1296: @y = cur_y; Chris@1296: Line(@l_margin - 1, @t_margin, @l_margin - 1, @y) # quoto line Chris@1296: end Chris@1296: if (@quote_count <= 0) Chris@1296: SetStyle('i', false); Chris@1296: @l_margin -= 5; Chris@1296: else Chris@1296: @l_margin -= 5 / 2; Chris@1296: end Chris@1296: @x = @l_margin; Chris@1296: Ln() if @quote_count == 0 Chris@1296: Chris@1296: when 'ul', 'ol' Chris@1296: @li_count -= 1 Chris@1296: if @li_state == true Chris@1296: Ln(); Chris@1296: @li_state = false; Chris@1296: end Chris@1296: Chris@1296: when 'li' Chris@1296: @li_spacer = ""; Chris@1296: if @li_state == true Chris@1296: Ln(); Chris@1296: @li_state = false; Chris@1296: end Chris@1296: Chris@1296: when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' Chris@1296: SetFontSize(@tempfontsize); Chris@1296: @tempfontsize = @font_size_pt; Chris@1296: SetStyle('b', false); Chris@1296: Ln(); Chris@1296: @lasth = @font_size * @@k_cell_height_ratio; Chris@1296: Chris@1296: if tag == 'h1' or tag == 'h2' or tag == 'h3' or tag == 'h4' Chris@1296: margin = 1; Chris@1296: hrWidth = @w - @l_margin - @r_margin - margin; Chris@1296: if tag == 'h1' or tag == 'h2' Chris@1296: SetLineWidth(0.2); Chris@1296: else Chris@1296: SetLineWidth(0.1); Chris@1296: end Chris@1296: Line(@x + margin, @y, @x + hrWidth, @y); Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Sets font style. Chris@1296: # @param string :tag tag name (in lowercase) Chris@1296: # @param boolean :enable Chris@1296: # @access private Chris@1296: # Chris@1296: def SetStyle(tag, enable) Chris@1296: #Modify style and select corresponding font Chris@1296: ['b', 'i', 'u', 'd'].each do |s| Chris@1296: if tag.downcase == s Chris@1296: if enable Chris@1296: @style << s if ! @style.include?(s) Chris@1296: else Chris@1296: @style = @style.gsub(s,'') Chris@1296: end Chris@1296: end Chris@1296: end Chris@1296: SetFont('', @style); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Output anchor link. Chris@1296: # @param string :url link URL Chris@1296: # @param string :name link name Chris@1296: # @param int :fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. Chris@1296: # @access public Chris@1296: # Chris@1296: def addHtmlLink(url, name, fill=0) Chris@1296: #Put a hyperlink Chris@1296: SetTextColor(0, 0, 255); Chris@1296: SetStyle('u', true); Chris@1296: Write(@lasth, name, url, fill); Chris@1296: SetStyle('u', false); Chris@1296: SetTextColor(0); Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Returns an associative array (keys: R,G,B) from Chris@1296: # a hex html code (e.g. #3FE5AA). Chris@1296: # @param string :color hexadecimal html color [#rrggbb] Chris@1296: # @return array Chris@1296: # @access private Chris@1296: # Chris@1296: def convertColorHexToDec(color = "#000000") Chris@1296: tbl_color = {} Chris@1296: tbl_color['R'] = color[1,2].hex.to_i; Chris@1296: tbl_color['G'] = color[3,2].hex.to_i; Chris@1296: tbl_color['B'] = color[5,2].hex.to_i; Chris@1296: return tbl_color; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Converts pixels to millimeters in 72 dpi. Chris@1296: # @param int :px pixels Chris@1296: # @return float millimeters Chris@1296: # @access private Chris@1296: # Chris@1296: def pixelsToMillimeters(px) Chris@1296: return px.to_f * 25.4 / 72; Chris@1296: end Chris@1296: Chris@1296: # Chris@1296: # Reverse function for htmlentities. Chris@1296: # Convert entities in UTF-8. Chris@1296: # Chris@1296: # @param :text_to_convert Text to convert. Chris@1296: # @return string converted Chris@1296: # Chris@1296: def unhtmlentities(string) Chris@1296: CGI.unescapeHTML(string) Chris@1296: end Chris@1296: Chris@1296: end # END OF CLASS Chris@1296: Chris@1296: #TODO 2007-05-25 (EJM) Level=0 - Chris@1296: #Handle special IE contype request Chris@1296: # if (!_SERVER['HTTP_USER_AGENT'].nil? and (_SERVER['HTTP_USER_AGENT']=='contype')) Chris@1296: # header('Content-Type: application/pdf'); Chris@1296: # exit; Chris@1296: # }