combine_pdf 0.1.18 → 0.1.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/combine_pdf/combine_pdf_methods.rb +86 -74
- data/lib/combine_pdf/combine_pdf_page.rb +114 -0
- data/lib/combine_pdf/combine_pdf_parser.rb +6 -2
- data/lib/combine_pdf/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 349e06732609e9290dc73720f99ac576b097c53c
|
4
|
+
data.tar.gz: 48737dde1fb3bf8e9d838d43795a9aa0d06453bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a08c2bd768ffb7e41788da436ff7409b609f97986a0dcf78ce168ec9d3ed1e041af8f2389588fe26e44f19b60cce444ed00bb57abc8d6aca22bfaaea24bc7b9f
|
7
|
+
data.tar.gz: 92249ecb752f219595a2a55ef0b72ce45dd4cda953df903bf3328f418300fd223378f24186d827b79514b7c734dab3aa39fc28272b0a473423863eba92efe5f6
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,23 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.1.20
|
6
|
+
(pre-release)
|
7
|
+
|
8
|
+
No changes yet.
|
9
|
+
|
10
|
+
***
|
11
|
+
|
12
|
+
Change log v.0.1.19
|
13
|
+
|
14
|
+
**fix**: merged @espinosa's fix for issue #16 which affected windows machines.
|
15
|
+
|
16
|
+
**feature**: added a #write_table method to the PDF pages, allowing tables to be written on existing PDF pages. This is a destructive method (it changes the table_data array by removing any rows written to the page and leaving the rest of the data untouched, for future writing). Read the documentation before using this method.
|
17
|
+
|
18
|
+
**update**: stricter parsing for PDF Stream Objects is now enforced. The stricter parsing is NOT final, as it walks a fine line between allowing non-conforming PDF files to be read and risking an error while reading a correctly structured file which has PDF keywords intentionaly embedded in a correctly structured object stream (keywords which would be normally ignored as expected, but which will be recognized as relevant if the parser is less strict about the structure of the PDF file).
|
19
|
+
|
20
|
+
***
|
21
|
+
|
5
22
|
Change log v.0.1.18
|
6
23
|
|
7
24
|
**fix**: Thank to Stefan, who reported issue #15 , we discovered that in some cases PDF files presented the wrong PDF standard version, causing an error while attempting to parse their data. The issue has been fixed by allowing the parser to search for PDF Object Streams even when the PDF file claims a PDF version below 1.5.
|
@@ -12,7 +12,7 @@ module CombinePDF
|
|
12
12
|
def load(file_name = "")
|
13
13
|
raise TypeError, "couldn't parse and data, expecting type String" unless file_name.is_a?(String) || file_name.is_a?(Pathname)
|
14
14
|
return PDF.new() if file_name == ''
|
15
|
-
PDF.new( PDFParser.new( IO.read(file_name).force_encoding(Encoding::ASCII_8BIT) ) )
|
15
|
+
PDF.new( PDFParser.new( IO.read(file_name, mode: 'rb').force_encoding(Encoding::ASCII_8BIT) ) )
|
16
16
|
end
|
17
17
|
def new(file_name = "")
|
18
18
|
load(file_name)
|
@@ -66,85 +66,97 @@ module CombinePDF
|
|
66
66
|
# header_align:: the header text alignment within each column (:right, :left, :center). defaults to :center.
|
67
67
|
# row_align:: the row text alignment within each column. defaults to :left (:right for RTL table).
|
68
68
|
# direction:: the table's writing direction (:ltr or :rtl). this reffers to the direction of the columns and doesn't effect text (rtl text is automatically recognized). defaults to :ltr.
|
69
|
-
#
|
69
|
+
# max_rows:: the number of rows per page, INCLUDING the header row. deafults to 25.
|
70
70
|
# page_size:: the size of the page in PDF points. defaults to [0, 0, 595.3, 841.9] (A4).
|
71
71
|
def create_table(options = {})
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
font: nil,
|
76
|
-
header_font: nil,
|
77
|
-
max_font_size: 14,
|
78
|
-
column_widths: nil,
|
79
|
-
header_color: [0.8, 0.8, 0.8],
|
80
|
-
main_color: nil,
|
81
|
-
alternate_color: [0.95, 0.95, 0.95],
|
82
|
-
font_color: [0,0,0],
|
83
|
-
border_color: [0,0,0],
|
84
|
-
border_width: 1,
|
85
|
-
header_align: :center,
|
86
|
-
row_align: nil,
|
87
|
-
direction: :ltr,
|
88
|
-
rows_per_page: 25,
|
89
|
-
page_size: [0, 0, 595.3, 841.9] #A4
|
90
|
-
}
|
91
|
-
options = defaults.merge options
|
92
|
-
options[:header_font] = options[:font] unless options[:header_font]
|
93
|
-
options[:row_align] ||= ( (options[:direction] == :rtl) ? :right : :left )
|
94
|
-
# assert table_data is an array of arrays
|
95
|
-
return false unless (options[:table_data].select {|r| !r.is_a?(Array) }).empty?
|
96
|
-
# compute sizes
|
97
|
-
page_size = options[:page_size]
|
98
|
-
top = page_size[3] * 0.9
|
99
|
-
height = page_size[3] * 0.8 / options[:rows_per_page]
|
100
|
-
from_side = page_size[2] * 0.1
|
101
|
-
width = page_size[2] * 0.8
|
102
|
-
columns = options[:table_data][0].length
|
103
|
-
column_widths = []
|
104
|
-
columns.times {|i| column_widths << (width/columns) }
|
105
|
-
if options[:column_widths]
|
106
|
-
scale = 0
|
107
|
-
options[:column_widths].each {|w| scale += w}
|
108
|
-
column_widths = []
|
109
|
-
options[:column_widths].each { |w| column_widths << (width*w/scale) }
|
110
|
-
end
|
111
|
-
column_widths = column_widths.reverse if options[:direction] == :rtl
|
112
|
-
# set pdf object and start writing the data
|
72
|
+
options[:max_rows] = options[:rows_per_page] if options[:rows_per_page]
|
73
|
+
|
74
|
+
page_size = options[:page_size] || [0, 0, 595.3, 841.9]
|
113
75
|
table = PDF.new()
|
114
76
|
page = nil
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
if row_number > rows_per_page
|
120
|
-
page = create_page page_size
|
121
|
-
table << page
|
122
|
-
row_number = 1
|
123
|
-
# add headers
|
124
|
-
if options[:headers]
|
125
|
-
x = from_side
|
126
|
-
headers = options[:headers]
|
127
|
-
headers = headers.reverse if options[:direction] == :rtl
|
128
|
-
column_widths.each_index do |i|
|
129
|
-
text = headers[i].to_s
|
130
|
-
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]})
|
131
|
-
x += column_widths[i]
|
132
|
-
end
|
133
|
-
row_number += 1
|
134
|
-
end
|
135
|
-
end
|
136
|
-
x = from_side
|
137
|
-
row_data = row_data.reverse if options[:direction] == :rtl
|
138
|
-
column_widths.each_index do |i|
|
139
|
-
text = row_data[i].to_s
|
140
|
-
box_color = options[:main_color]
|
141
|
-
box_color = options[:alternate_color] if options[:alternate_color] && row_number.odd?
|
142
|
-
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)
|
143
|
-
x += column_widths[i]
|
144
|
-
end
|
145
|
-
row_number += 1
|
77
|
+
until options[:table_data].empty?
|
78
|
+
page = create_page page_size
|
79
|
+
page.write_table options
|
80
|
+
table << page
|
146
81
|
end
|
147
82
|
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
|
148
160
|
end
|
149
161
|
def new_table(options = {})
|
150
162
|
create_table options
|
@@ -43,6 +43,22 @@ module CombinePDF
|
|
43
43
|
self[:MediaBox].is_a?(Array) ? self[:MediaBox] : self[:MediaBox][:referenced_object]
|
44
44
|
end
|
45
45
|
|
46
|
+
# accessor (setter) for the :CropBox element of the page
|
47
|
+
# dimensions:: an Array consisting of four numbers (can be floats) setting the size of the media box.
|
48
|
+
def cropbox=(dimensions = [0.0, 0.0, 612.0, 792.0])
|
49
|
+
self[:CropBox] = dimensions
|
50
|
+
end
|
51
|
+
|
52
|
+
# accessor (getter) for the :CropBox element of the page
|
53
|
+
def cropbox
|
54
|
+
(self[:CropBox].is_a?(Array) || self[:CropBox].nil?) ? self[:CropBox] : self[:CropBox][:referenced_object]
|
55
|
+
end
|
56
|
+
|
57
|
+
# get page size
|
58
|
+
def page_size
|
59
|
+
cropbox || mediabox
|
60
|
+
end
|
61
|
+
|
46
62
|
# accessor (getter) for the :Resources element of the page
|
47
63
|
def resources
|
48
64
|
self[:Resources] ||= {}
|
@@ -374,6 +390,104 @@ module CombinePDF
|
|
374
390
|
end
|
375
391
|
|
376
392
|
|
393
|
+
# Writes a table to the current page, removing(!) the written rows from the table_data Array.
|
394
|
+
#
|
395
|
+
# since the table_data Array is updated, it is possible to call this method a few times,
|
396
|
+
# each time creating or moving to the next page, until table_data.empty? returns true.
|
397
|
+
#
|
398
|
+
# accepts a Hash with any of the following keys as well as any of the PDFWriter#textbox options:
|
399
|
+
# headers:: an Array of strings with the headers (will be repeated every page).
|
400
|
+
# 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.
|
401
|
+
# font:: a registered or standard font name (see PDFWriter). defaults to nil (:Helvetica).
|
402
|
+
# header_font:: a registered or standard font name for the headers (see PDFWriter). defaults to nil (the font for all the table rows).
|
403
|
+
# max_font_size:: the maximum font size. if the string doesn't fit, it will be resized. defaults to 14.
|
404
|
+
# 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).
|
405
|
+
# header_color:: the header color. defaults to [0.8, 0.8, 0.8] (light gray).
|
406
|
+
# main_color:: main row color. defaults to nil (transparent / white).
|
407
|
+
# alternate_color:: alternate row color. defaults to [0.95, 0.95, 0.95] (very light gray).
|
408
|
+
# font_color:: font color. defaults to [0,0,0] (black).
|
409
|
+
# border_color:: border color. defaults to [0,0,0] (black).
|
410
|
+
# border_width:: border width in PDF units. defaults to 1.
|
411
|
+
# header_align:: the header text alignment within each column (:right, :left, :center). defaults to :center.
|
412
|
+
# row_align:: the row text alignment within each column. defaults to :left (:right for RTL table).
|
413
|
+
# direction:: the table's writing direction (:ltr or :rtl). this reffers to the direction of the columns and doesn't effect text (rtl text is automatically recognized). defaults to :ltr.
|
414
|
+
# max_rows:: the maximum number of rows to actually draw, INCLUDING the header row. deafults to 25.
|
415
|
+
# xy:: an Array specifying the top-left corner of the table. defaulte to [page_width*0.1, page_height*0.9].
|
416
|
+
# size:: an Array specifying the height and the width of the table. defaulte to [page_width*0.8, page_height*0.8].
|
417
|
+
def write_table(options = {})
|
418
|
+
defaults = {
|
419
|
+
headers: nil,
|
420
|
+
table_data: [[]],
|
421
|
+
font: nil,
|
422
|
+
header_font: nil,
|
423
|
+
max_font_size: 14,
|
424
|
+
column_widths: nil,
|
425
|
+
header_color: [0.8, 0.8, 0.8],
|
426
|
+
main_color: nil,
|
427
|
+
alternate_color: [0.95, 0.95, 0.95],
|
428
|
+
font_color: [0,0,0],
|
429
|
+
border_color: [0,0,0],
|
430
|
+
border_width: 1,
|
431
|
+
header_align: :center,
|
432
|
+
row_align: nil,
|
433
|
+
direction: :ltr,
|
434
|
+
max_rows: 25,
|
435
|
+
xy: nil,
|
436
|
+
size: nil
|
437
|
+
}
|
438
|
+
options = defaults.merge options
|
439
|
+
raise "method call error! not enough rows allowed to create table" if (options[:max_rows].to_i < 1 && options[:headers]) || (options[:max_rows].to_i <= 0)
|
440
|
+
options[:header_font] ||= options[:font]
|
441
|
+
options[:row_align] ||= ( (options[:direction] == :rtl) ? :right : :left )
|
442
|
+
options[:xy] ||= [( (page_size[2]-page_size[0])*0.1 ), ( (page_size[3]-page_size[1])*0.9 )]
|
443
|
+
options[:size] ||= [( (page_size[2]-page_size[0])*0.8 ), ( (page_size[3]-page_size[1])*0.8 )]
|
444
|
+
# assert table_data is an array of arrays
|
445
|
+
return false unless (options[:table_data].select {|r| !r.is_a?(Array) }).empty?
|
446
|
+
# compute sizes
|
447
|
+
top = options[:xy][1]
|
448
|
+
height = options[:size][1] / options[:max_rows]
|
449
|
+
from_side = options[:xy][0]
|
450
|
+
width = options[:size][0]
|
451
|
+
columns = options[:table_data][0].length
|
452
|
+
column_widths = []
|
453
|
+
columns.times {|i| column_widths << (width/columns) }
|
454
|
+
if options[:column_widths]
|
455
|
+
scale = 0
|
456
|
+
options[:column_widths].each {|w| scale += w}
|
457
|
+
column_widths = []
|
458
|
+
options[:column_widths].each { |w| column_widths << (width*w/scale) }
|
459
|
+
end
|
460
|
+
column_widths = column_widths.reverse if options[:direction] == :rtl
|
461
|
+
# set count and start writing the data
|
462
|
+
row_number = 1
|
463
|
+
|
464
|
+
until (options[:table_data].empty? || row_number > options[:max_rows])
|
465
|
+
# add headers
|
466
|
+
if options[:headers] && row_number == 1
|
467
|
+
x = from_side
|
468
|
+
headers = options[:headers]
|
469
|
+
headers = headers.reverse if options[:direction] == :rtl
|
470
|
+
column_widths.each_index do |i|
|
471
|
+
text = headers[i].to_s
|
472
|
+
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]})
|
473
|
+
x += column_widths[i]
|
474
|
+
end
|
475
|
+
row_number += 1
|
476
|
+
end
|
477
|
+
x = from_side
|
478
|
+
row_data = options[:table_data].shift
|
479
|
+
row_data = row_data.reverse if options[:direction] == :rtl
|
480
|
+
column_widths.each_index do |i|
|
481
|
+
text = row_data[i].to_s
|
482
|
+
box_color = (options[:alternate_color] && ( (row_number.odd? && options[:headers]) || row_number.even? ) ) ? options[:alternate_color] : options[:main_color]
|
483
|
+
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)
|
484
|
+
x += column_widths[i]
|
485
|
+
end
|
486
|
+
row_number += 1
|
487
|
+
end
|
488
|
+
self
|
489
|
+
end
|
490
|
+
|
377
491
|
|
378
492
|
###################################
|
379
493
|
# protected methods
|
@@ -153,8 +153,12 @@ module CombinePDF
|
|
153
153
|
##########################################
|
154
154
|
## parse a Stream
|
155
155
|
##########################################
|
156
|
-
when @scanner.scan(/stream[\r]?[\n]/)
|
157
|
-
|
156
|
+
# when @scanner.scan(/stream[\r]?[\n]/)
|
157
|
+
# str = @scanner.scan_until(/endstream/)
|
158
|
+
when @scanner.scan(/stream(\r\n|\r|\n)/)
|
159
|
+
str = @scanner.scan_until(/(\r\n|\r|\n)endstream/)
|
160
|
+
# raise error if the stream doesn't end.
|
161
|
+
raise "Parsing Error: PDF file error - a stream object wasn't properly colsed using 'endstream'!" unless str
|
158
162
|
# need to remove end of stream
|
159
163
|
if out.last.is_a? Hash
|
160
164
|
out.last[:raw_stream_content] = str[0...-10] #cuts only one EON char (\n or \r)
|
data/lib/combine_pdf/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: combine_pdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03
|
11
|
+
date: 2015-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-rc4
|