Module: CombinePDF::Page_Methods
- Included in:
- PDFWriter
- Defined in:
- lib/combine_pdf/combine_pdf_page.rb
Overview
This module injects methods into existing page objects
Instance Method Summary collapse
-
#<<(obj) ⇒ Object
the injection method.
-
#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).
-
#fix_rotation ⇒ Object
This method moves the Page property into the page’s data stream, so that “what you see is what you get”.
-
#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.
-
#orientation(force = nil, clockwise = true) ⇒ Object
get or set (by clockwise rotation) the page’s orientation.
-
#resources ⇒ Object
accessor (getter) for the :Resources element of the page.
-
#rotate_180 ⇒ Object
rotate the page by 180 degrees.
-
#rotate_left ⇒ Object
rotate the page 90 degrees counter clockwise.
-
#rotate_right ⇒ Object
rotate the page 90 degrees clockwise.
-
#secure_injection ⇒ Object
accessor (getter) for the secure_injection setting.
-
#secure_injection=(safe) ⇒ Object
accessor (setter) for the secure_injection setting.
-
#textbox(text, properties = {}) ⇒ Object
This method adds a simple text box to the Page represented by the PDFWriter class.
Instance Method Details
#<<(obj) ⇒ Object
the injection method
26 27 28 29 30 31 32 33 34 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 26 def << obj obj = secure_injection ? PDFOperations.copy_and_secure_for_injection(obj) : PDFOperations.create_deep_copy(obj) PDFOperations.inject_to_page self, obj # should add new referenced objects to the main PDF objects array, # but isn't done because the container is unknown. # This should be resolved once the container is rendered and references are renewed. # holder.add_referenced self self end |
#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_page.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_page.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 |
#fix_rotation ⇒ Object
This method moves the Page property into the page’s data stream, so that “what you see is what you get”.
This is usful in cases where there might be less control over the source PDF files, and the user assums that the PDF page’s data is the same as the PDF’s pages on screen display (Rotate rotates a page but leaves the data in the original orientation).
The method returns the page object, thus allowing method chaining (i.e. ‘page = 90; page.textbox(’hello!‘).fix_rotation.textbox(’hello!‘)`)
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 309 def fix_rotation return self if self[:Rotate].to_f == 0.0 || mediabox.nil? # calculate the rotation r = self[:Rotate].to_f * Math::PI / 180 s = Math.sin(r).round 6 c = Math.cos(r).round 6 ctm = [c, s, -s, c] # calculate the translation (move the origin of x,y to the new origin). x = mediabox[2] - mediabox[0] y = mediabox[3] - mediabox[1] ctm.push( ( (x*c).abs - x*c + (y*s).abs + y*s )/2 , ( (x*s).abs - x*s + (y*c).abs - y*c )/2 ) # insert the rotation stream into the current content stream insert_object "q\n#{ctm.join ' '} cm\n", 0 # close the rotation stream insert_object PDFOperations.create_deep_copy(CONTENT_CONTAINER_END) # reset the mediabox and cropbox values - THIS IS ONLY FOR ORIENTATION CHANGE... if ((self[:Rotate].to_f / 90)%2) != 0 self[:MediaBox] = self[:MediaBox].values_at(1,0,3,2) self[:CropBox] = self[:CropBox].values_at(1,0,3,2) if self[:CropBox] end # reset the Rotate property self.delete :Rotate # disconnect the content stream, so that future inserts aren't rotated @contents = false #init_contents # always return self, for chaining. self end |
#mediabox ⇒ Object
accessor (getter) for the :MediaBox element of the page
42 43 44 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 42 def mediabox self[:MediaBox].is_a?(Array) ? self[:MediaBox] : self[:MediaBox][:referenced_object] 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.
37 38 39 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 37 def mediabox=(dimensions = [0.0, 0.0, 612.0, 792.0]) self[:MediaBox] = dimensions end |
#orientation(force = nil, clockwise = true) ⇒ Object
get or set (by clockwise rotation) the page’s orientation
accepts one optional parameter:
- force
-
to get the orientation, pass nil. to set the orientatiom, set fource to either :portrait or :landscape. defaults to nil (get orientation).
- clockwise
-
sets the rotation directions. defaults to true (clockwise rotation).
returns the current orientation (:portrait or :landscape) if used to get the orientation. otherwise, if used to set the orientation, returns the page object to allow method chaining.
-
Notice: a square page always returns the :portrait value and is ignored when trying to set the orientation.
364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 364 def orientation force = nil, clockwise = true a = self[:CropBox] || self[:MediaBox] unless force return (a[2] - a[0] > a[3] - a[1]) ? :landscape : :portrait end unless orientation == force || (a[2] - a[0] == a[3] - a[1]) self[:Rotate] = 0; clockwise ? rotate_right : rotate_left end self end |
#resources ⇒ Object
accessor (getter) for the :Resources element of the page
47 48 49 50 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 47 def resources self[:Resources] ||= {} self[:Resources][:referenced_object] || self[:Resources] end |
#rotate_180 ⇒ Object
rotate the page by 180 degrees
350 351 352 353 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 350 def rotate_180 self[:Rotate] = self[:Rotate].to_f +180 fix_rotation end |
#rotate_left ⇒ Object
rotate the page 90 degrees counter clockwise
340 341 342 343 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 340 def rotate_left self[:Rotate] = self[:Rotate].to_f + 90 fix_rotation end |
#rotate_right ⇒ Object
rotate the page 90 degrees clockwise
345 346 347 348 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 345 def rotate_right self[:Rotate] = self[:Rotate].to_f - 90 fix_rotation end |
#secure_injection ⇒ Object
accessor (getter) for the secure_injection setting
17 18 19 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 17 def secure_injection @secure_injection end |
#secure_injection=(safe) ⇒ Object
accessor (setter) for the secure_injection setting
21 22 23 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 21 def secure_injection= safe @secure_injection = safe 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)
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/combine_pdf/combine_pdf_page.rb', line 90 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: nil # ~= [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_stream << "#{[:ctm].join ' '} cm\n" if [:ctm] box_graphic_state = { ca: [:opacity], CA: [:opacity], LW: [:border_width], LC: 0, LJ: 0, LD: 0} 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" # the following line was removed for Acrobat Reader compatability # 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.empty? && [: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_stream << "#{[:ctm].join ' '} cm\n" if [:ctm] text_graphic_state = graphic_state({ca: [:opacity], CA: [:opacity], LW: [:stroke_width].to_f, LC: 2, LJ: 1, LD: 0 }) text_stream << "#{PDFOperations._object_to_pdf text_graphic_state} gs\n" # the following line was removed for Acrobat Reader compatability # 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.round 3} 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 |