Class | PDF::Writer |
In: |
lib/pdf/writer/state.rb
lib/pdf/writer.rb |
Parent: | Object |
VERSION | = | '1.1.7' | The version of PDF::Writer. | |||||||
FONT_PATH | = | [] |
The system font path. The sytem font path will be determined differently
for each operating system.
|
|||||||
PAGE_SIZES | = | { # :value {...}: "4A0" => [0, 0, 4767.87, 6740.79], "2A0" => [0, 0, 3370.39, 4767.87], "A0" => [0, 0, 2383.94, 3370.39], "A1" => [0, 0, 1683.78, 2383.94], "A2" => [0, 0, 1190.55, 1683.78], "A3" => [0, 0, 841.89, 1190.55], "A4" => [0, 0, 595.28, 841.89], "A5" => [0, 0, 419.53, 595.28], "A6" => [0, 0, 297.64, 419.53], "A7" => [0, 0, 209.76, 297.64], "A8" => [0, 0, 147.40, 209.76], "A9" => [0, 0, 104.88, 147.40], "A10" => [0, 0, 73.70, 104.88], "B0" => [0, 0, 2834.65, 4008.19], "B1" => [0, 0, 2004.09, 2834.65], "B2" => [0, 0, 1417.32, 2004.09], "B3" => [0, 0, 1000.63, 1417.32], "B4" => [0, 0, 708.66, 1000.63], "B5" => [0, 0, 498.90, 708.66], "B6" => [0, 0, 354.33, 498.90], "B7" => [0, 0, 249.45, 354.33], "B8" => [0, 0, 175.75, 249.45], "B9" => [0, 0, 124.72, 175.75], "B10" => [0, 0, 87.87, 124.72], "C0" => [0, 0, 2599.37, 3676.54], "C1" => [0, 0, 1836.85, 2599.37], "C2" => [0, 0, 1298.27, 1836.85], "C3" => [0, 0, 918.43, 1298.27], "C4" => [0, 0, 649.13, 918.43], "C5" => [0, 0, 459.21, 649.13], "C6" => [0, 0, 323.15, 459.21], "C7" => [0, 0, 229.61, 323.15], "C8" => [0, 0, 161.57, 229.61], "C9" => [0, 0, 113.39, 161.57], "C10" => [0, 0, 79.37, 113.39], "RA0" => [0, 0, 2437.80, 3458.27], "RA1" => [0, 0, 1729.13, 2437.80], "RA2" => [0, 0, 1218.90, 1729.13], "RA3" => [0, 0, 864.57, 1218.90], "RA4" => [0, 0, 609.45, 864.57], "SRA0" => [0, 0, 2551.18, 3628.35], "SRA1" => [0, 0, 1814.17, 2551.18], "SRA2" => [0, 0, 1275.59, 1814.17], "SRA3" => [0, 0, 907.09, 1275.59], "SRA4" => [0, 0, 637.80, 907.09], "LETTER" => [0, 0, 612.00, 792.00], "LEGAL" => [0, 0, 612.00, 1008.00], "FOLIO" => [0, 0, 612.00, 936.00], "EXECUTIVE" => [0, 0, 521.86, 756.00] |
Standard page size names. One of these
may be provided to PDF::Writer.new as the
:paper parameter.
Page sizes supported are:
|
|||||||
PDF_VERSION_13 | = | '1.3' | ||||||||
PDF_VERSION_14 | = | '1.4' | ||||||||
PDF_VERSION_15 | = | '1.5' | ||||||||
PDF_VERSION_16 | = | '1.6' | ||||||||
ENCRYPT_OPTIONS | = | { #:nodoc: :print => 4, :modify => 8, :copy => 16, :add => 32 | Standard encryption/DRM options. | |||||||
TAGS | = | { :pair => { }, :single => { }, :replace => { } |
Callback tag relationships. All relationships are of the form
"tagname" => CallbackClass.
There are three types of tag callbacks:
|
absolute_bottom_margin | [R] | Returns the absolute y position of the bottom margin. | ||||||||
absolute_left_margin | [R] | The absolute x position of the left margin. | ||||||||
absolute_right_margin | [R] | The absolute x position of the right margin. | ||||||||
absolute_top_margin | [R] | Returns the absolute y position of the top margin. | ||||||||
absolute_x_middle | [R] | The absolute x middle position. | ||||||||
absolute_y_middle | [R] | The absolute y middle position. | ||||||||
bottom_margin | [RW] | |||||||||
column_count | [R] | The total number of columns. Returns zero (0) if columns are off. | ||||||||
column_gutter | [R] | The gutter between columns. This will return zero (0) if columns are off. | ||||||||
column_number | [R] | The current column number. Returns zero (0) if columns are off. | ||||||||
column_width | [R] | The width of the currently active column. This will return zero (0) if columns are off. | ||||||||
compressed | [RW] | Sets the document to compressed (true) or uncompressed (false). Defaults to uncompressed. This can ONLY be set once and should be set as early as possible in the document creation process. | ||||||||
current_base_font | [R] | |||||||||
current_contents | [R] | Returns the current contents object to which raw PDF instructions may be written. | ||||||||
current_font | [R] | |||||||||
encryption_key | [RW] | The string that will be used to encrypt this PDF document. | ||||||||
first_page | [R] | Allows the user to find out what the ID is of the first page that was created during startup - useful if they wish to add something to it later. | ||||||||
font_families | [R] |
Add a new translation table for a font
family. A font family will be used to associate a single name and font
styles with multiple fonts. A style will be identified with a
single-character style identifier or a series of style identifiers. The
only styles currently recognised are:
Each font family key is the base name for the font. |
||||||||
font_size | [RW] | |||||||||
info | [R] | The PDF::Writer::Object::Info info object. This is used to provide certain metadata. | ||||||||
left_margin | [RW] | |||||||||
margin_height | [R] | The height of the margin area. | ||||||||
margin_width | [R] | The width of the margin area. | ||||||||
margin_x_middle | [R] | The middle of the writing area between the left and right margins. | ||||||||
margin_y_middle | [R] | The middle of the writing area between the top and bottom margins. | ||||||||
page_height | [R] | |||||||||
page_width | [R] | |||||||||
pointer | [RW] | The vertical position of the writing point. If the vertical position is outside of the bottom margin, a new page will be created. | ||||||||
right_margin | [RW] | |||||||||
top_margin | [RW] | |||||||||
version | [R] | The version of PDF to which this document conforms. Should be one of PDF_VERSION_13, PDF_VERSION_14, PDF_VERSION_15, or PDF_VERSION_16. | ||||||||
y | [RW] | The vertical position of the writing point. The vertical position is constrained between the top and bottom margins. Any attempt to set it outside of those margins will cause the y pointer to be placed absolutely at the margins. |
Creates a new PDF document as a writing canvas. It accepts three named parameters:
:paper: | Specifies the size of the default page in PDF::Writer. This may be a four-element array of coordinates specifying the lower-left (xll, yll) and upper-right (xur, yur) corners, a two-element array of width and height in centimetres, or a page name as defined in PAGE_SIZES. |
:orientation: | The orientation of the page, either long (:portrait) or wide (:landscape). This may be used to swap the width and the height of the page. |
:version: | The feature set available to the document is limited by the PDF version. Setting this version restricts the feature set available to PDF::Writer. PDF::Writer currently supports PDF version 1.3 features and does not yet support advanced features from PDF 1.4, 1.5, or 1.6. |
# File lib/pdf/writer.rb, line 325 325: def initialize(options = {}) 326: paper = options[:paper] || "LETTER" 327: orientation = options[:orientation] || :portrait 328: version = options[:version] || PDF_VERSION_13 329: 330: @mutex = Mutex.new 331: @current_id = @current_font_id = 0 332: 333: # Start the document 334: @objects = [] 335: @callbacks = [] 336: @font_families = {} 337: @fonts = {} 338: @stack = [] 339: @state_stack = StateStack.new 340: @loose_objects = [] 341: @current_text_state = "" 342: @options = {} 343: @destinations = {} 344: @add_loose_objects = {} 345: @images = [] 346: @word_space_adjust = nil 347: @current_stroke_style = PDF::Writer::StrokeStyle.new(1) 348: @page_numbering = nil 349: @arc4 = nil 350: @encryption = nil 351: @file_identifier = nil 352: 353: @columns = {} 354: @columns_on = false 355: @insert_mode = nil 356: 357: @catalog = PDF::Writer::Object::Catalog.new(self) 358: @outlines = PDF::Writer::Object::Outlines.new(self) 359: @pages = PDF::Writer::Object::Pages.new(self) 360: 361: @current_node = @pages 362: @procset = PDF::Writer::Object::Procset.new(self) 363: @info = PDF::Writer::Object::Info.new(self) 364: @page = PDF::Writer::Object::Page.new(self) 365: @current_text_render_style = 0 366: @first_page = @page 367: 368: @version = version 369: 370: # Initialize the default font families. 371: init_font_families 372: 373: @font_size = 10 374: @pageset = [@pages.first_page] 375: 376: if paper.kind_of?(Array) 377: if paper.size == 4 378: size = paper # Coordinate Array 379: else 380: size = [0, 0, PDF::Writer.cm2pts(paper[0]), PDF::Writer.cm2pts(paper[1])] 381: # Paper size in centimeters has been passed 382: end 383: else 384: size = PAGE_SIZES[paper.upcase].dup 385: end 386: size[3], size[2] = size[2], size[3] if orientation == :landscape 387: 388: @pages.media_box = size 389: 390: @page_width = size[2] - size[0] 391: @page_height = size[3] - size[1] 392: @y = @page_height 393: 394: # Also set the margins to some reasonable defaults -- 1.27 cm, 36pt, 395: # or 0.5 inches. 396: margins_pt(36) 397: 398: # Set the current writing position to the top of the first page 399: @y = absolute_top_margin 400: # Get the ID of the page that was created during the instantiation 401: # process. 402: 403: fill_color! Color::RGB::Black 404: stroke_color! Color::RGB::Black 405: 406: yield self if block_given? 407: end
Create the document with prepress options. Uses the same options as PDF::Writer.new (:paper, :orientation, and :version). It also supports the following options:
:left_margin: | The left margin. |
:right_margin: | The right margin. |
:top_margin: | The top margin. |
:bottom_margin: | The bottom margin. |
:bleed_size: | The size of the bleed area in points. Default 12. |
:mark_length: | The length of the prepress marks in points. Default 18. |
The prepress marks are added to the loose objects and will appear on all pages.
# File lib/pdf/writer.rb, line 169 169: def prepress(options = { }) 170: pdf = self.new(options) 171: 172: bleed_size = options[:bleed_size] || 12 173: mark_length = options[:mark_length] || 18 174: 175: pdf.left_margin = options[:left_margin] if options[:left_margin] 176: pdf.right_margin = options[:right_margin] if options[:right_margin] 177: pdf.top_margin = options[:top_margin] if options[:top_margin] 178: pdf.bottom_margin = options[:bottom_margin] if options[:bottom_margin] 179: 180: # This is in an "odd" order because the y-coordinate system in PDF 181: # is from bottom to top. 182: tx0 = pdf.pages.media_box[0] + pdf.left_margin 183: ty0 = pdf.pages.media_box[3] - pdf.top_margin 184: tx1 = pdf.pages.media_box[2] - pdf.right_margin 185: ty1 = pdf.pages.media_box[1] + pdf.bottom_margin 186: 187: bx0 = tx0 - bleed_size 188: by0 = ty0 - bleed_size 189: bx1 = tx1 + bleed_size 190: by1 = ty1 + bleed_size 191: 192: pdf.pages.trim_box = [ tx0, ty0, tx1, ty1 ] 193: pdf.pages.bleed_box = [ bx0, by0, bx1, by1 ] 194: 195: all = pdf.open_object 196: pdf.save_state 197: kk = Color::CMYK.new(0, 0, 0, 100) 198: pdf.stroke_color! kk 199: pdf.fill_color! kk 200: pdf.stroke_style! StrokeStyle.new(0.3) 201: 202: pdf.prepress_clip_mark(tx1, ty0, 0, mark_length, bleed_size) # Upper Right 203: pdf.prepress_clip_mark(tx0, ty0, 90, mark_length, bleed_size) # Upper Left 204: pdf.prepress_clip_mark(tx0, ty1, 180, mark_length, bleed_size) # Lower Left 205: pdf.prepress_clip_mark(tx1, ty1, -90, mark_length, bleed_size) # Lower Right 206: 207: mid_x = pdf.pages.media_box[2] / 2.0 208: mid_y = pdf.pages.media_box[3] / 2.0 209: 210: pdf.prepress_center_mark(mid_x, ty0, 0, mark_length, bleed_size) # Centre Top 211: pdf.prepress_center_mark(tx0, mid_y, 90, mark_length, bleed_size) # Centre Left 212: pdf.prepress_center_mark(mid_x, ty1, 180, mark_length, bleed_size) # Centre Bottom 213: pdf.prepress_center_mark(tx1, mid_y, -90, mark_length, bleed_size) # Centre Right 214: 215: pdf.restore_state 216: pdf.close_object 217: pdf.add_object(all, :all) 218: 219: yield pdf if block_given? 220: 221: pdf 222: end
Parse the fonts.conf XML file.
# File lib/pdf/writer.rb, line 94 94: def parse_fonts_conf(filename) 95: doc = REXML::Document.new(File.open(filename, "rb")).root rescue nil 96: 97: if doc 98: path = REXML::XPath.match(doc, '//dir').map do |el| 99: el.text.gsub($/, '') 100: end 101: doc = nil 102: else 103: path = [] 104: end 105: path 106: end
memory improvement for transaction-simple
# File lib/pdf/writer.rb, line 2754 2754: def _post_transaction_rewind 2755: @objects.each { |e| e.instance_variable_set(:@parent,self) } 2756: end
add content to the currently active object
# File lib/pdf/writer.rb, line 1058 1058: def add_content(cc) 1059: @current_contents << cc 1060: end
Create a labelled destination within the document. The label is the name which will be used for <c:ilink> destinations.
XYZ: | The viewport will be opened at position (left, top) with zoom percentage. params must have three values representing left, top, and zoom, respectively. If the values are "null", the current parameter values are unchanged. |
Fit: | Fit the page to the viewport (horizontal and vertical). params will be ignored. |
FitH: | Fit the page horizontally to the viewport. The top of the viewport is set to the first value in params. |
FitV: | Fit the page vertically to the viewport. The left of the viewport is set to the first value in params. |
FitR: | Fits the page to the provided rectangle. params must have four values representing the left, bottom, right, and top positions, respectively. |
FitB: | Fits the page to the bounding box of the page. params is ignored. |
FitBH: | Fits the page horizontally to the bounding box of the page. The top position is defined by the first value in params. |
FitBV: | Fits the page vertically to the bounding box of the page. The left position is defined by the first value in params. |
# File lib/pdf/writer.rb, line 1885 1885: def add_destination(label, style, *params) 1886: @destinations[label] = PDF::Writer::Object::Destination.new(self, @current_page, style, *params) 1887: end
Add content to the documents info object.
# File lib/pdf/writer.rb, line 1834 1834: def add_info(label, value = 0) 1835: # This will only work if the label is one of the valid ones. Modify 1836: # this so that arrays can be passed as well. If @label is an array 1837: # then assume that it is key => value pairs else assume that they are 1838: # both scalar, anything else will probably error. 1839: if label.kind_of?(Hash) 1840: label.each { |kk, vv| @info.__send__(kk.downcase.intern, vv) } 1841: else 1842: @info.__send__(label.downcase.intern, value) 1843: end 1844: end
Add a link in the document to an internal destination (ie. within the document)
# File lib/pdf/writer.rb, line 681 681: def add_internal_link(label, x0, y0, x1, y1) 682: PDF::Writer::Object::Annotation.new(self, :ilink, [x0, y0, x1, y1], label) 683: end
Add a link in the document to an external URL.
# File lib/pdf/writer.rb, line 675 675: def add_link(uri, x0, y0, x1, y1) 676: PDF::Writer::Object::Annotation.new(self, :link, [x0, y0, x1, y1], uri) 677: end
After an object has been created, it will only show if it has been added, using this method.
# File lib/pdf/writer.rb, line 1807 1807: def add_object(id, where = :this_page) 1808: obj = @loose_objects.detect { |ii| ii == id } 1809: 1810: if obj and @current_contents != obj 1811: case where 1812: when :all_pages, :this_page 1813: @add_loose_objects[obj] = where if where == :all_pages 1814: @current_contents.on_page.contents << obj if @current_contents.on_page 1815: when :even_pages 1816: @add_loose_objects[obj] = where 1817: page = @current_contents.on_page 1818: add_object(id) if (page.info.page_number % 2) == 0 1819: when :odd_pages 1820: @add_loose_objects[obj] = where 1821: page = @current_contents.on_page 1822: add_object(id) if (page.info.page_number % 2) == 1 1823: when :all_following_pages 1824: @add_loose_objects[obj] = :all_pages 1825: when :following_even_pages 1826: @add_loose_objects[obj] = :even_pages 1827: when :following_odd_pages 1828: @add_loose_objects[obj] = :odd_pages 1829: end 1830: end 1831: end
Add an outline item (Bookmark).
# File lib/pdf/writer.rb, line 686 686: def add_outline_item(label, title = label) 687: PDF::Writer::Object::Outline.new(self, label, title) 688: end
Add text to the document at (x, y) location at size and angle. The word_space_adjust parameter is an internal parameter that should not be used.
As of PDF::Writer 1.1, size and text have been reversed and size is now optional, defaulting to the current font_size if unset.
# File lib/pdf/writer.rb, line 1391 1391: def add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0) 1392: if text.kind_of?(Numeric) and size.kind_of?(String) 1393: text, size = size, text 1394: warn PDF::Writer::Lang[:add_text_parameters_reversed] % caller[0] 1395: end 1396: 1397: if size.nil? or size <= 0 1398: size = @font_size 1399: end 1400: 1401: select_font("Helvetica") if @fonts.empty? 1402: 1403: text = text.to_s 1404: 1405: # If there are any open callbacks, then they should be called, to show 1406: # the start of the line 1407: @callbacks.reverse_each do |ii| 1408: info = ii.dup 1409: info[:x] = x 1410: info[:y] = y 1411: info[:angle] = angle 1412: info[:status] = :start_line 1413: 1414: info[:tag][self, info] 1415: end 1416: if angle == 0 1417: add_content("\nBT %.3f %.3f Td" % [x, y]) 1418: else 1419: rad = PDF::Math.deg2rad(angle) 1420: tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm" 1421: tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ] 1422: add_content(tt) 1423: end 1424: 1425: if (word_space_adjust != 0) or not ((@word_space_adjust.nil?) and (@word_space_adjust != word_space_adjust)) 1426: @word_space_adjust = word_space_adjust 1427: add_content(" %.3f Tw" % word_space_adjust) 1428: end 1429: 1430: pos = -1 1431: start = 0 1432: loop do 1433: pos += 1 1434: break if pos == text.size 1435: font_change = true 1436: tag_size, text, font_change = quick_text_tags(text, pos, font_change) 1437: 1438: if tag_size != 0 1439: if pos > start 1440: part = text[start, pos - start] 1441: tt = " /F#{find_font(@current_font).font_id}" 1442: tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ] 1443: tt << " (#{PDF::Writer.escape(part)}) Tj" 1444: add_content(tt) 1445: end 1446: 1447: if font_change 1448: current_font! 1449: else 1450: add_content(" ET") 1451: xp = x 1452: yp = y 1453: tag_size, text, font_change, xp, yp = text_tags(text, pos, font_change, true, xp, yp, size, angle, word_space_adjust) 1454: 1455: # Restart the text object 1456: if angle.zero? 1457: add_content("\nBT %.3f %.3f Td" % [xp, yp]) 1458: else 1459: rad = PDF::Math.deg2rad(angle) 1460: tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm" 1461: tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), xp, yp ] 1462: add_content(tt) 1463: end 1464: 1465: if (word_space_adjust != 0) or (word_space_adjust != @word_space_adjust) 1466: @word_space_adjust = word_space_adjust 1467: add_content(" %.3f Tw" % [word_space_adjust]) 1468: end 1469: end 1470: 1471: pos += tag_size - 1 1472: start = pos + 1 1473: end 1474: end 1475: 1476: if start < text.size 1477: part = text[start..-1] 1478: 1479: tt = " /F#{find_font(@current_font).font_id}" 1480: tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ] 1481: tt << " (#{PDF::Writer.escape(part)}) Tj" 1482: add_content(tt) 1483: end 1484: add_content(" ET") 1485: 1486: # XXX: Experimental fix. 1487: @callbacks.reverse_each do |ii| 1488: info = ii.dup 1489: info[:x] = x 1490: info[:y] = y 1491: info[:angle] = angle 1492: info[:status] = :end_line 1493: info[:tag][self, info] 1494: end 1495: end
Add text to the page, but ensure that it fits within a certain width. If it does not fit then put in as much as possible, breaking at word boundaries; return the remainder. justification and angle can also be specified for the text.
This will display the text; if it goes beyond the width width, it will backttrack to the previous space or hyphen and return the remainder of the text.
justification: | :left, :right, :center, or :full |
# File lib/pdf/writer.rb, line 1633 1633: def add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false) 1634: if text.kind_of?(Numeric) and size.kind_of?(String) 1635: text, size = size, text 1636: warn PDF::Writer::Lang[:add_textw_parameters_reversed] % caller[0] 1637: end 1638: 1639: if size.nil? or size <= 0 1640: size = @font_size 1641: end 1642: 1643: # Need to store the initial text state, as this will change during the 1644: # width calculation, but will need to be re-set before printing, so 1645: # that the chars work out right 1646: t_CTS = @current_text_state.dup 1647: 1648: select_font("Helvetica") if @fonts.empty? 1649: return "" if width <= 0 1650: 1651: w = brk = brkw = 0 1652: font = @current_font 1653: tw = width / size.to_f * 1000 1654: 1655: pos = -1 1656: loop do 1657: pos += 1 1658: break if pos == text.size 1659: font_change = true 1660: tag_size, text, font_change = quick_text_tags(text, pos, font_change) 1661: if tag_size != 0 1662: if font_change 1663: current_font! 1664: font = @current_font 1665: end 1666: pos += (tag_size - 1) 1667: else 1668: w += char_width(font, text[pos, 1]) 1669: 1670: if w > tw # We need to truncate this line 1671: if brk > 0 # There is somewhere to break the line. 1672: if text[brk] == " " 1673: tmp = text[0, brk] 1674: else 1675: tmp = text[0, brk + 1] 1676: end 1677: x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification) 1678: 1679: # Reset the text state 1680: @current_text_state = t_CTS.dup 1681: current_font! 1682: add_text(x, y, tmp, size, angle, adjust) unless test 1683: return text[brk + 1..-1] 1684: else # just break before the current character 1685: tmp = text[0, pos] 1686: # tmpw = (w - char_width(font, text[pos, 1])) * size / 1000.0 1687: x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification) 1688: 1689: # Reset the text state 1690: @current_text_state = t_CTS.dup 1691: current_font! 1692: add_text(x, y, tmp, size, angle, adjust) unless test 1693: return text[pos..-1] 1694: end 1695: end 1696: 1697: if text[pos] == ?- 1698: brk = pos 1699: brkw = w * size / 1000.0 1700: end 1701: 1702: if text[pos, 1] == " " 1703: brk = pos 1704: ctmp = text[pos] 1705: ctmp = @fonts[font].differences[ctmp] unless @fonts[font].differences.nil? 1706: z = @fonts[font].c[tmp].nil? ? 0 : @fonts[font].c[tmp]['WX'] 1707: brkw = (w - z) * size / 1000.0 1708: end 1709: end 1710: end 1711: 1712: # There was no need to break this line. 1713: justification = :left if justification == :full 1714: tmpw = (w * size) / 1000.0 1715: x, adjust = adjust_wrapped_text(text, tmpw, width, x, justification) 1716: # reset the text state 1717: @current_text_state = t_CTS.dup 1718: current_font! 1719: add_text(x, y, text, size, angle, adjust) unless test 1720: return "" 1721: end
Changes the insert_page property to append to the page set.
# File lib/pdf/writer.rb, line 2045 2045: def append_page 2046: insert_mode(:last) 2047: end
Sets the bleed box area.
# File lib/pdf/writer.rb, line 657 657: def bleed_box(x0, y0, x1, y1) 658: @pages.bleed_box = [ x0, y0, x1, y1 ] 659: end
should be used for internal checks, not implemented as yet
# File lib/pdf/writer.rb, line 699 699: def check_all_here 700: end
Close an object for writing.
# File lib/pdf/writer.rb, line 1791 1791: def close_object 1792: unless @stack.empty? 1793: obj = @stack.pop 1794: @current_contents = obj[:contents] 1795: @current_page = obj[:page] 1796: end 1797: end
Indicates if columns are currently on.
# File lib/pdf/writer.rb, line 1931 1931: def columns? 1932: @columns_on 1933: end
Returns true if the document is compressed.
# File lib/pdf/writer.rb, line 438 438: def compressed? 439: @compressed == true 440: end
Selects the current font based on defined font families and the current text state. As noted in font_families, a "bi" font can be defined differently than an "ib" font. It should not be possible to have a "bb" text state, but if one were to show up, an entry for the font_families would have to be defined to select anything other than the default font. This function is to be called whenever the current text state is changed; it will update the current font to whatever the appropriate font defined in the font family.
When the user calls select_font, both the current base font and the current font will be reset; this function only changes the current font, not the current base font.
This will probably not be needed by end users.
# File lib/pdf/writer.rb, line 1026 1026: def current_font! 1027: select_font("Helvetica") unless @current_base_font 1028: 1029: font = File.basename(@current_base_font) 1030: if @font_families[font] and @font_families[font][@current_text_state] 1031: # Then we are in some state or another and this font has a family, 1032: # and the current setting exists within it select the font, then 1033: # return it. 1034: if File.dirname(@current_base_font) != '.' 1035: nf = File.join(File.dirname(@current_base_font), @font_families[font][@current_text_state]) 1036: else 1037: nf = @font_families[font][@current_text_state] 1038: end 1039: 1040: unless @fonts[nf] 1041: enc = { 1042: :encoding => @fonts[font].encoding, 1043: :differences => @fonts[font].differences 1044: } 1045: load_font(nf, enc) 1046: end 1047: @current_font = nf 1048: else 1049: @current_font = @current_base_font 1050: end 1051: end
Return the font descender, this will normally return a negative number. If you add this number to the baseline, you get the level of the bottom of the font it is in the PDF user units. Uses the current font_size if size is not provided.
# File lib/pdf/writer.rb, line 1076 1076: def font_descender(size = nil) 1077: size = @font_size if size.nil? or size <= 0 1078: 1079: select_font("Helvetica") if @fonts.empty? 1080: hi = @fonts[@current_font].fontbbox[1].to_f 1081: (size * hi / 1000.0) 1082: end
Return the height in units of the current font in the given size. Uses the current font_size if size is not provided.
# File lib/pdf/writer.rb, line 1064 1064: def font_height(size = nil) 1065: size = @font_size if size.nil? or size <= 0 1066: 1067: select_font("Helvetica") if @fonts.empty? 1068: hh = @fonts[@current_font].fontbbox[3].to_f - @fonts[@current_font].fontbbox[1].to_f 1069: (size * hh / 1000.0) 1070: end
Changes page insert mode. May be called as follows:
pdf.insert_mode # => current insert mode # The following four affect the insert mode without changing the # insert page or insert position. pdf.insert_mode(:on) # enables insert mode pdf.insert_mode(true) # enables insert mode pdf.insert_mode(:off) # disables insert mode pdf.insert_mode(false) # disables insert mode # Changes the insert mode, the insert page, and the insert # position at the same time. opts = { :on => true, :page => :last, :position => :before } pdf.insert_mode(opts)
# File lib/pdf/writer.rb, line 2009 2009: def insert_mode(options = {}) 2010: case options 2011: when :on, true 2012: @insert_mode = true 2013: when :off, false 2014: @insert_mode = false 2015: else 2016: return @insert_mode unless options 2017: 2018: @insert_mode = options[:on] unless options[:on].nil? 2019: 2020: unless options[:page].nil? 2021: if @pageset[options[:page]].nil? or options[:page] == :last 2022: @insert_page = @pageset[-1] 2023: else 2024: @insert_page = @pageset[options[:page]] 2025: end 2026: end 2027: 2028: @insert_position = options[:position] if options[:position] 2029: end 2030: end
Returns or changes the insert page property.
pdf.insert_page # => current insert page pdf.insert_page(35) # insert at page 35 pdf.insert_page(:last) # insert at the last page
# File lib/pdf/writer.rb, line 2036 2036: def insert_page(page = nil) 2037: return @insert_page unless page 2038: if page == :last 2039: @insert_page = @pageset[-1] 2040: else 2041: @insert_page = @pageset[page] 2042: end 2043: end
Returns or changes the insert position to be before or after the specified page.
pdf.insert_position # => current insert position pdf.insert_position(:before) # insert before #insert_page pdf.insert_position(:after) # insert before #insert_page
# File lib/pdf/writer.rb, line 2054 2054: def insert_position(position = nil) 2055: return @insert_position unless position 2056: @insert_position = position 2057: end
Returns the estimated number of lines remaining given the default or specified font size.
# File lib/pdf/writer.rb, line 2475 2475: def lines_remaining(font_size = nil) 2476: font_size ||= @font_size 2477: remaining = @y - @bottom_margin 2478: remaining / font_height(font_size).to_f 2479: end
Define the margins in centimetres.
# File lib/pdf/writer.rb, line 566 566: def margins_cm(top, left = top, bottom = top, right = left) 567: margins_pt(cm2pts(top), cm2pts(left), cm2pts(bottom), cm2pts(right)) 568: end
Define the margins in inches.
# File lib/pdf/writer.rb, line 571 571: def margins_in(top, left = top, bottom = top, right = left) 572: margins_pt(in2pts(top), in2pts(left), in2pts(bottom), in2pts(right)) 573: end
Define the margins in millimetres.
# File lib/pdf/writer.rb, line 561 561: def margins_mm(top, left = top, bottom = top, right = left) 562: margins_pt(mm2pts(top), mm2pts(left), mm2pts(bottom), mm2pts(right)) 563: end
Define the margins in points. This will move the y pointer
# T L B R pdf.margins_pt(36) # 36 36 36 36 pdf.margins_pt(36, 54) # 36 54 36 54 pdf.margins_pt(36, 54, 72) # 36 54 72 54 pdf.margins_pt(36, 54, 72, 90) # 36 54 72 90
# File lib/pdf/writer.rb, line 582 582: def margins_pt(top, left = top, bottom = top, right = left) 583: # Set the margins to new values 584: @top_margin = top 585: @bottom_margin = bottom 586: @left_margin = left 587: @right_margin = right 588: # Check to see if this means that the current writing position is 589: # outside the writable area 590: if @y > (@page_height - top) 591: # Move y down 592: @y = @page_height - top 593: end 594: 595: start_new_page if @y < bottom # Make a new page 596: end
Used to change the vertical position of the writing point. The pointer is moved down the page by dy (that is, y is reduced by dy), so if the pointer is to be moved up, a negative number must be used. Moving up the page will not move to the previous page because of limitations in the way that PDF::Writer works. The writing point will be limited to the top margin position.
If make_space is true and a new page is forced, then the pointer will be moved down on the new page. This will allow space to be reserved for graphics.
# File lib/pdf/writer.rb, line 550 550: def move_pointer(dy, make_space = false) 551: @y -= dy 552: if @y < @bottom_margin 553: start_new_page 554: @y -= dy if make_space 555: elsif @y > absolute_top_margin 556: @y = absolute_top_margin 557: end 558: end
Add a new page to the document. This also makes the new page the current active object. This allows for mandatory page creation regardless of multi-column output.
For most purposes, start_new_page is preferred.
# File lib/pdf/writer.rb, line 2113 2113: def new_page(insert = false, page = nil, pos = :after) 2114: reset_state_at_page_finish 2115: 2116: if insert 2117: # The id from the PDF::Writer class is the id of the contents of the 2118: # page, not the page object itself. Query that object to find the 2119: # parent. 2120: _new_page = PDF::Writer::Object::Page.new(self, { :rpage => page, :pos => pos }) 2121: else 2122: _new_page = PDF::Writer::Object::Page.new(self) 2123: end 2124: 2125: reset_state_at_page_start 2126: 2127: # If there has been a stroke or fill color set, transfer them. 2128: fill_color! 2129: stroke_color! 2130: stroke_style! 2131: 2132: # the call to the page object set @current_contents to the present page, 2133: # so this can be returned as the page id 2134: # @current_contents 2135: _new_page 2136: end
Specify the Destination object where the document should open when it first starts. style must be one of the following values. The value of style affects the interpretation of params. Uses page as the starting location.
# File lib/pdf/writer.rb, line 1858 1858: def open_at(page, style, *params) 1859: d = PDF::Writer::Object::Destination.new(self, page, style, *params) 1860: @catalog.open_here = d 1861: end
Specify the Destination object where the document should open when it first starts. style must be one of the values detailed for destinations. The value of style affects the interpretation of params. Uses the current page as the starting location.
# File lib/pdf/writer.rb, line 1850 1850: def open_here(style, *params) 1851: open_at(@current_page, style, *params) 1852: end
Opens a new PDF object for operating against. Returns the object‘s identifier. To close the object, you‘ll need to do:
ob = open_new_object # Opens the object # do stuff here close_object # Closes the PDF document # do stuff here reopen_object(ob) # Reopens the custom object. close_object # Closes it. restore_state # Returns full control to the PDF document.
… I think. I haven‘t examined the full details to be sure of what this is doing, but the code works.
# File lib/pdf/writer.rb, line 2739 2739: def open_new_object 2740: save_state 2741: oid = open_object 2742: close_object 2743: add_object(oid) 2744: reopen_object(oid) 2745: oid 2746: end
Make a loose object. The output will go into this object, until it is closed, then will revert to the current one. This object will not appear until it is included within a page. The function will return the object reference.
# File lib/pdf/writer.rb, line 1772 1772: def open_object 1773: @stack << { :contents => @current_contents, :page => @current_page } 1774: @current_contents = PDF::Writer::Object::Contents.new(self) 1775: @loose_objects << @current_contents 1776: yield @current_contents if block_given? 1777: @current_contents 1778: end
Set the page mode of the catalog. Must be one of the following:
UseNone: | Neither document outline nor thumbnail images are visible. |
UseOutlines: | Document outline visible. |
UseThumbs: | Thumbnail images visible. |
FullScreen: | Full-screen mode, with no menu bar, window controls, or any other window visible. |
UseOC: | Optional content group panel is visible. |
# File lib/pdf/writer.rb, line 1898 1898: def page_mode=(mode) 1899: @catalog.page_mode = value 1900: end
Return the PDF stream as a string.
# File lib/pdf/writer.rb, line 703 703: def render(debug = false) 704: add_page_numbers 705: @compression = false if $DEBUG or debug 706: @arc4.init(@encryption_key) unless @arc4.nil? 707: 708: check_all_here 709: 710: xref = [] 711: 712: content = "%PDF-#{@version}\n%âãÏÓ\n" 713: pos = content.size 714: 715: objects.each do |oo| 716: cont = oo.to_s 717: content << cont 718: xref << pos 719: pos += cont.size 720: end 721: 722: # pos += 1 # Newline character before XREF 723: 724: content << "\nxref\n0 #{xref.size + 1}\n0000000000 65535 f \n" 725: xref.each { |xx| content << "#{'%010d' % [xx]} 00000 n \n" } 726: content << "\ntrailer\n" 727: content << " << /Size #{xref.size + 1}\n" 728: content << " /Root 1 0 R\n /Info #{@info.oid} 0 R\n" 729: # If encryption has been applied to this document, then add the marker 730: # for this dictionary 731: if @arc4 and @encryption 732: content << "/Encrypt #{@encryption.oid} 0 R\n" 733: end 734: 735: if @file_identifier 736: content << "/ID[<#{@file_identifier}><#{@file_identifier}>]\n" 737: end 738: content << " >>\nstartxref\n#{pos}\n%%EOF\n" 739: content 740: end
Opens an existing object for editing.
# File lib/pdf/writer.rb, line 1781 1781: def reopen_object(id) 1782: @stack << { :contents => @current_contents, :page => @current_page } 1783: @current_contents = id 1784: # if this object is the primary contents for a page, then set the 1785: # current page to its parent 1786: @current_page = @current_contents.on_page unless @current_contents.on_page.nil? 1787: @current_contents 1788: end
Restore a previously saved state.
# File lib/pdf/writer.rb, line 1750 1750: def restore_state 1751: unless @state_stack.empty? 1752: state = @state_stack.pop 1753: @current_fill_color = state.fill_color 1754: @current_stroke_color = state.stroke_color 1755: @current_text_render_style = state.text_render_style 1756: @current_stroke_style = state.stroke_style 1757: stroke_style! 1758: end 1759: add_content("\nQ") 1760: end
Saves the state.
# File lib/pdf/writer.rb, line 1724 1724: def save_state 1725: PDF::Writer::State.new do |state| 1726: state.fill_color = @current_fill_color 1727: state.stroke_color = @current_stroke_color 1728: state.text_render_style = @current_text_render_style 1729: state.stroke_style = @current_stroke_style 1730: @state_stack.push state 1731: end 1732: add_content("\nq") 1733: end
If the named font is not loaded, then load it and make the required PDF objects to represent the font. If the font is already loaded, then make it the current font.
The parameter encoding applies only when the font is first being loaded; it may not be applied later. It may either be an encoding name or a hash. The Hash must contain two keys:
:encoding: | The name of the encoding. Either none, WinAnsiEncoding, MacRomanEncoding, or MacExpertEncoding. For symbolic fonts, an encoding of none is recommended with a differences Hash. |
:differences: | This Hash value is a mapping between character byte values (0 .. 255) and character names from the AFM file for the font. |
The standard PDF encodings are detailed fully in the PDF Reference version 1.6, Appendix D.
Note that WinAnsiEncoding is not the same as Windows code page 1252 (roughly equivalent to latin-1), Most characters map, but not all. The encoding value currently defaults to WinAnsiEncoding.
If the font‘s "natural" encoding is desired, then it is necessary to specify the encoding parameter as { :encoding => nil }.
# File lib/pdf/writer.rb, line 1004 1004: def select_font(font, encoding = nil) 1005: load_font(font, encoding) unless @fonts[font] 1006: 1007: @current_base_font = font 1008: current_font! 1009: @current_base_font 1010: end
Starts multi-column output. Creates size number of columns with a gutter PDF unit space between each column.
If columns are already started, this will return false.
# File lib/pdf/writer.rb, line 1939 1939: def start_columns(size = 2, gutter = 10) 1940: # Start from the current y-position; make the set number of columns. 1941: return false if @columns_on 1942: 1943: @columns = { 1944: :current => 1, 1945: :bot_y => @y 1946: } 1947: @columns_on = true 1948: # store the current margins 1949: @columns[:left] = @left_margin 1950: @columns[:right] = @right_margin 1951: @columns[:top] = @top_margin 1952: @columns[:bottom] = @bottom_margin 1953: # Reset the margins to suit the new columns. Safe enough to assume the 1954: # first column here, but start from the current y-position. 1955: @top_margin = @page_height - @y 1956: @columns[:size] = size || 2 1957: @columns[:gutter] = gutter || 10 1958: w = absolute_right_margin - absolute_left_margin 1959: @columns[:width] = (w - ((size - 1) * gutter)) / size.to_f 1960: @right_margin = @page_width - (@left_margin + @columns[:width]) 1961: end
Creates a new page. If multi-column output is turned on, this will change the column to the next greater or create a new page as necessary. If force is true, then a new page will be created even if multi-column output is on.
# File lib/pdf/writer.rb, line 2063 2063: def start_new_page(force = false) 2064: page_required = true 2065: 2066: if @columns_on 2067: # Check if this is just going to a new column. Increment the column 2068: # number. 2069: @columns[:current] += 1 2070: 2071: if @columns[:current] <= @columns[:size] and not force 2072: page_required = false 2073: @columns[:bot_y] = @y if @y < @columns[:bot_y] 2074: else 2075: @columns[:current] = 1 2076: @top_margin = @columns[:top] 2077: @columns[:bot_y] = absolute_top_margin 2078: end 2079: 2080: w = @columns[:width] 2081: g = @columns[:gutter] 2082: n = @columns[:current] - 1 2083: @left_margin = @columns[:left] + n * (g + w) 2084: @right_margin = @page_width - (@left_margin + w) 2085: end 2086: 2087: if page_required or force 2088: # make a new page, setting the writing point back to the top. 2089: @y = absolute_top_margin 2090: # make the new page with a call to the basic class 2091: if @insert_mode 2092: id = new_page(true, @insert_page, @insert_position) 2093: @pageset << id 2094: # Manipulate the insert options so that inserted pages follow each 2095: # other 2096: @insert_page = id 2097: @insert_position = :after 2098: else 2099: @pageset << new_page 2100: end 2101: 2102: else 2103: @y = absolute_top_margin 2104: end 2105: @pageset 2106: end
Put page numbers on the pages from the current page. Place them relative to the coordinates (x, y) with the text horizontally relative according to pos, which may be :left, :right, or :center. The page numbers will be written on each page using pattern.
When pattern is rendered, <PAGENUM> will be replaced with the current page number; <TOTALPAGENUM> will be replaced with the total number of pages in the page numbering scheme. The default pattern is "<PAGENUM> of <TOTALPAGENUM>".
Each time page numbers are started, a new page number scheme will be started. The scheme number will be returned.
# File lib/pdf/writer.rb, line 2158 2158: def start_page_numbering(x, y, size, pos = nil, pattern = nil, starting = nil) 2159: pos ||= :left 2160: pattern ||= "<PAGENUM> of <TOTALPAGENUM>" 2161: starting ||= 1 2162: 2163: @page_numbering ||= [] 2164: @page_numbering << (o = {}) 2165: 2166: page = @pageset.size - 1 2167: o[page] = { 2168: :x => x, 2169: :y => y, 2170: :pos => pos, 2171: :pattern => pattern, 2172: :starting => starting, 2173: :size => size, 2174: :start => true 2175: } 2176: @page_numbering.index(o) 2177: end
Turns off multi-column output. If we are in the first column, or the lowest point at which columns were written is higher than the bottom of the page, then the writing pointer will be placed at the lowest point. Otherwise, a new page will be started.
# File lib/pdf/writer.rb, line 1975 1975: def stop_columns 1976: return false unless @columns_on 1977: @columns_on = false 1978: 1979: @columns[:bot_y] = @y if @y < @columns[:bot_y] 1980: 1981: if (@columns[:bot_y] > @bottom_margin) or @column_number == 1 1982: @y = @columns[:bot_y] 1983: else 1984: start_new_page 1985: end 1986: restore_margins_after_columns 1987: @columns = {} 1988: true 1989: end
Stop an object from appearing on pages from this point on.
# File lib/pdf/writer.rb, line 1800 1800: def stop_object(id) 1801: obj = @loose_objects.detect { |ii| ii.oid == id.oid } 1802: @add_loose_objects[obj] = nil 1803: end
Stop page numbering. Returns false if page numbering is off.
If stop_total is true, then then the totaling of pages for this page numbering scheme will be stopped as well. If stop_at is :current, then the page numbering will stop at this page; otherwise, it will stop at the next page.
This method has been dprecated.
# File lib/pdf/writer.rb, line 2216 2216: def stop_page_numbering(stop_total = false, stop_at = :current, scheme = 0) 2217: return false unless @page_numbering 2218: 2219: page = @pageset.size - 1 2220: 2221: @page_numbering[scheme][page] ||= {} 2222: o = @page_numbering[scheme][page] 2223: 2224: case [ stop_total, stop_at == :current ] 2225: when [ true, true ] 2226: o[:stop] = :stop_total 2227: when [ true, false ] 2228: o[:stop] = :stop_total_next 2229: when [ false, true ] 2230: o[:stop] = :stop_next 2231: else 2232: o[:stop] = :stop 2233: end 2234: end
This will add a string of text to the document, starting at the current drawing position. It will wrap to keep within the margins, including optional offsets from the left and the right. The text will go to the start of the next line when a return code "\n" is found.
Possible options are:
:font_size: | The font size to be used. If not specified, is either the last font size or the default font size of 12 points. Setting this value changes the current font_size. |
:left: | number, gap to leave from the left margin |
:right: | number, gap to leave from the right margin |
:absolute_left: | number, absolute left position (overrides :left) |
:absolute_right: | number, absolute right position (overrides :right) |
:justification: | :left, :right, :center, :full |
:leading: | number, defines the total height taken by the line, independent of the font height. |
:spacing: | a Floating point number, though usually set to one of 1, 1.5, 2 (line spacing as used in word processing) |
Only one of :leading or :spacing should be specified (leading overrides spacing).
If the :test option is true, then this should just check to see if the text is flowing onto a new page or not; returns true or false. Note that the new page test is only sensitive to exceeding the bottom margin of the page. It is not known whether the writing of the text will require a new physical page or whether it will require a new column.
# File lib/pdf/writer.rb, line 2363 2363: def text(text, options = {}) 2364: # Apply the filtering which will make underlining (and other items) 2365: # function. 2366: text = preprocess_text(text) 2367: 2368: options ||= {} 2369: 2370: new_page_required = false 2371: __y = @y 2372: 2373: if options[:absolute_left] 2374: left = options[:absolute_left] 2375: else 2376: left = @left_margin 2377: left += options[:left] if options[:left] 2378: end 2379: 2380: if options[:absolute_right] 2381: right = options[:absolute_right] 2382: else 2383: right = absolute_right_margin 2384: right -= options[:right] if options[:right] 2385: end 2386: 2387: size = options[:font_size] || 0 2388: if size <= 0 2389: size = @font_size 2390: else 2391: @font_size = size 2392: end 2393: 2394: just = options[:justification] || :left 2395: 2396: if options[:leading] # leading instead of spacing 2397: height = options[:leading] 2398: elsif options[:spacing] 2399: height = options[:spacing] * font_height(size) 2400: else 2401: height = font_height(size) 2402: end 2403: 2404: text.each do |line| 2405: start = true 2406: loop do # while not line.empty? or start 2407: break if (line.nil? or line.empty?) and not start 2408: 2409: start = false 2410: 2411: @y -= height 2412: 2413: if @y < @bottom_margin 2414: if options[:test] 2415: new_page_required = true 2416: else 2417: # and then re-calc the left and right, in case they have 2418: # changed due to columns 2419: start_new_page 2420: @y -= height 2421: 2422: if options[:absolute_left] 2423: left = options[:absolute_left] 2424: else 2425: left = @left_margin 2426: left += options[:left] if options[:left] 2427: end 2428: 2429: if options[:absolute_right] 2430: right = options[:absolute_right] 2431: else 2432: right = absolute_right_margin 2433: right -= options[:right] if options[:right] 2434: end 2435: end 2436: end 2437: 2438: line = add_text_wrap(left, @y, right - left, line, size, just, 0, options[:test]) 2439: end 2440: end 2441: 2442: if options[:test] 2443: @y = __y 2444: new_page_required 2445: else 2446: @y 2447: end 2448: end
Calculate how wide a given text string will be on a page, at a given size. This may be called externally, but is alse used by text_width. If size is not specified, PDF::Writer will use the current font_size.
The argument list is reversed from earlier versions.
# File lib/pdf/writer.rb, line 1518 1518: def text_line_width(text, size = nil) 1519: if text.kind_of?(Numeric) and size.kind_of?(String) 1520: text, size = size, text 1521: warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0] 1522: end 1523: 1524: if size.nil? or size <= 0 1525: size = @font_size 1526: end 1527: 1528: # This function should not change any of the settings, though it will 1529: # need to track any tag which change during calculation, so copy them 1530: # at the start and put them back at the end. 1531: t_CTS = @current_text_state.dup 1532: 1533: select_font("Helvetica") if @fonts.empty? 1534: # converts a number or a float to a string so it can get the width 1535: tt = text.to_s 1536: # hmm, this is where it all starts to get tricky - use the font 1537: # information to calculate the width of each character, add them up 1538: # and convert to user units 1539: width = 0 1540: font = @current_font 1541: 1542: pos = -1 1543: loop do 1544: pos += 1 1545: break if pos == tt.size 1546: font_change = true 1547: tag_size, text, font_change = quick_text_tags(text, pos, font_change) 1548: if tag_size != 0 1549: if font_change 1550: current_font! 1551: font = @current_font 1552: end 1553: pos += tag_size - 1 1554: else 1555: if "<" == tt[pos, 4] 1556: width += char_width(font, '<') 1557: pos += 3 1558: elsif ">" == tt[pos, 4] 1559: width += char_width(font, '>') 1560: pos += 3 1561: elsif "&" == tt[pos, 5] 1562: width += char_width(font, '&') 1563: pos += 4 1564: else 1565: width += char_width(font, tt[pos, 1]) 1566: end 1567: end 1568: end 1569: 1570: @current_text_state = t_CTS.dup 1571: current_font! 1572: 1573: (width * size / 1000.0) 1574: end
Calculate how wide a given text string will be on a page, at a given size. If size is not specified, PDF::Writer will use the current font_size. The difference between this method and text_line_width is that this method will iterate over lines separated with newline characters.
The argument list is reversed from earlier versions.
# File lib/pdf/writer.rb, line 1583 1583: def text_width(text, size = nil) 1584: if text.kind_of?(Numeric) and size.kind_of?(String) 1585: text, size = size, text 1586: warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0] 1587: end 1588: 1589: if size.nil? or size <= 0 1590: size = @font_size 1591: end 1592: 1593: max = 0 1594: 1595: text.to_s.each do |line| 1596: width = text_line_width(line, size) 1597: max = width if width > max 1598: end 1599: max 1600: end
Sets the trim box area.
# File lib/pdf/writer.rb, line 652 652: def trim_box(x0, y0, x1, y1) 653: @pages.trim_box = [ x0, y0, x1, y1 ] 654: end
set the viewer preferences of the document, it is up to the browser to obey these.
# File lib/pdf/writer.rb, line 663 663: def viewer_preferences(label, value = 0) 664: @catalog.viewer_preferences ||= PDF::Writer::Object::ViewerPreferences.new(self) 665: 666: # This will only work if the label is one of the valid ones. 667: if label.kind_of?(Hash) 668: label.each { |kk, vv| @catalog.viewer_preferences.__send__("#{kk.downcase}=".intern, vv) } 669: else 670: @catalog.viewer_preferences.__send__("#{label.downcase}=".intern, value) 671: end 672: end
Given a particular generic page number page_num (numbered sequentially from the beginning of the page set), return the page number under a particular page numbering scheme (defaults to the first scheme turned on). Returns nil if page numbering is not turned on or if the page is not under the current numbering scheme.
This method has been dprecated.
# File lib/pdf/writer.rb, line 2186 2186: def which_page_number(page_num, scheme = 0) 2187: return nil unless @page_numbering 2188: 2189: num = nil 2190: start = start_num = 1 2191: 2192: @page_numbering[scheme].each do |kk, vv| 2193: if kk <= page_num 2194: if vv.kind_of?(Hash) 2195: unless vv[:starting].nil? 2196: start = vv[:starting] 2197: start_num = kk 2198: num = page_num - start_num + start 2199: end 2200: else 2201: num = nil 2202: end 2203: end 2204: end 2205: num 2206: end
# File lib/pdf/writer.rb, line 2243 2243: def add_page_numbers 2244: # This will go through the @page_numbering array and add the page 2245: # numbers are required. 2246: if @page_numbering 2247: page_count = @pageset.size 2248: pn_tmp = @page_numbering.dup 2249: 2250: # Go through each of the page numbering schemes. 2251: pn_tmp.each do |scheme| 2252: # First, find the total pages for this schemes. 2253: page = page_number_search(:stop_total, scheme) 2254: 2255: if page 2256: total_pages = page 2257: else 2258: page = page_number_search(:stop_total_next, scheme) 2259: if page 2260: total_pages = page 2261: else 2262: total_pages = page_count 2263: end 2264: end 2265: 2266: status = nil 2267: delta = pattern = pos = x = y = size = nil 2268: pattern = pos = x = y = size = nil 2269: 2270: @pageset.each_with_index do |page, index| 2271: next if status.nil? and scheme[index].nil? 2272: 2273: info = scheme[index] 2274: if info 2275: if info[:start] 2276: status = true 2277: if info[:starting] 2278: delta = info[:starting] - index 2279: else 2280: delta = index 2281: end 2282: 2283: pattern = info[:pattern] 2284: pos = info[:pos] 2285: x = info[:x] 2286: y = info[:y] 2287: size = info[:size] 2288: 2289: # Check for the special case of page numbering starting and 2290: # stopping on the same page. 2291: status = :stop_next if info[:stop] 2292: elsif [:stop, :stop_total].include?(info[:stop]) 2293: status = :stop_now 2294: elsif status == true and [:stop_next, :stop_total_next].include?(info[:stop]) 2295: status = :stop_next 2296: end 2297: end 2298: 2299: if status 2300: # Add the page numbering to this page 2301: num = index + delta.to_i 2302: total = total_pages + num - index 2303: patt = pattern.gsub(/<PAGENUM>/, num.to_s).gsub(/<TOTALPAGENUM>/, total.to_s) 2304: reopen_object(page.contents.first) 2305: 2306: case pos 2307: when :left # Write the page number from x. 2308: w = 0 2309: when :right # Write the page number to x. 2310: w = text_width(patt, size) 2311: when :center # Write the page number around x. 2312: w = text_width(patt, size) / 2.0 2313: end 2314: add_text(x - w, y, patt, size) 2315: close_object 2316: status = nil if [ :stop_now, :stop_next ].include?(status) 2317: end 2318: end 2319: end 2320: end 2321: end
Partially calculate the values necessary to sort out the justification of text.
# File lib/pdf/writer.rb, line 1604 1604: def adjust_wrapped_text(text, actual, width, x, just) 1605: adjust = 0 1606: 1607: case just 1608: when :left 1609: nil 1610: when :right 1611: x += (width - actual) 1612: when :center 1613: x += (width - actual) / 2.0 1614: when :full 1615: spaces = text.count(" ") 1616: adjust = (width - actual) / spaces.to_f if spaces > 0 1617: end 1618: 1619: [x, adjust] 1620: end
# File lib/pdf/writer.rb, line 1497 1497: def char_width(font, char) 1498: char = char[0] unless @fonts[font].c[char] 1499: 1500: if @fonts[font].differences and @fonts[font].c[char].nil? 1501: name = @fonts[font].differences[char] || 'M' 1502: width = @fonts[font].c[name]['WX'] if @fonts[font].c[name]['WX'] 1503: elsif @fonts[font].c[char] 1504: width = @fonts[font].c[char]['WX'] 1505: else 1506: width = @fonts[font].c['M']['WX'] 1507: end 1508: width 1509: end
# File lib/pdf/writer.rb, line 754 754: def find_font(fontname) 755: name = File.basename(fontname, ".afm") 756: @objects.detect do |oo| 757: oo.kind_of?(PDF::Writer::Object::Font) and /#{oo.basefont}$/ =~ name 758: end 759: end
# File lib/pdf/writer.rb, line 762 762: def font_file(fontfile) 763: path = "#{fontfile}.pfb" 764: return path if File.exists?(path) 765: path = "#{fontfile}.ttf" 766: return path if File.exists?(path) 767: nil 768: end
Initialize the font families for the default fonts.
# File lib/pdf/writer.rb, line 623 623: def init_font_families 624: # Set the known family groups. These font families will be used to 625: # enable bold and italic markers to be included within text 626: # streams. HTML forms will be used... <b></b> <i></i> 627: @font_families["Helvetica"] = 628: { 629: "b" => 'Helvetica-Bold', 630: "i" => 'Helvetica-Oblique', 631: "bi" => 'Helvetica-BoldOblique', 632: "ib" => 'Helvetica-BoldOblique' 633: } 634: @font_families['Courier'] = 635: { 636: "b" => 'Courier-Bold', 637: "i" => 'Courier-Oblique', 638: "bi" => 'Courier-BoldOblique', 639: "ib" => 'Courier-BoldOblique' 640: } 641: @font_families['Times-Roman'] = 642: { 643: "b" => 'Times-Bold', 644: "i" => 'Times-Italic', 645: "bi" => 'Times-BoldItalic', 646: "ib" => 'Times-BoldItalic' 647: } 648: end
# File lib/pdf/writer.rb, line 771 771: def load_font(font, encoding = nil) 772: metrics = load_font_metrics(font) 773: 774: name = File.basename(font).gsub(/\.afm$/o, "") 775: 776: encoding_diff = nil 777: case encoding 778: when Hash 779: encoding_name = encoding[:encoding] 780: encoding_diff = encoding[:differences] 781: encoding = PDF::Writer::Object::FontEncoding.new(self, encoding_name, encoding_diff) 782: when NilClass 783: encoding_name = encoding = 'WinAnsiEncoding' 784: else 785: encoding_name = encoding 786: end 787: 788: wfo = PDF::Writer::Object::Font.new(self, name, encoding) 789: 790: # We have an Adobe Font Metrics (.afm) file. We need to find the 791: # associated Type1 (.pfb) or TrueType (.ttf) files (we do not yet 792: # support OpenType fonts); we need to load it into a 793: # PDF::Writer::Object and put the references into the metrics object. 794: base = metrics.path.sub(/\.afm$/o, "") 795: fontfile = font_file(base) 796: unless fontfile 797: base = File.basename(base) 798: FONT_PATH.each do |path| 799: fontfile = font_file(File.join(path, base)) 800: break if fontfile 801: end 802: end 803: 804: if font =~ /afm/o and fontfile 805: # Find the array of font widths, and put that into an object. 806: first_char = -1 807: last_char = 0 808: 809: widths = {} 810: metrics.c.each_value do |details| 811: num = details["C"] 812: 813: if num >= 0 814: # warn "Multiple definitions of #{num}" if widths.has_key?(num) 815: widths[num] = details['WX'] 816: first_char = num if num < first_char or first_char < 0 817: last_char = num if num > last_char 818: end 819: end 820: 821: # Adjust the widths for the differences array. 822: if encoding_diff 823: encoding_diff.each do |cnum, cname| 824: (cnum - last_char).times { widths << 0 } if cnum > last_char 825: last_char = cnum 826: widths[cnum - firstchar] = fonts.c[cname]['WX'] if metrics.c[cname] 827: end 828: end 829: 830: widthid = PDF::Writer::Object::Contents.new(self, :raw) 831: widthid << "[" 832: (first_char .. last_char).each do |ii| 833: if widths.has_key?(ii) 834: widthid << " #{widths[ii].to_i}" 835: else 836: widthid << " 0" 837: end 838: end 839: widthid << "]" 840: 841: # Load the pfb file, and put that into an object too. Note that PDF 842: # supports only binary format Type1 font files and TrueType font 843: # files. There is a simple utility to convert Type1 from pfa to pfb. 844: data = File.open(fbfile, "rb") { |ff| ff.read } 845: 846: # Check to see if the font licence allows embedding. 847: if fbtype =~ /\.ttf$/o 848: offset = 4 849: tables = data[offset, 2].unpack('n')[0] 850: offset += 8 851: 852: found = false 853: tables.times do 854: if data[offset, 4] == 'OS/2' 855: found = true 856: break 857: end 858: offset += 4 + 12 859: end 860: 861: if found 862: offset += 4 863: newoff = data[offset, 4].unpack('N')[0] 864: offset = newoff + 8 865: licence = data[offset, 2].unpack('n')[0] 866: 867: rl = ((licence & 0x02) != 0) 868: pp = ((licence & 0x04) != 0) 869: ee = ((licence & 0x08) != 0) 870: 871: if rl and pp and ee 872: warn PDF::Writer::Lang[:ttf_licence_no_embedding] % name 873: end 874: end 875: end 876: 877: # Create the font descriptor. 878: fdsc = PDF::Writer::Object::FontDescriptor.new(self) 879: # Raw contents causes problems with Acrobat Reader. 880: pfbc = PDF::Writer::Object::Contents.new(self) 881: 882: # Determine flags (more than a little flakey, hopefully will not 883: # matter much). 884: flags = 0 885: if encoding == "none" 886: flags += 2 ** 2 887: else 888: flags += 2 ** 6 if metrics.italicangle.nonzero? 889: flags += 2 ** 0 if metrics.isfixedpitch == "true" 890: flags += 2 ** 5 # Assume a non-symbolic font 891: end 892: 893: # 1: FixedPitch: All glyphs have the same width (as opposed to 894: # proportional or variable-pitch fonts, which have 895: # different widths). 896: # 2: Serif: Glyphs have serifs, which are short strokes drawn 897: # at an angle on the top and bottom of glyph stems. 898: # (Sans serif fonts do not have serifs.) 899: # 3: Symbolic Font contains glyphs outside the Adobe standard 900: # Latin character set. This flag and the Nonsymbolic 901: # flag cannot both be set or both be clear (see 902: # below). 903: # 4: Script: Glyphs resemble cursive handwriting. 904: # 6: Nonsymbolic: Font uses the Adobe standard Latin character set 905: # or a subset of it (see below). 906: # 7: Italic: Glyphs have dominant vertical strokes that are 907: # slanted. 908: # 17: AllCap: Font contains no lowercase letters; typically used 909: # for display purposes, such as for titles or 910: # headlines. 911: # 18: SmallCap: Font contains both uppercase and lowercase 912: # letters. The uppercase letters are similar to 913: # those in the regular version of the same typeface 914: # family. The glyphs for the lowercase letters have 915: # the same shapes as the corresponding uppercase 916: # letters, but they are sized and their proportions 917: # adjusted so that they have the same size and 918: # stroke weight as lowercase glyphs in the same 919: # typeface family. 920: # 19: ForceBold: See below. 921: 922: list = { 923: 'Ascent' => 'Ascender', 924: 'CapHeight' => 'CapHeight', 925: 'Descent' => 'Descender', 926: 'FontBBox' => 'FontBBox', 927: 'ItalicAngle' => 'ItalicAngle' 928: } 929: fdopt = { 930: 'Flags' => flags, 931: 'FontName' => metrics.fontname, 932: 'StemV' => 100 # Don't know what the value for this should be! 933: } 934: 935: list.each do |kk, vv| 936: zz = metrics.__send__(vv.downcase.intern) 937: fdopt[kk] = zz if zz 938: end 939: 940: # Determine the cruicial lengths within this file 941: if fbtype =~ /\.pfb$/o 942: fdopt['FontFile'] = pfbc.oid 943: i1 = data.index('eexec') + 6 944: i2 = data.index('00000000') - i1 945: i3 = data.size - i2 - i1 946: pfbc.add('Length1' => i1, 'Length2' => i2, 'Length3' => i3) 947: elsif fbtype =~ /\.ttf$/o 948: fdopt['FontFile2'] = pfbc.oid 949: pfbc.add('Length1' => data.size) 950: end 951: 952: fdsc.options = fdopt 953: # Embed the font program 954: pfbc << data 955: 956: # Tell the font object about all this new stuff 957: tmp = { 958: 'BaseFont' => metrics.fontname, 959: 'Widths' => widthid.oid, 960: 'FirstChar' => first_char, 961: 'LastChar' => last_char, 962: 'FontDescriptor' => fdsc.oid 963: } 964: tmp['SubType'] = 'TrueType' if fbtype == "ttf" 965: 966: tmp.each { |kk, vv| wfo.__send__("#{kk.downcase}=".intern, vv) } 967: end 968: 969: # Also set the differences here. Note that this means that these will 970: # take effect only the first time that a font is selected, else they 971: # are ignored. 972: metrics.differences = encoding_diff unless encoding_diff.nil? 973: metrics.encoding = encoding_name 974: metrics 975: end
Loads the font metrics. This is now thread-safe.
# File lib/pdf/writer.rb, line 744 744: def load_font_metrics(font) 745: metrics = PDF::Writer::FontMetrics.open(font) 746: @mutex.synchronize do 747: @fonts[font] = metrics 748: @fonts[font].font_num = @fonts.size 749: end 750: metrics 751: end
# File lib/pdf/writer.rb, line 2236 2236: def page_number_search(condition, scheme) 2237: res = nil 2238: scheme.each { |page, value| res = page if value[:stop] == condition } 2239: res 2240: end
# File lib/pdf/writer.rb, line 1375 1375: def parse_tag_params(params) 1376: params ||= "" 1377: ph = {} 1378: params.scan(TAG_PARAM_RE) do |param| 1379: ph[param[0]] = param[1] || param[2] || param[3] 1380: end 1381: ph 1382: end
Restore the state at the end of a page.
# File lib/pdf/writer.rb, line 1763 1763: def reset_state_at_page_finish 1764: add_content("\nQ" * @state_stack.size) 1765: end
This will be called at a new page to return the state to what it was on the end of the previous page, before the stack was closed down. This is to get around not being able to have open ‘q’ across pages.
# File lib/pdf/writer.rb, line 1738 1738: def reset_state_at_page_start 1739: @state_stack.each do |state| 1740: fill_color! state.fill_color 1741: stroke_color! state.stroke_color 1742: text_render_style! state.text_render_style 1743: stroke_style! state.stroke_style 1744: add_content("\nq") 1745: end 1746: end
# File lib/pdf/writer.rb, line 1963 1963: def restore_margins_after_columns 1964: @left_margin = @columns[:left] 1965: @right_margin = @columns[:right] 1966: @top_margin = @columns[:top] 1967: @bottom_margin = @columns[:bottom] 1968: end
Given a start position and information about how text is to be laid out, calculate where on the page the text will end.
# File lib/pdf/writer.rb, line 1086 1086: def text_end_position(x, y, angle, size, wa, text) 1087: width = text_width(text, size) 1088: width += wa * (text.count(" ")) 1089: rad = PDF::Math.deg2rad(angle) 1090: [Math.cos(rad) * width + x, ((-Math.sin(rad)) * width + y)] 1091: end
Checks if text contains a control tag at pos. Control tags are XML-like tags that contain tag information.
<b>: | Adds b to the end of the current text state. If this is the closing tag, </b>, b is removed from the end of the current text state. |
<i>: | Adds i to the end of the current text state. If this is the closing tag, </i, i is removed from the end of the current text state. |
<r:TAG[ PARAMS]/>: | Calls a stand-alone replace callback method of the form tag_TAG_replace. PARAMS must be separated from the TAG name by a single space. The PARAMS, if present, are passed to the replace callback unmodified, whose responsibility it is to interpret the parameters. The replace callback is expected to return text that will be used in the place of the tag. text_tags is called again immediately so that if the replacement text has tags, they will be dealt with properly. |
<C:TAG[ PARAMS]/>: | Calls a stand-alone drawing callback method. The method will be provided an information hash (see below for the data provided). It is expected to use this information to perform whatever drawing tasks are needed to perform its task. |
<c:TAG[ PARAMS]>: | Calls a paired drawing callback method. The method will be provided an information hash (see below for the data provided). It is expected to use this information to perform whatever drawing tasks are needed to perform its task. It must have a corresponding </c:TAG> closing tag. Paired callback behaviours will be preserved over page breaks and line changes. |
Drawing callback tags will be provided an information hash that tells the callback method where it must perform its drawing tasks.
:x: | The current X position of the text. |
:y: | The current y position of the text. |
:angle: | The current text drawing angle. |
:params: | Any parameters that may be important to the callback. This value is only guaranteed to have meaning when a stand-alone callback is made or the opening tag is processed. |
:status: | :start, :end, :start_line, :end_line |
:cbid: | The identifier of this callback. This may be used as a key into a different variable where state may be kept. |
:callback: | The name of the callback function. Only set for stand-alone or opening callback tags. |
:height: | The font height. |
:descender: | The font descender size. |
:start: | The callback has been started. This applies either when the callback is a stand-alone callback (<C:TAG/>) or the opening tag of a paired tag (<c:TAG>). |
:end: | The callback has been manually terminated with a closing tag (</c:TAG>). |
:start_line: | Called when a new line is to be drawn. This allows the callback to perform any updates necessary to permit paired callbacks to cross line boundaries. This will usually involve updating x, y positions. |
:end_line: | Called when the end of a line is reached. This permits the callback to perform any drawing necessary to permit paired callbacks to cross line boundaries. |
Drawing callback methods may return a hash of the :x and :y position that the drawing pointer should take after the callback is complete.
<c:alink URI>: | makes an external link around text between the opening and closing tags of this callback. The URI may be any URL, including http://, ftp://, and mailto:, as long as there is a URL handler registered. URI is of the form uri="URI". |
<c:ilink DEST>: | makes an internal link within the document. The DEST must refer to a known named destination within the document. DEST is of the form dest="DEST". |
<c:uline>: | underlines the specified text. |
<C:bullet>: | Draws a solid bullet at the tag position. |
<C:disc>: | Draws a disc bullet at the tag position. |
# File lib/pdf/writer.rb, line 1209 1209: def text_tags(text, pos, font_change, final = false, x = 0, y = 0, size = 0, angle = 0, word_space_adjust = 0) 1210: tag_size = 0 1211: 1212: tag_match = %r!^<(/)?([^>]+)>!.match(text[pos..-1]) 1213: 1214: if tag_match 1215: closed, tag_name = tag_match.captures 1216: cts = @current_text_state # Alias for shorter lines. 1217: tag_size = tag_name.size + 2 + (closed ? 1 : 0) 1218: 1219: case tag_name 1220: when %r{^(?:b|strong)$}o 1221: if closed 1222: cts.slice!(-1, 1) if ?b == cts[-1] 1223: else 1224: cts << ?b 1225: end 1226: when %r{^(?:i|em)$}o 1227: if closed 1228: cts.slice!(-1, 1) if ?i == cts[-1] 1229: else 1230: cts << ?i 1231: end 1232: when %r{^r:}o 1233: _match = MATCH_TAG_REPLACE_RE.match(tag_name) 1234: if _match.nil? 1235: warn PDF::Writer::Lang[:callback_warning] % [ 'r:', tag_name ] 1236: tag_size = 0 1237: else 1238: func = _match.captures[0] 1239: params = parse_tag_params(_match.captures[1] || "") 1240: tag = TAGS[:replace][func] 1241: 1242: if tag 1243: text[pos, tag_size] = tag[self, params] 1244: tag_size, text, font_change, x, y = text_tags(text, pos, 1245: font_change, 1246: final, x, y, size, 1247: angle, 1248: word_space_adjust) 1249: else 1250: warn PDF::Writer::Lang[:callback_warning] % [ 'r:', func ] 1251: tag_size = 0 1252: end 1253: end 1254: when %r{^C:}o 1255: _match = MATCH_TAG_DRAW_ONE_RE.match(tag_name) 1256: if _match.nil? 1257: warn PDF::Writer::Lang[:callback_warning] % [ 'C:', tag_name ] 1258: tag_size = 0 1259: else 1260: func = _match.captures[0] 1261: params = parse_tag_params(_match.captures[1] || "") 1262: tag = TAGS[:single][func] 1263: 1264: if tag 1265: font_change = false 1266: 1267: if final 1268: # Only call the function if this is the "final" call. Assess 1269: # the text position. Calculate the text width to this point. 1270: x, y = text_end_position(x, y, angle, size, word_space_adjust, 1271: text[0, pos]) 1272: info = { 1273: :x => x, 1274: :y => y, 1275: :angle => angle, 1276: :params => params, 1277: :status => :start, 1278: :cbid => @callbacks.size + 1, 1279: :callback => func, 1280: :height => font_height(size), 1281: :descender => font_descender(size) 1282: } 1283: 1284: ret = tag[self, info] 1285: if ret.kind_of?(Hash) 1286: ret.each do |rk, rv| 1287: x = rv if rk == :x 1288: y = rv if rk == :y 1289: font_change = rv if rk == :font_change 1290: end 1291: end 1292: end 1293: else 1294: warn PDF::Writer::Lang[:callback_Warning] % [ 'C:', func ] 1295: tag_size = 0 1296: end 1297: end 1298: when %r{^c:}o 1299: _match = MATCH_TAG_DRAW_PAIR_RE.match(tag_name) 1300: 1301: if _match.nil? 1302: warn PDF::Writer::Lang[:callback_warning] % [ 'c:', tag_name ] 1303: tag_size = 0 1304: else 1305: func = _match.captures[0] 1306: params = parse_tag_params(_match.captures[1] || "") 1307: tag = TAGS[:pair][func] 1308: 1309: if tag 1310: font_change = false 1311: 1312: if final 1313: # Only call the function if this is the "final" call. Assess 1314: # the text position. Calculate the text width to this point. 1315: x, y = text_end_position(x, y, angle, size, word_space_adjust, 1316: text[0, pos]) 1317: info = { 1318: :x => x, 1319: :y => y, 1320: :angle => angle, 1321: :params => params, 1322: } 1323: 1324: if closed 1325: info[:status] = :end 1326: info[:cbid] = @callbacks.size 1327: 1328: ret = tag[self, info] 1329: 1330: if ret.kind_of?(Hash) 1331: ret.each do |rk, rv| 1332: x = rv if rk == :x 1333: y = rv if rk == :y 1334: font_change = rv if rk == :font_change 1335: end 1336: end 1337: 1338: @callbacks.pop 1339: else 1340: info[:status] = :start 1341: info[:cbid] = @callbacks.size + 1 1342: info[:tag] = tag 1343: info[:callback] = func 1344: info[:height] = font_height(size) 1345: info[:descender] = font_descender(size) 1346: 1347: @callbacks << info 1348: 1349: ret = tag[self, info] 1350: 1351: if ret.kind_of?(Hash) 1352: ret.each do |rk, rv| 1353: x = rv if rk == :x 1354: y = rv if rk == :y 1355: font_change = rv if rk == :font_change 1356: end 1357: end 1358: end 1359: end 1360: else 1361: warn PDF::Writer::Lang[:callback_warning] % [ 'c:', func ] 1362: tag_size = 0 1363: end 1364: end 1365: else 1366: tag_size = 0 1367: end 1368: end 1369: [ tag_size, text, font_change, x, y ] 1370: end