rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,92 @@
1
+ # This is a task scheduling WebSocket push example application.
2
+ #
3
+ # Benchmark HTTPP with `ab` or `wrk` (a 5 seconds benchmark with 2000 concurrent clients):
4
+ #
5
+ # ab -c 2000 -t 5 -n 1000000 -k http://127.0.0.1:3000/
6
+ # wrk -c2000 -d5 -t12 http://localhost:3000/
7
+ #
8
+ # Test websocket tasks using the browser. For example:
9
+ # ws = new WebSocket("ws://localhost:3000/userID"); ws.onmessage = function(e) {console.log(e.data);}; ws.onclose = function(e) {console.log("closed")};
10
+ # ws.onopen = function(e) {ws.send(JSON.stringify({'task': 'echo', 'data': 'Hello!'}));};
11
+ require 'iodine'
12
+ require 'json'
13
+
14
+ TASK_PUBLISHING_ENGINE = Iodine::PubSub::PROCESS
15
+
16
+ # This module handles tasks and send them back to the frontend
17
+ module TaskHandler
18
+ def echo msg
19
+ msg = Iodine::JSON.parse(msg, symbolize_names: true)
20
+ publish_to = msg.delete(:from)
21
+ Iodine.publish(publish_to, msg.to_json, TASK_PUBLISHING_ENGINE) if publish_to
22
+ puts "performed 'echo' task"
23
+ rescue => e
24
+ puts "JSON task message error? #{e.message} - under attack?"
25
+ end
26
+
27
+ def add msg
28
+ msg = Iodine::JSON.parse(msg, symbolize_names: true)
29
+ raise "addition task requires an array of numbers" unless msg[:data].is_a?(Array)
30
+ msg[:data] = msg[:data].inject(0){|sum,x| sum + x }
31
+ publish_to = msg.delete(:from)
32
+ Iodine.publish(publish_to, msg.to_json, TASK_PUBLISHING_ENGINE) if publish_to
33
+ puts "performed 'add' task"
34
+ rescue => e
35
+ puts
36
+ "JSON task message error? #{e.message} - under attack?"
37
+ end
38
+
39
+ def listen2tasks
40
+ Iodine.subscribe(:echo) {|ch,msg| TaskHandler.echo(msg) }
41
+ Iodine.subscribe(:add) {|ch,msg| TaskHandler.add(msg) }
42
+ end
43
+
44
+ extend self
45
+ end
46
+
47
+ module WebsocketClient
48
+ def on_open client
49
+ # Pub/Sub directly to the client (or use a block to process the messages)
50
+ client.subscribe client.env['PATH_INFO'.freeze]
51
+ end
52
+ def on_message client, data
53
+ # Strings and symbol channel names are equivalent.
54
+ msg = Iodine::JSON.parse(data, symbolize_names: true)
55
+ raise "no valid task" unless ["echo".freeze, "add".freeze].include? msg[:task]
56
+ msg[:from] = client.env['PATH_INFO'.freeze]
57
+ client.publish msg[:task], msg.to_json, TASK_PUBLISHING_ENGINE
58
+ rescue => e
59
+ puts "JSON message error? #{e.message}\n\t#{data}\n\t#{msg}"
60
+ end
61
+ extend self
62
+ end
63
+
64
+ APP = Proc.new do |env|
65
+ if env['rack.upgrade?'.freeze] == :websocket
66
+ env['rack.upgrade'.freeze] = WebsocketClient
67
+ [0,{}, []] # It's possible to set cookies for the response.
68
+ elsif env['rack.upgrade?'.freeze] == :sse
69
+ puts "SSE connections can only receive data from the server, the can't write."
70
+ env['rack.upgrade'.freeze] = WebsocketClient
71
+ [0,{}, []] # It's possible to set cookies for the response.
72
+ else
73
+ [200, {"Content-Type" => "text/plain"}, ["Send messages with WebSockets using JSON.\ni.e.: {\"task\":\"add\", \"data\":[1,2]}"]]
74
+ end
75
+ end
76
+
77
+ # test automatically for Redis extensions.
78
+ if(Iodine::PubSub.default.is_a? Iodine::PubSub::Redis)
79
+ TASK_PUBLISHING_ENGINE = Iodine::PubSub.default
80
+ if(ARGV.include? "worker")
81
+ TaskHandler.listen2tasks
82
+ Iodine.workers = 1
83
+ Iodine.threads = 16 if Iodine.threads == 0
84
+ Iodine.start
85
+ exit(0)
86
+ end
87
+ else
88
+ TaskHandler.listen2tasks
89
+ end
90
+
91
+ # # or in config.ru
92
+ run APP
@@ -0,0 +1,3 @@
1
+ # Bates numbering with CombinePDF and Rack
2
+
3
+ This folder holds a demo application combining Iodine's `X-Sendfile` support with the [`combine_pdf` gem](https://github.com/boazsegev/combine_pdf) to create a "bates numbering" tool that is common to the one used by some court systems (such as Israeli courts).
@@ -0,0 +1,342 @@
1
+ require 'combine_pdf'
2
+ require 'rack'
3
+ require 'base64'
4
+ # require_relative 'pdf_controller'
5
+
6
+ module BatesAPP
7
+ # This is the HTTP response object according to the Rack specification.
8
+ STATIC_RESPONSE = [200, { 'Content-Type' => 'text/html', 'X-Sendfile' => File.expand_path('./public/index.html') }.freeze, []].freeze
9
+ WS_RESPONSE = [403, { 'Content-Type' => 'text/html' }, "Forbidden"]
10
+
11
+ # this is function will be called by the Rack server (iodine) for every request.
12
+ def self.call env
13
+ req = Rack::Request.new(env)
14
+ # check if this is an upgrade request (WebsSocket / SSE).
15
+ return WS_RESPONSE if(env['rack.upgrade?'.freeze])
16
+ # simply return the RESPONSE object, no matter what request was received.
17
+ return STATIC_RESPONSE if req.params.empty?
18
+ return bates(req.params)
19
+ end
20
+
21
+
22
+ def self.bates(params)
23
+ params['file'] = params['file'].values # convert to Array
24
+ # catch any exception and tell the user which PDF caused the exception.
25
+ begin
26
+ # set the file_name variable
27
+ # this will be used in case an exception is caught
28
+ # to state which file caused the exception.
29
+ file_name = ''
30
+
31
+ # container for the complete pdf
32
+ # the complete pdf container will hold all
33
+ # the merged pdf data.
34
+ completed_pdf = CombinePDF.new
35
+
36
+ # get the output file name
37
+ # this will be used to name the download for the client's browser
38
+ output_name = params['bates']['output'].to_s + '.pdf'
39
+ output_name = "unknown.pdf" if output_name == '.pdf'
40
+
41
+ # get some paramaters that will be used while combining pages
42
+ params['bates'] ||= {}
43
+ first_page_number = params['bates']['first_page_number'].to_i
44
+ first_index_number = params['bates']['first_index_number'].to_i
45
+
46
+ # we will add an option for the stamping to ignore the first pdf
47
+ # this is useful for the court cases that use bates numbering
48
+ # (the "cover PDF" will be the briefs of submissions that contain exhibits to be bates stamped)
49
+ ignore_first_file = params['bates']['first_pdf_is_cover']
50
+ first_page = nil
51
+
52
+ # we will pick some data up while running combining the different pdf files.
53
+ # this will be used for the table of contents later on.
54
+ pdfs_pages_count = []
55
+ pdf_dates = []
56
+ pdf_titles = []
57
+
58
+ # we will be creating title pages before each PDF file.
59
+ # the title pages will be sized using the mediabox variable
60
+ # wich will be set with the dimentions of each pdf file's first page.
61
+ mediabox = nil
62
+
63
+ # register UNICODE fonts if necessary
64
+ # in this example I will register the Hebrew font David from an existing PDF file.
65
+ unless CombinePDF::Fonts.get_font :my_new_david
66
+ # if your running Rails, consider Rails.root instead of Root
67
+ fonts = CombinePDF.new(File.expand_path('./david+bold.pdf').to_s).fonts(true)
68
+ # I know the first font is David regular, after using the
69
+ # ruby console and looking at the fonts array for the file.
70
+ CombinePDF.register_font_from_pdf_object :my_new_david, fonts[0]
71
+ # the second font of the array was the latin font for a newline and space... useless
72
+ # the third was the david bold. I will now add that font.
73
+ CombinePDF.register_font_from_pdf_object :my_new_david_bold, fonts[2]
74
+ end
75
+
76
+ # iterate through the different files sent from the client's browser's web form
77
+ params['file'].each do |v|
78
+ # set the file_name variable in case an exception will be raised
79
+ file_name = v['name']
80
+
81
+ # parse the pdf data
82
+ # we will use the CombinePDF.parse method which allows us
83
+ # to parse data without saving the PDF to the file system.
84
+ # our javascript encoded the file data using base64, which we will need to decode.
85
+ # (this is specific to my form which uses the HTML5 File API in this specific manner)
86
+ # begin
87
+ puts "parsing file: #{file_name}"
88
+ pdf_file = CombinePDF.parse(
89
+ Base64.urlsafe_decode64(
90
+ v['data'].slice('data:application/pdf;base64,'.length,
91
+ v['data'].length)
92
+ )
93
+ )
94
+ # rescue
95
+ # puts "Failed when parsing #{file_name} with data:\n#{v['data'].inspect}"
96
+ # raise
97
+ # end
98
+
99
+ # we will use the pages array a few times, so in order to avoid
100
+ # recomputing the array every time, we will save it to a local variable.
101
+ pdf_file_pages = pdf_file.pages
102
+
103
+ # we will add the page count to the page count array,
104
+ # used by the table of contents
105
+ pdfs_pages_count << pdf_file_pages.length
106
+
107
+ ######
108
+ # create and add title page to arrays.
109
+
110
+ if ignore_first_file && first_page.nil?
111
+ # if the first PDF file is a "cover page" PDF,
112
+ # we will not add a title, nor add the file.
113
+ # instead we will save the data in a variable to add it after we're done
114
+ pdf_dates << ''
115
+ pdf_titles << ''
116
+ first_page = pdf_file
117
+ else
118
+ # set title page mediabox size
119
+ # the mediabox data (page size) is contained in the page's
120
+ # :CropBox or :MediaBox keys (pages are Hash objects).
121
+ mediabox ||= pdf_file_pages[0][:CropBox] || pdf_file_pages[0][:MediaBox]
122
+
123
+ # create a title page unless we're only merging or there is no indexing
124
+ if params['bates']['should_index'] && params['bates']['numbering'] != 3
125
+
126
+ # create an empty page object
127
+ title_page = CombinePDF.create_page mediabox
128
+
129
+ # write the content to the title page.
130
+ # we will be using the I18n.t shortcut to write some of the data.
131
+ # the rest of the data, like the title, we got from the form.
132
+ title_page.textbox("#{params['bates']['title_type']} #{pdfs_pages_count.length + first_index_number - (ignore_first_file ? 2 : 1)}",
133
+ max_font_size: 34,
134
+ font: :my_new_david,
135
+ y: (mediabox[3] - mediabox[1]) / 2) unless params['bates']['title_type'].to_s.empty?
136
+ title_page.textbox v['title'].to_s, max_font_size: 36, font: :my_new_david_bold
137
+ title_page.textbox v['date'].to_s, max_font_size: 24, font: :my_new_david, height: (mediabox[3] - mediabox[1]) / 2
138
+
139
+ # we will add the page object to the completed pdf object.
140
+ # notice that page objects are created as "floating" pages,
141
+ # not attached to any specific PDF file/object.
142
+ completed_pdf << title_page
143
+
144
+ # we will add some data that will be used to create the
145
+ # table of contents at a later stage.
146
+ page_count = pdfs_pages_count.pop + 1
147
+ pdfs_pages_count << page_count
148
+ pdf_dates << v['date'].to_s
149
+ pdf_titles << v['title'].to_s
150
+ end
151
+
152
+ # now we are ready to add the pdf file data to the completed pdf object.
153
+ # there is no need to add each page, we can add the pdf as a whole.
154
+ # (it's actually faster, as the PDF page catalog isn't recomputed for each page)
155
+ completed_pdf << pdf_file
156
+ end
157
+ end
158
+
159
+ ##########
160
+ # create the index pdf...
161
+ # ...unless we're only merging or there is no indexing
162
+ if params['bates']['should_index'] && params['bates']['numbering'].to_i != 3
163
+
164
+ # set the fonts and formatting for the table of contents.
165
+ #
166
+ # also, add an empty array for the table data.
167
+ #
168
+ # the table data array will contain arrays of String objects, each one
169
+ # corresponding to a row in the table.
170
+ table_options = { font: :my_new_david,
171
+ header_font: :my_new_david_bold,
172
+ max_font_size: 12,
173
+ column_widths: (params['bates']['date_header'].to_s.empty? ? [3, 40, 4] : [3, 10, 30, 4]),
174
+ table_data: [] }
175
+
176
+ # set the table header array.
177
+ # this is an array of strings.
178
+ # to chose the localized strings
179
+ table_options[:headers] = [params['bates']['number_header'],
180
+ (params['bates']['date_header'].to_s.empty? ? nil : params['bates']['date_header']),
181
+ params['bates']['title_header'].to_s,
182
+ params['bates']['page_header'].to_s]
183
+ table_options[:headers].compact!
184
+
185
+ # by default, there are 25 rows per page for table pdf created by CombinePDF
186
+ # we can override this in the formatting (but we didn't).
187
+ #
188
+ # the 25 rows include 1 header row per page - so there are only 24 effective rows.
189
+ #
190
+ # we will calculate how many pages the table of contents pdf will have once completes,
191
+ # so we can add the count to the page numbers in the index.
192
+ index_page_length = pdfs_pages_count.length / 24
193
+ index_page_length += 1 if pdfs_pages_count.length % 24 > 0
194
+
195
+ # set the page number for the first entry in the table of contents.
196
+ page_number = first_page_number + index_page_length
197
+
198
+ # set the index count to 0, we will use it to change the index for each entry.
199
+ # we need a different variable in case the first PDF file is a "cover page".
200
+ index_count = 0
201
+
202
+ # iterate over the data we collected before and add it to the table data.
203
+ pdfs_pages_count.each_index do |i|
204
+ # add the data unless it is set to be ignored
205
+ unless ignore_first_file
206
+
207
+ # add an array of strings to the :table_data array,
208
+ # representing a row in our table.
209
+ # remember there might not be a date column.
210
+ if params['bates']['date_header'].to_s.empty?
211
+ table_options[:table_data] << [(first_index_number + index_count).to_s,
212
+ pdf_titles[i], page_number]
213
+ else
214
+ table_options[:table_data] << [(first_index_number + index_count).to_s,
215
+ pdf_dates[i],
216
+ pdf_titles[i], page_number]
217
+ end
218
+
219
+ # if the data was added to the index table, bump the index count
220
+ index_count += 1
221
+
222
+ end
223
+
224
+ # make sure future data will not be ignored
225
+ ignore_first_file = false
226
+
227
+ # add the page count to the page number, so that the next
228
+ # index's page number is up to date.
229
+ page_number += pdfs_pages_count[i]
230
+ end
231
+
232
+ # if out current locale is hebrew, which is a right to left language,
233
+ # set the direction for the table to Right-To-left (:rtl).
234
+ #
235
+ # notice that RTL text should be automatically recognized, but that
236
+ # feature isn't available (and shouldn't be available) for tables.
237
+ table_options[:direction] = :rtl if params['bates']['dir'] == 'rtl'
238
+
239
+ # if there is table data, we will create an index pdf.
240
+ unless table_options[:table_data].empty?
241
+
242
+ # create the index PDF from the table data and options we have.
243
+ index_pdf = CombinePDF.create_table table_options
244
+
245
+ # We will now add the words "Table of Contents" (or the I18n equivilant)
246
+ # to the first page of our new index_pdf PDF object.
247
+ #
248
+ # the table PDF object was created by CombinePDF using writable PDF pages,
249
+ # so we have properties like .mediabox and methods like .textbox
250
+ # at our disposal.
251
+
252
+ # get the first page of the index_pdf object, we will use this reference a lot.
253
+ title_page = index_pdf.pages[0]
254
+
255
+ # write the textbox, using the mediabox page data [x,y,width,height] to place
256
+ # the text we want to write.
257
+ #
258
+ # we will use the I18n.t shortcut to choose the text to write down.
259
+ title_page.textbox params['bates']['index_title'],
260
+ y: ((title_page.mediabox[3] - title_page.mediabox[1]) * 0.91),
261
+ height: ((title_page.mediabox[3] - title_page.mediabox[1]) * 0.03),
262
+ font: :my_new_david,
263
+ max_font_size: 36,
264
+ text_valign: :bottom
265
+
266
+ # now we will add the index_pdf to the BEGINING of the completed pdf.
267
+ # for this we will use the >> operator instead of the << operator.
268
+ completed_pdf >> index_pdf
269
+
270
+ end
271
+
272
+ end
273
+
274
+ # add first file if it was skipped
275
+ completed_pdf >> first_page unless first_page.nil?
276
+
277
+ #####
278
+ # number pages
279
+ # unless no numbering
280
+ if params['bates']['numbering'].to_i != 3
281
+ # list the numbering options
282
+ numbering_options = [[:top, :bottom], [:top_left, :bottom_left], [:top_right, :bottom_right]]
283
+
284
+ # set the first visible page number to the page where numbering starts
285
+ # this assumes that the bates numbering include the numbering of the "cover page",
286
+ # yet at the same time the numbering isn't visible on the "cover page"
287
+ first_page_number += pdfs_pages_count[0] if params['bates']['first_pdf_is_cover'] && params['bates']['skip_cover']
288
+
289
+ # call the page numbering method and
290
+ # add the special properties we want for the textbox
291
+ completed_pdf.number_pages(start_at: first_page_number,
292
+ page_range: params['bates']['skip_cover'] ? (pdfs_pages_count[0].to_i..-1) : nil,
293
+ font_name: :my_new_david,
294
+ font_size: 14,
295
+ font_color: [0, 0, 0.4],
296
+ box_color: [0.8, 0.8, 0.8],
297
+ border_width: 1,
298
+ border_color: [0.3, 0.3, 0.3],
299
+ box_radius: 8,
300
+ number_location: numbering_options[params['bates']['numbering'].to_i],
301
+ opacity: 0.75)
302
+ end
303
+
304
+ # send the completed PDF to the client.
305
+ # if the completed PDF is empty, raise an error.
306
+ if completed_pdf.pages.empty?
307
+ # inform the client there was an unknown error.
308
+ response = STATIC_RESPONSE.dup
309
+ response[1] = response[1].dup
310
+ response[1]['set-cookie'] = "Unknown error - 0 pages."
311
+ return response
312
+ end
313
+
314
+ # make sure the PDF version is high enough for the opacity we used in the page numbering.
315
+ completed_pdf.version = [completed_pdf.version, 1.6].max
316
+
317
+ # we will format the PDF to a pdf file WITHOUT saving it to the file system,
318
+ # using the .to_pdf method (instead of the .save method).
319
+ # we will send the raw PDF data stream.
320
+ return [200, {'content-type' => "application/pdf",
321
+ 'content-disposition' => "attachment; filename=\"#{output_name}\""}, completed_pdf.to_pdf]
322
+
323
+ rescue Exception => e
324
+ puts e.message, e.backtrace
325
+ puts "The file causing the exception is: #{file_name}"
326
+ # if an exception was raised, tell the user which PDF caused the exception
327
+ response = STATIC_RESPONSE.dup
328
+ response[1] = response[1].dup
329
+ response[1]['set-cookie'] = "notice=Unsupported or error reading file: #{file_name}"
330
+ return response
331
+ end
332
+ end
333
+ end
334
+
335
+ Iodine.workers = 1
336
+ Iodine.threads = 5
337
+ Iodine::DEFAULT_SETTINGS[:public] ||= './public'
338
+
339
+ p Iodine::DEFAULT_SETTINGS
340
+ Iodine::DEFAULT_SETTINGS[:max_body] = 70 if Iodine::DEFAULT_SETTINGS[:max_body].to_i < 5
341
+
342
+ run BatesAPP
Binary file
Binary file