Class: CombinePDF::PDFWriter
- Inherits:
-
Hash
- Object
- Hash
- CombinePDF::PDFWriter
- Defined in:
- lib/combine_pdf/combine_pdf_basic_writer.rb
Overview
Limited Unicode Support (font dependent)!
The PDFWriter class is a subclass of Hash and represents a PDF Page object.
Writing on this Page is done using the textbox function.
Setting the page dimensions can be either at the new or using the mediabox method. New pages default to size A4, which is: [0, 0, 595.3, 841.9].
Once the Page is completed (the last text box was added), we can insert the page to a CombinePDF object.
We can either insert the PDFWriter as a new page:
pdf = CombinePDF.new
new_page = PDFWriter.new
new_page.textbox "some text"
pdf << new_page
pdf.save "file_with_new_page.pdf"
Or we can insert the PDFWriter as an overlay (stamp / watermark) over existing pages:
pdf = CombinePDF.new
new_page = PDFWriter.new "some_file.pdf"
new_page.textbox "some text"
pdf.pages.each {|page| page << new_page }
pdf.save "stamped_file.pdf"
Instance Method Summary collapse
-
#dimensions_of(text, fonts, size = 1000) ⇒ Object
gets the dimentions (width and height) of the text, as it will be printed in the PDF.
-
#fit_text(text, font, length, height = 10000000) ⇒ Object
this method returns the size for which the text fits the requested metrices the size is type Float and is rather exact if the text cannot fit such a small place, returns zero (0).
-
#initialize(mediabox = [0, 0, 595.3, 841.9]) ⇒ PDFWriter
constructor
create a new PDFWriter object.
-
#mediabox ⇒ Object
accessor (getter) for the :MediaBox element of the page.
-
#mediabox=(dimensions = [0.0, 0.0, 612.0, 792.0]) ⇒ Object
- accessor (setter) for the :MediaBox element of the page dimensions
-
an Array consisting of four numbers (can be floats) setting the size of the media box.
-
#textbox(text, properties = {}) ⇒ Object
This method adds a simple text box to the Page represented by the PDFWriter class.
Constructor Details
#initialize(mediabox = [0, 0, 595.3, 841.9]) ⇒ PDFWriter
create a new PDFWriter object.
- mediabox
-
the PDF page size in PDF points. defaults to [0, 0, 595.3, 841.9] (A4)
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/combine_pdf/combine_pdf_basic_writer.rb', line 41 def initialize(mediabox = [0, 0, 595.3, 841.9]) # indirect_reference_id, :indirect_generation_number @contents = "" @base_font_name = "Writer" + SecureRandom.urlsafe_base64(7) + "PDF" self[:Type] = :Page self[:indirect_reference_id] = 0 self[:Resources] = {} self[:Contents] = { is_reference_only: true , referenced_object: {indirect_reference_id: 0, raw_stream_content: @contents} } self[:MediaBox] = mediabox end |
Instance Method Details
#dimensions_of(text, fonts, size = 1000) ⇒ Object
gets the dimentions (width and height) of the text, as it will be printed in the PDF.
- text
-
the text to measure
- font
-
a font name or an Array of font names. Font names should be registered fonts. The 14 standard fonts are pre regitered with the font library.
- size
-
the size of the font (defaults to 1000 points).
276 277 278 |
# File 'lib/combine_pdf/combine_pdf_basic_writer.rb', line 276 def dimensions_of(text, fonts, size = 1000) Fonts.dimensions_of text, fonts, size end |
#fit_text(text, font, length, height = 10000000) ⇒ Object
this method returns the size for which the text fits the requested metrices the size is type Float and is rather exact if the text cannot fit such a small place, returns zero (0). maximum font size possible is set to 100,000 - which should be big enough for anything
- text
-
the text to fit
- font
-
the font name. @see font
- length
-
the length to fit
- height
-
the height to fit (optional - normally length is the issue)
287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/combine_pdf/combine_pdf_basic_writer.rb', line 287 def fit_text(text, font, length, height = 10000000) size = 100000 size_array = [size] metrics = Fonts.dimensions_of text, font, size if metrics[0] > length size_array << size * length/metrics[0] end if metrics[1] > height size_array << size * height/metrics[1] end size_array.min end |
#mediabox ⇒ Object
accessor (getter) for the :MediaBox element of the page
52 53 54 |
# File 'lib/combine_pdf/combine_pdf_basic_writer.rb', line 52 def mediabox self[:MediaBox] end |
#mediabox=(dimensions = [0.0, 0.0, 612.0, 792.0]) ⇒ Object
accessor (setter) for the :MediaBox element of the page
- dimensions
-
an Array consisting of four numbers (can be floats) setting the size of the media box.
57 58 59 |
# File 'lib/combine_pdf/combine_pdf_basic_writer.rb', line 57 def mediabox=(dimensions = [0.0, 0.0, 612.0, 792.0]) self[:MediaBox] = dimensions end |
#textbox(text, properties = {}) ⇒ Object
This method adds a simple text box to the Page represented by the PDFWriter class. This function takes two values:
- text
-
the text to potin the box.
- properties
-
a Hash of box properties.
the symbols and values in the properties Hash could be any or all of the following:
- x
-
the left position of the box.
- y
-
the BUTTOM position of the box.
- width
-
the width/length of the box. negative values will be computed from edge of page. defaults to 0 (end of page).
- height
-
the height of the box. negative values will be computed from edge of page. defaults to 0 (end of page).
- text_align
-
symbol for horizontal text alignment, can be “:center” (default), “:right”, “:left”
- text_valign
-
symbol for vertical text alignment, can be “:center” (default), “:top”, “:buttom”
- text_padding
-
a Float between 0 and 1, setting the padding for the text. defaults to 0.05 (5%).
- font
-
a registered font name or an Array of names. defaults to “:Helvetica”. The 14 standard fonts names are:
-
:“Times-Roman”
-
:“Times-Bold”
-
:“Times-Italic”
-
:“Times-BoldItalic”
-
:Helvetica
-
:“Helvetica-Bold”
-
:“Helvetica-BoldOblique”
-
:“Helvetica- Oblique”
-
:Courier
-
:“Courier-Bold”
-
:“Courier-Oblique”
-
:“Courier-BoldOblique”
-
:Symbol
-
:ZapfDingbats
- font_size
-
a Fixnum for the font size, or :fit_text to fit the text in the box. defaults to “:fit_text”
- max_font_size
-
if font_size is set to :fit_text, this will be the maximum font size. defaults to nil (no maximum)
- font_color
-
text color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be “[0.5, 0.5, 0.5]”). defaults to black.
- stroke_color
-
text stroke color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be “[0.5, 0.5, 0.5]”). defounlts to nil (no stroke).
- stroke_width
-
text stroke width in PDF units. defaults to 0 (none).
- box_color
-
box fill color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be “[0.5, 0.5, 0.5]”). defaults to nil (none).
- border_color
-
box border color in [R, G, B], an array with three floats, each in a value between 0 to 1 (gray will be “[0.5, 0.5, 0.5]”). defaults to nil (none).
- border_width
-
border width in PDF units. defaults to nil (none).
- box_radius
-
border radius in PDF units. defaults to 0 (no corner rounding).
- opacity
-
textbox opacity, a float between 0 (transparent) and 1 (opaque)
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/combine_pdf/combine_pdf_basic_writer.rb', line 98 def textbox(text, properties = {}) = { x: 0, y: 0, width: 0, height: -1, text_align: :center, text_valign: :center, text_padding: 0.1, font: nil, font_size: :fit_text, max_font_size: nil, font_color: [0,0,0], stroke_color: nil, stroke_width: 0, box_color: nil, border_color: nil, border_width: 0, box_radius: 0, opacity: 1, ctm: [1,0,0,1,0,0] } .update properties # reset the length and height to meaningful values, if negative [:width] = mediabox[2] - [:x] + [:width] if [:width] <= 0 [:height] = mediabox[3] - [:y] + [:height] if [:height] <= 0 # reset the padding value [:text_padding] = 0 if [:text_padding].to_f >= 1 # create box stream box_stream = "" # set graphic state for box if [:box_color] || ([:border_width].to_i > 0 && [:border_color]) # compute x and y position for text x = [:x] y = [:y] # set graphic state for the box box_stream << "q\n" box_graphic_state = { ca: [:opacity], CA: [:opacity], LW: [:border_width], LC: 0, LJ: 0, LD: 0, CTM: [:ctm]} if [:box_radius] != 0 # if the text box has rounded corners box_graphic_state[:LC], box_graphic_state[:LJ] = 2, 1 end box_graphic_state = graphic_state box_graphic_state # adds the graphic state to Resources and gets the reference box_stream << "#{PDFOperations._object_to_pdf box_graphic_state} gs\n" box_stream << "DeviceRGB CS\nDeviceRGB cs\n" if [:box_color] box_stream << "#{[:box_color].join(' ')} rg\n" end if [:border_width].to_i > 0 && [:border_color] box_stream << "#{[:border_color].join(' ')} RG\n" end # create the path radius = [:box_radius] half_radius = (radius.to_f / 2).round 4 ## set starting point box_stream << "#{[:x] + radius} #{[:y]} m\n" ## buttom and right corner - first line and first corner box_stream << "#{[:x] + [:width] - radius} #{[:y]} l\n" #buttom if [:box_radius] != 0 # make first corner, if not straight. box_stream << "#{[:x] + [:width] - half_radius} #{[:y]} " box_stream << "#{[:x] + [:width]} #{[:y] + half_radius} " box_stream << "#{[:x] + [:width]} #{[:y] + radius} c\n" end ## right and top-right corner box_stream << "#{[:x] + [:width]} #{[:y] + [:height] - radius} l\n" if [:box_radius] != 0 box_stream << "#{[:x] + [:width]} #{[:y] + [:height] - half_radius} " box_stream << "#{[:x] + [:width] - half_radius} #{[:y] + [:height]} " box_stream << "#{[:x] + [:width] - radius} #{[:y] + [:height]} c\n" end ## top and top-left corner box_stream << "#{[:x] + radius} #{[:y] + [:height]} l\n" if [:box_radius] != 0 box_stream << "#{[:x] + half_radius} #{[:y] + [:height]} " box_stream << "#{[:x]} #{[:y] + [:height] - half_radius} " box_stream << "#{[:x]} #{[:y] + [:height] - radius} c\n" end ## left and buttom-left corner box_stream << "#{[:x]} #{[:y] + radius} l\n" if [:box_radius] != 0 box_stream << "#{[:x]} #{[:y] + half_radius} " box_stream << "#{[:x] + half_radius} #{[:y]} " box_stream << "#{[:x] + radius} #{[:y]} c\n" end # fill / stroke path box_stream << "h\n" if [:box_color] && [:border_width].to_i > 0 && [:border_color] box_stream << "B\n" elsif [:box_color] # fill if fill color is set box_stream << "f\n" elsif [:border_width].to_i > 0 && [:border_color] # stroke if border is set box_stream << "S\n" end # exit graphic state for the box box_stream << "Q\n" end contents << box_stream # reset x,y by text alignment - x,y are calculated from the buttom left # each unit (1) is 1/72 Inch # create text stream text_stream = "" if text.to_s != "" && [:font_size] != 0 && ([:font_color] || [:stroke_color]) # compute x and y position for text x = [:x] + ([:width]*[:text_padding]) y = [:y] + ([:height]*[:text_padding]) # set the fonts (fonts array, with :Helvetica as fallback). fonts = [*[:font], :Helvetica] # fit text in box, if requested font_size = [:font_size] if [:font_size] == :fit_text font_size = self.fit_text text, fonts, ([:width]*(1-[:text_padding])), ([:height]*(1-[:text_padding])) font_size = [:max_font_size] if [:max_font_size] && font_size > [:max_font_size] end text_size = dimensions_of text, fonts, font_size if [:text_align] == :center x = ( ( [:width]*(1-(2*[:text_padding])) ) - text_size[0] )/2 + x elsif [:text_align] == :right x = ( ( [:width]*(1-(1.5*[:text_padding])) ) - text_size[0] ) + x end if [:text_valign] == :center y = ( ( [:height]*(1-(2*[:text_padding])) ) - text_size[1] )/2 + y elsif [:text_valign] == :top y = ( [:height]*(1-(1.5*[:text_padding])) ) - text_size[1] + y end # set graphic state for text text_stream << "q\n" text_graphic_state = graphic_state({ca: [:opacity], CA: [:opacity], LW: [:stroke_width].to_f, LC: 2, LJ: 1, LD: 0, CTM: [:ctm]}) text_stream << "#{PDFOperations._object_to_pdf text_graphic_state} gs\n" text_stream << "DeviceRGB CS\nDeviceRGB cs\n" # set text render mode if [:font_color] text_stream << "#{[:font_color].join(' ')} rg\n" end if [:stroke_width].to_i > 0 && [:stroke_color] text_stream << "#{[:stroke_color].join(' ')} RG\n" if [:font_color] text_stream << "2 Tr\n" else final_stream << "1 Tr\n" end elsif [:font_color] text_stream << "0 Tr\n" else text_stream << "3 Tr\n" end # format text object(s) # text_stream << "#{options[:font_color].join(' ')} rg\n" # sets the color state encode(text, fonts).each do |encoded| text_stream << "BT\n" # the Begine Text marker text_stream << PDFOperations._format_name_to_pdf(set_font encoded[0]) # Set font name text_stream << " #{font_size} Tf\n" # set font size and add font operator text_stream << "#{x.round 4} #{y.round 4} Td\n" # set location for text object text_stream << ( encoded[1] ) # insert the encoded string to the stream text_stream << " Tj\n" # the Text object operator and the End Text marker text_stream << "ET\n" # the Text object operator and the End Text marker x += encoded[2]/1000*font_size #update text starting point y -= encoded[3]/1000*font_size #update text starting point end # exit graphic state for text text_stream << "Q\n" end contents << text_stream self end |