combine_pdf 0.1.23 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eca9f69f00f53b73949ed2b50dbfeb1f805003f6
4
- data.tar.gz: 69b5a220280dfbd5ec5b20f78c5f47fd2011a328
3
+ metadata.gz: e4bac3486fb9fc968d2ca625eb4ad9c0f9423cc7
4
+ data.tar.gz: 5d7d3b20987af05c2cf71c2513bf09418125cae3
5
5
  SHA512:
6
- metadata.gz: f1544fdae3546f748a640c2a8b2e7902b1bc38eae5cebf3f4a4849da0e584504ea8343705210f7ad9a3d5ca6c9ce3491202aefcfee5f049f9fc2a88f98962c9b
7
- data.tar.gz: 9bb5f19edac2b53133ad3d2e8e1a4d02e478b15f46ff3f13e75ada91b56fce38595e4e67696280771bb75a210136120de1be0f598a73f678bff621a1dba54b9b
6
+ metadata.gz: cb03d4476aa78f13d783142c9f91263814d5c9dce299ceb5728dddc79cac0253f4d45ea048953dc69b04fa4ad8416926cd09f05c124ba3b6f3169b827fed86be
7
+ data.tar.gz: c3dbbfc3716b9caa49ffc1b98282a266d20ebf66a126d2cd7d8a06699ed196a97491a51ad425e1d0d4dcc940eb87721e5114b9aed3d2511b4f0ae33ec05ebb6a
@@ -2,6 +2,18 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.2.0
6
+
7
+ Refractoring of code and API overhall.
8
+
9
+ Any code relying on inner/advanced API calls might be broken.
10
+
11
+ **fix**: fixed an object numbering issue introduced by duplicating pages. The issue didn't seem to effect readers nor performance.
12
+
13
+ **fix**: combine_pdf will now properly raise an error when Optional Content Groups (OCG's) are implemented in a PDF file. Page extraction isn't supported for PDF files with OCG's.
14
+
15
+ ***
16
+
5
17
  Change log v.0.1.23
6
18
 
7
19
  **fix**: @kruszczynski fixed an issue with CombinePDF::PDF#number_pages where the page numbering margines were ignored and only the default values were used. Thank you @kruszczynski .
data/README.md CHANGED
@@ -15,15 +15,15 @@ To combine PDF files (or data):
15
15
 
16
16
  ```ruby
17
17
  pdf = CombinePDF.new
18
- pdf << CombinePDF.new("file1.pdf") # one way to combine, very fast.
19
- pdf << CombinePDF.new("file2.pdf")
18
+ pdf << CombinePDF.load("file1.pdf") # one way to combine, very fast.
19
+ pdf << CombinePDF.load("file2.pdf")
20
20
  pdf.save "combined.pdf"
21
21
  ```
22
22
 
23
23
  Or even a one liner:
24
24
 
25
25
  ```ruby
26
- (CombinePDF.new("file1.pdf") << CombinePDF.new("file2.pdf") << CombinePDF.new("file3.pdf")).save("combined.pdf")
26
+ (CombinePDF.load("file1.pdf") << CombinePDF.load("file2.pdf") << CombinePDF.load("file3.pdf")).save("combined.pdf")
27
27
  ```
28
28
 
29
29
  you can also add just odd or even pages:
@@ -31,7 +31,7 @@ you can also add just odd or even pages:
31
31
  ```ruby
32
32
  pdf = CombinePDF.new
33
33
  i = 0
34
- CombinePDF.new("file.pdf").pages.each do |page|
34
+ CombinePDF.load("file.pdf").pages.each do |page|
35
35
  i += 1
36
36
  pdf << page if i.even?
37
37
  end
@@ -46,8 +46,8 @@ To add content to existing PDF pages, first import the new content from an exist
46
46
  In this example, we will add a company logo to each page:
47
47
 
48
48
  ```ruby
49
- company_logo = CombinePDF.new("company_logo.pdf").pages[0]
50
- pdf = CombinePDF.new "content_file.pdf"
49
+ company_logo = CombinePDF.load("company_logo.pdf").pages[0]
50
+ pdf = CombinePDF.load "content_file.pdf"
51
51
  pdf.pages.each {|page| page << company_logo} # notice the << operator is on a page and not a PDF object.
52
52
  pdf.save "content_with_logo.pdf"
53
53
  ```
@@ -65,7 +65,7 @@ pdf.pages(nil, false).each {|page| page << stamp_page}
65
65
  adding page numbers to a PDF object or file is as simple as can be:
66
66
 
67
67
  ```ruby
68
- pdf = CombinePDF.new "file_to_number.pdf"
68
+ pdf = CombinePDF.load "file_to_number.pdf"
69
69
  pdf.number_pages
70
70
  pdf.save "file_with_numbering.pdf"
71
71
  ```
@@ -79,7 +79,7 @@ Loading PDF data can be done from file system or directly from the memory.
79
79
  Loading data from a file is easy:
80
80
 
81
81
  ```ruby
82
- pdf = CombinePDF.new("file.pdf")
82
+ pdf = CombinePDF.load("file.pdf")
83
83
  ```
84
84
 
85
85
  you can also parse PDF files from memory:
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Boaz Segev"]
10
10
  spec.email = ["boaz@2be.co.il"]
11
11
  spec.summary = %q{Combine, stamp and watermark PDF files in pure Ruby.}
12
- spec.description = %q{A nifty gem, in pure Ruby, to parse PDF files and combine (merge) them with other PDF files, number the pages, watermark them or stamp them, create tables or basic text objects etc` (all using the PDF file format).}
12
+ spec.description = %q{A nifty gem, in pure Ruby, to parse PDF files and combine (merge) them with other PDF files, number the pages, watermark them or stamp them, create tables, add basic text objects etc` (all using the PDF file format).}
13
13
  spec.homepage = "https://github.com/boazsegev/combine_pdf"
14
14
  spec.license = "MIT"
15
15
 
@@ -9,17 +9,20 @@ require 'matrix'
9
9
  require 'rc4'
10
10
 
11
11
 
12
- load "combine_pdf/combine_pdf_operations.rb"
13
- load "combine_pdf/combine_pdf_page.rb"
14
- load "combine_pdf/combine_pdf_basic_writer.rb"
15
- load "combine_pdf/combine_pdf_decrypt.rb"
16
- load "combine_pdf/combine_pdf_fonts.rb"
17
- load "combine_pdf/combine_pdf_filter.rb"
18
- load "combine_pdf/combine_pdf_parser.rb"
19
- load "combine_pdf/combine_pdf_pdf.rb"
20
- require "combine_pdf/version"
12
+ load "combine_pdf/api.rb"
13
+ load "combine_pdf/renderer.rb"
14
+ load "combine_pdf/page_methods.rb"
15
+ load "combine_pdf/basic_writer.rb"
16
+ load "combine_pdf/decrypt.rb"
17
+ load "combine_pdf/fonts.rb"
18
+ load "combine_pdf/filter.rb"
19
+ load "combine_pdf/parser.rb"
20
+ load "combine_pdf/pdf_public.rb"
21
+ load "combine_pdf/pdf_protected.rb"
21
22
 
22
- load "combine_pdf/combine_pdf_methods.rb"
23
+ # load "combine_pdf/operations.rb"
24
+
25
+ load "combine_pdf/version.rb"
23
26
 
24
27
 
25
28
  # This is a pure ruby library to combine/merge, stmap/overlay and number PDF files - as well as to create tables (ment for indexing combined files).
@@ -33,22 +36,22 @@ load "combine_pdf/combine_pdf_methods.rb"
33
36
  # Loading PDF data can be done from file system or directly from the memory.
34
37
  #
35
38
  # Load data from a file:
36
- # pdf = CombinePDF.new("file.pdf")
39
+ # pdf = CombinePDF.load("file.pdf")
37
40
  # parse PDF files from memory:
38
41
  # pdf = CombinePDF.parse(pdf_data)
39
42
  #
40
43
  # == Combine/Merge PDF files or Pages
41
44
  # To combine PDF files (or data):
42
45
  # pdf = CombinePDF.new
43
- # pdf << CombinePDF.new("file1.pdf")
44
- # pdf << CombinePDF.new("file2.pdf")
46
+ # pdf << CombinePDF.load("file1.pdf")
47
+ # pdf << CombinePDF.load("file2.pdf")
45
48
  # pdf.save "combined.pdf"
46
49
  #
47
50
  # It is possible to add only specific pages.
48
51
  # in this example, only even pages will be added:
49
52
  # pdf = CombinePDF.new
50
53
  # i = 0
51
- # CombinePDF.new("file.pdf").pages.each do |page|
54
+ # CombinePDF.load("file.pdf").pages.each do |page|
52
55
  # i += 1
53
56
  # pdf << page if i.even?
54
57
  # end
@@ -56,14 +59,14 @@ load "combine_pdf/combine_pdf_methods.rb"
56
59
  # Notice that adding the whole file is faster then adding each page seperately.
57
60
  # == Add content to existing pages (Stamp / Watermark)
58
61
  # It is possible "stamp" one PDF page using another PDF page. In this example, a company logo will be stamped over each page:
59
- # company_logo = CombinePDF.new("company_logo.pdf").pages[0]
60
- # pdf = CombinePDF.new "content_file.pdf"
62
+ # company_logo = CombinePDF.load("company_logo.pdf").pages[0]
63
+ # pdf = CombinePDF.load "content_file.pdf"
61
64
  # pdf.pages.each {|page| page << company_logo}
62
65
  # pdf.save "content_with_logo.pdf"
63
66
  # Notice the << operator is on a page and not a PDF object. The << operator acts differently on PDF objects and on Pages.
64
67
  # == Page Numbering
65
68
  # It is possible to number the pages. in this example we will add very simple numbering:
66
- # pdf = CombinePDF.new "file_to_number.pdf"
69
+ # pdf = CombinePDF.load "file_to_number.pdf"
67
70
  # pdf.number_pages
68
71
  # pdf.save "file_with_numbering.pdf"
69
72
  #
@@ -73,13 +76,13 @@ load "combine_pdf/combine_pdf_methods.rb"
73
76
  #
74
77
  # in this example, all the PDF pages will be stamped, along the top, with a red box, with blue text, stating "Draft, page #".
75
78
  # here is the easy way (we can even use "number_pages" without page numbers, if we wish):
76
- # pdf = CombinePDF.new "file_to_stamp.pdf"
79
+ # pdf = CombinePDF.load "file_to_stamp.pdf"
77
80
  # pdf.number_pages number_format: " - Draft, page %d - ", number_location: [:top], font_color: [0,0,1], box_color: [0.4,0,0], opacity: 0.75, font_size:16
78
81
  # pdf.save "draft.pdf"
79
82
  #
80
83
  # in this example we will add a first page with the word "Draft", in red over a colored background:
81
84
  #
82
- # pdf = CombinePDF.new "file.pdf"
85
+ # pdf = CombinePDF.load "file.pdf"
83
86
  # pdf_first_page = pdf.pages[0]
84
87
  # mediabox = page[:CropBox] || page[:MediaBox] #copy page size
85
88
  # title_page = CombinePDF.create_page mediabox #make title page same size as first page
@@ -10,12 +10,22 @@ module CombinePDF
10
10
  # Create an empty PDF object or create a PDF object from a file (parsing the file).
11
11
  # file_name:: is the name of a file to be parsed.
12
12
  def load(file_name = "")
13
- raise TypeError, "couldn't parse and data, expecting type String" unless file_name.is_a?(String) || file_name.is_a?(Pathname)
13
+ raise TypeError, "couldn't parse data, expecting type String" unless file_name.is_a?(String) || file_name.is_a?(Pathname)
14
14
  return PDF.new() if file_name == ''
15
15
  PDF.new( PDFParser.new( IO.read(file_name, mode: 'rb').force_encoding(Encoding::ASCII_8BIT) ) )
16
16
  end
17
- def new(file_name = "")
18
- load(file_name) rescue parse(file_name)
17
+ # creats a new PDF object.
18
+ #
19
+ # Combine PDF will check to see if `string` is a filename.
20
+ # If it's a file name, it will attempt to load the PDF file using `CombinePDF.load`. Otherwise it will attempt parsing `string` using `CombinePDF.parse`.
21
+ #
22
+ # If the string is empty it will return a new PDF object (the same as parse).
23
+ #
24
+ # For both performance and code readability reasons, `CombinePDF.load` and `CombinePDF.parse` should be preffered unless creating a new PDF object.
25
+ def new(string = false)
26
+ return PDF.new unless string
27
+ raise TypeError, "couldn't create PDF object, expecting type String" unless string.is_a?(String) || string.is_a?(Pathname)
28
+ (File.file? string rescue false) ? load(string) : parse(string)
19
29
  end
20
30
 
21
31
  # Create a PDF object from a raw PDF data (parsing the data).
@@ -50,11 +60,11 @@ module CombinePDF
50
60
  # pdf = CombinePDF.create_table headers: ["header 1", "another header"], table_data: [ ["this is one row", "with two columns"] , ["this is another row", "also two columns", "the third will be ignored"] ]
51
61
  # pdf.save "table_file.pdf"
52
62
  #
53
- # accepts a Hash with any of the following keys as well as any of the PDFWriter#textbox options:
63
+ # accepts a Hash with any of the following keys as well as any of the Page_Methods#textbox options:
54
64
  # headers:: an Array of strings with the headers (will be repeated every page).
55
65
  # table_data:: as Array of Arrays, each containing a string for each column. the first row sets the number of columns. extra columns will be ignored.
56
- # font:: a registered or standard font name (see PDFWriter). defaults to nil (:Helvetica).
57
- # header_font:: a registered or standard font name for the headers (see PDFWriter). defaults to nil (the font for all the table rows).
66
+ # font:: a registered or standard font name (see Page_Methods). defaults to nil (:Helvetica).
67
+ # header_font:: a registered or standard font name for the headers (see Page_Methods). defaults to nil (the font for all the table rows).
58
68
  # max_font_size:: the maximum font size. if the string doesn't fit, it will be resized. defaults to 14.
59
69
  # column_widths:: an array of relative column widths ([1,2] will display only the first two columns, the second twice as big as the first). defaults to nil (even widths).
60
70
  # header_color:: the header color. defaults to [0.8, 0.8, 0.8] (light gray).
@@ -80,83 +90,6 @@ module CombinePDF
80
90
  table << page
81
91
  end
82
92
  table
83
-
84
- # defaults = {
85
- # headers: nil,
86
- # table_data: [[]],
87
- # font: nil,
88
- # header_font: nil,
89
- # max_font_size: 14,
90
- # column_widths: nil,
91
- # header_color: [0.8, 0.8, 0.8],
92
- # main_color: nil,
93
- # alternate_color: [0.95, 0.95, 0.95],
94
- # font_color: [0,0,0],
95
- # border_color: [0,0,0],
96
- # border_width: 1,
97
- # header_align: :center,
98
- # row_align: nil,
99
- # direction: :ltr,
100
- # rows_per_page: 25,
101
- # page_size: [0, 0, 595.3, 841.9] #A4
102
- # }
103
- # options = defaults.merge options
104
- # options[:header_font] = options[:font] unless options[:header_font]
105
- # options[:row_align] ||= ( (options[:direction] == :rtl) ? :right : :left )
106
- # # assert table_data is an array of arrays
107
- # return false unless (options[:table_data].select {|r| !r.is_a?(Array) }).empty?
108
- # # compute sizes
109
- # page_size = options[:page_size]
110
- # top = page_size[3] * 0.9
111
- # height = page_size[3] * 0.8 / options[:rows_per_page]
112
- # from_side = page_size[2] * 0.1
113
- # width = page_size[2] * 0.8
114
- # columns = options[:table_data][0].length
115
- # column_widths = []
116
- # columns.times {|i| column_widths << (width/columns) }
117
- # if options[:column_widths]
118
- # scale = 0
119
- # options[:column_widths].each {|w| scale += w}
120
- # column_widths = []
121
- # options[:column_widths].each { |w| column_widths << (width*w/scale) }
122
- # end
123
- # column_widths = column_widths.reverse if options[:direction] == :rtl
124
- # # set pdf object and start writing the data
125
- # table = PDF.new()
126
- # page = nil
127
- # rows_per_page = options[:rows_per_page]
128
- # row_number = rows_per_page + 1
129
-
130
- # options[:table_data].each do |row_data|
131
- # if row_number > rows_per_page
132
- # page = create_page page_size
133
- # table << page
134
- # row_number = 1
135
- # # add headers
136
- # if options[:headers]
137
- # x = from_side
138
- # headers = options[:headers]
139
- # headers = headers.reverse if options[:direction] == :rtl
140
- # column_widths.each_index do |i|
141
- # text = headers[i].to_s
142
- # page.textbox text, {x: x, y: (top - (height*row_number)), width: column_widths[i], height: height, box_color: options[:header_color], text_align: options[:header_align] }.merge(options).merge({font: options[:header_font]})
143
- # x += column_widths[i]
144
- # end
145
- # row_number += 1
146
- # end
147
- # end
148
- # x = from_side
149
- # row_data = row_data.reverse if options[:direction] == :rtl
150
- # column_widths.each_index do |i|
151
- # text = row_data[i].to_s
152
- # box_color = options[:main_color]
153
- # box_color = options[:alternate_color] if options[:alternate_color] && row_number.odd?
154
- # page.textbox text, {x: x, y: (top - (height*row_number)), width: column_widths[i], height: height, box_color: box_color, text_align: options[:row_align]}.merge(options)
155
- # x += column_widths[i]
156
- # end
157
- # row_number += 1
158
- # end
159
- # table
160
93
  end
161
94
  def new_table(options = {})
162
95
  create_table options
@@ -0,0 +1,62 @@
1
+ # -*- encoding : utf-8 -*-
2
+ ########################################################
3
+ ## Thoughts from reading the ISO 32000-1:2008
4
+ ## this file is part of the CombinePDF library and the code
5
+ ## is subject to the same license.
6
+ ########################################################
7
+
8
+
9
+
10
+
11
+ module CombinePDF
12
+
13
+ # Limited Unicode Support (font dependent)!
14
+ #
15
+ # The PDFWriter class is a subclass of Hash and represents a PDF Page object.
16
+ #
17
+ # Writing on this Page is done using the textbox function.
18
+ #
19
+ # 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].
20
+ #
21
+ # Once the Page is completed (the last text box was added),
22
+ # we can insert the page to a CombinePDF object.
23
+ #
24
+ # We can either insert the PDFWriter as a new page:
25
+ # pdf = CombinePDF.new
26
+ # new_page = CombinePDF.create_page # => PDFWriter object
27
+ # new_page.textbox "some text"
28
+ # pdf << new_page
29
+ # pdf.save "file_with_new_page.pdf"
30
+ #
31
+ # Or we can use the Page_Methods methods to write an overlay (stamp / watermark) over existing pages:
32
+ # pdf = CombinePDF.new
33
+ # new_page = PDFWriter.new "some_file.pdf"
34
+ # pdf.pages.each {|page| page.textbox "Draft", opacity: 0.4 }
35
+ # pdf.save "stamped_file.pdf"
36
+ class PDFWriter < Hash
37
+
38
+ # create a new PDFWriter object.
39
+ #
40
+ # mediabox:: the PDF page size in PDF points. defaults to [0, 0, 595.3, 841.9] (A4)
41
+ def initialize(mediabox = [0, 0, 595.3, 841.9])
42
+ # indirect_reference_id, :indirect_generation_number
43
+ @contents = ""
44
+ @base_font_name = "Writer" + SecureRandom.hex(7) + "PDF"
45
+ self[:Type] = :Page
46
+ self[:indirect_reference_id] = 0
47
+ self[:Resources] = {}
48
+ self[:Contents] = { is_reference_only: true , referenced_object: {indirect_reference_id: 0, raw_stream_content: @contents} }
49
+ self[:MediaBox] = mediabox
50
+ end
51
+
52
+ # includes the PDF Page_Methods module, including all page methods (textbox etc').
53
+ include Page_Methods
54
+
55
+ end
56
+
57
+ end
58
+
59
+
60
+
61
+
62
+
@@ -34,7 +34,7 @@ module CombinePDF
34
34
  0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A ]
35
35
  @key_crypt_first_iv_store = nil
36
36
  @encryption_iv = nil
37
- PDFOperations.change_references_to_actual_values @objects, @encryption_dictionary
37
+ change_references_to_actual_values @encryption_dictionary
38
38
  end
39
39
 
40
40
  # call this to start the decryption.
@@ -142,6 +142,9 @@ module CombinePDF
142
142
  cipher.padding = 0
143
143
  (cipher.update(encrypted) + cipher.final).unpack("C*")
144
144
  end
145
+
146
+ protected
147
+
145
148
  def _perform_decrypt_proc_ (object, decrypt_proc, encrypted_id = nil, encrypted_generation = nil, encrypted_filter = nil)
146
149
  case
147
150
  when object.is_a?(Array)
@@ -153,7 +156,7 @@ module CombinePDF
153
156
  if object[:raw_stream_content]
154
157
  stream_length = object[:Length]
155
158
  if stream_length.is_a?(Hash) && stream_length[:is_reference_only]
156
- stream_length = PDFOperations.get_refernced_object( @objects, stream_length)[:indirect_without_dictionary]
159
+ stream_length = get_refernced_object(stream_length)[:indirect_without_dictionary]
157
160
  end
158
161
  actual_length = object[:raw_stream_content].length
159
162
  warn "Stream registeded length was #{object[:Length].to_s} and the actual length was #{actual_length}." if actual_length < stream_length
@@ -174,6 +177,36 @@ module CombinePDF
174
177
  warn "Data raising exception:\n #{object.to_s.split(',').join("\n")}"
175
178
  raise "File is encrypted - not supported."
176
179
  end
180
+
181
+ def change_references_to_actual_values(hash_with_references = {})
182
+ hash_with_references.each do |k,v|
183
+ if v.is_a?(Hash) && v[:is_reference_only]
184
+ hash_with_references[k] = get_refernced_object(v)
185
+ hash_with_references[k] = hash_with_references[k][:indirect_without_dictionary] if hash_with_references[k].is_a?(Hash) && hash_with_references[k][:indirect_without_dictionary]
186
+ warn "Couldn't connect all values from references - didn't find reference #{hash_with_references}!!!" if hash_with_references[k] == nil
187
+ hash_with_references[k] = v unless hash_with_references[k]
188
+ end
189
+ end
190
+ hash_with_references
191
+ end
192
+ def get_refernced_object(reference_hash = {})
193
+ @objects.each do |stored_object|
194
+ return stored_object if ( stored_object.is_a?(Hash) &&
195
+ reference_hash[:indirect_reference_id] == stored_object[:indirect_reference_id] &&
196
+ reference_hash[:indirect_generation_number] == stored_object[:indirect_generation_number] )
197
+ end
198
+ warn "didn't find reference #{reference_hash}"
199
+ nil
200
+ end
201
+
202
+
203
+ # # returns the PDF Object Hash holding the acutal data (if exists) or the original hash (if it wasn't a reference)
204
+ # #
205
+ # # works only AFTER references have been connected.
206
+ # def get_referenced object
207
+ # object[:referenced_object] || object
208
+ # end
209
+
177
210
  end
178
211
  #####################################################
179
212
  ## The following isn't my code!!!!
@@ -18,6 +18,7 @@ module CombinePDF
18
18
 
19
19
 
20
20
  module Fonts
21
+ extend Renderer
21
22
 
22
23
  protected
23
24
 
@@ -53,7 +54,7 @@ module CombinePDF
53
54
  # This function translate a unicode string, to a character glyph ID stream.
54
55
  def encode text
55
56
  # FixMe: embed RTL text convertion
56
- return PDFOperations._format_string_to_pdf(text) unless self.cmap
57
+ return format_string_to_pdf(text) unless self.cmap
57
58
  coded_array = text.chars.map do |c|
58
59
  if self.cmap[c]
59
60
  self.cmap[c]
@@ -91,14 +92,24 @@ module CombinePDF
91
92
  FONTS_LIBRARY.keys
92
93
  end
93
94
 
94
- # gets a font from the fonts library
95
- def get_font(name = :Helvetica)
95
+ # gets the original font object from the fonts library (this allows you to edit the font).
96
+ def get_original_font(name = :Helvetica)
96
97
  initiate_library
97
98
  FONTS_LIBRARY[name]
98
99
  end
99
100
 
101
+ # gets a copy of the font object from the fonts library (this allows you to use the font as an object is a PDF file, without altering the original registered font).
102
+ def get_font(name = :Helvetica)
103
+ initiate_library
104
+ font = FONTS_LIBRARY[name]
105
+ return nil unless font
106
+ font = font.dup
107
+ font[:referenced_object] = font[:referenced_object].dup if font[:referenced_object]
108
+ font
109
+ end
110
+
100
111
  # adds a correctly formatted font object to the font library.
101
- # font_name:: a Symbol with the name of the font. if the fonts exists, it will be overwritten!
112
+ # font_name:: a Symbol with the name of the font. if the fonts name exists, the font will be overwritten!
102
113
  # font_metrics:: a Hash of ont metrics, of the format char => {wx: char_width, boundingbox: [left_x, buttom_y, right_x, top_y]} where i == character code (i.e. 32 for space). The Hash should contain a special value :missing for the metrics of missing characters. an optional :wy will be supported in the future, for up to down fonts.
103
114
  # font_pdf_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
104
115
  # font_cmap:: a CMap dictionary Hash) which maps unicode characters to the hex CID for the font (i.e. {"a" => "61", "z" => "7a" }).
@@ -125,9 +136,9 @@ module CombinePDF
125
136
  merged_metrics = {}
126
137
  # merge the metrics last to first (so that important fonts override secondary fonts)
127
138
  fonts.length.downto(1).each do |i|
128
- f = get_font(fonts[i-1])
139
+ f = get_original_font(fonts[i-1])
129
140
  if f && f.metrics
130
- merged_metrics.update( get_font(fonts[i-1]).metrics)
141
+ merged_metrics.update( get_original_font(fonts[i-1]).metrics)
131
142
  else
132
143
  warn "metrics of font not found!"
133
144
  end
@@ -212,7 +223,7 @@ module CombinePDF
212
223
  to_unicode = font_object[:ToUnicode]
213
224
  to_unicode = to_unicode[:referenced_object] if to_unicode[:is_reference_only]
214
225
  # deflate the cmap file stream before parsing
215
- to_unicode = PDFOperations.create_deep_copy to_unicode
226
+ to_unicode = create_deep_copy to_unicode
216
227
  CombinePDF::PDFFilter.inflate_object to_unicode
217
228
  # parse the deflated stream
218
229
  cmap = self.parse_cmap to_unicode[:raw_stream_content]
@@ -241,7 +252,7 @@ module CombinePDF
241
252
  if old_widths[:W]
242
253
  old_widths = old_widths[:W]
243
254
  old_widths = old_widths[:referenced_object][:indirect_without_dictionary] if old_widths[:is_reference_only]
244
- old_widths = PDFOperations.create_deep_copy old_widths
255
+ old_widths = create_deep_copy old_widths
245
256
  while old_widths[0] do
246
257
  a = old_widths.shift
247
258
  b = old_widths.shift