copper-cti 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,566 @@
1
+ require File.expand_path('CTIP2/CTIP2', File.dirname(__FILE__))
2
+ require File.expand_path('Builder/FileBuilder', File.dirname(__FILE__))
3
+ require File.expand_path('Results/SingleResult', File.dirname(__FILE__))
4
+ require File.expand_path('Builder/StreamBuilder', File.dirname(__FILE__))
5
+ require File.expand_path('Results/DirectoryResults', File.dirname(__FILE__))
6
+ require "io/nonblock"
7
+
8
+ include CTI::Builder, CTI::Results
9
+
10
+ module CTI
11
+ =begin rdoc
12
+ Version:: $Id: Session.rb 1617 2022-01-24 07:26:44Z miyabe $
13
+
14
+ ドキュメント変換サーバーへの接続オブジェクトです。
15
+ =end
16
+ class Session
17
+ =begin rdoc
18
+ セッションを構築します。このメソッドを直接呼び出す必要はありません。
19
+
20
+ CTI#get_session メソッドを使用してください。
21
+
22
+ io:: サーバーと通信するための IO オブジェクト
23
+ opts:: 接続オプション(ハッシュ型で、'encoding', 'user', 'password'というキーで文字コード、ユーザー名、パスワードを設定することができます。)
24
+
25
+ 返り値:: サーバー情報のデータ文字列(XML)
26
+ =end
27
+ def initialize(io, opts)
28
+ self.reset
29
+ @io = io
30
+
31
+ io.extend CTIP2
32
+
33
+ opts.default = 'UTF-8'
34
+ @encoding = opts['encoding']
35
+ opts.default = ''
36
+ user = opts['user']
37
+ password = opts['password']
38
+
39
+ io.cti_connect(@encoding)
40
+ data = "PLAIN: #{user} #{password}\n"
41
+ io.write(data)
42
+ res = io.read(4)
43
+ raise "Authentication failure." if res != "OK \n"
44
+ end
45
+
46
+ =begin rdoc
47
+ サーバー情報を返します。
48
+ 詳細は{オンラインのドキュメント}[http://sourceforge.jp/projects/copper/wiki/CTIP2.0%E3%81%AE%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E6%83%85%E5%A0%B1]をご覧下さい。
49
+
50
+ uri:: サーバー情報のURI
51
+
52
+ 返り値:: サーバー情報のデータ文字列(XML)
53
+ =end
54
+
55
+ def get_server_info(uri)
56
+ @io.req_server_info(uri)
57
+ data = ''
58
+ while true
59
+ res = @io.res_next
60
+ break if res['type'] == CTIP2::RES_EOF
61
+ data += res['bytes']
62
+ end
63
+ return data
64
+ end
65
+
66
+ =begin rdoc
67
+ 変換結果の出力先を指定します。
68
+
69
+ CTI::Session#transcode および CTI::Session#transcode_server の前に呼び出してください。
70
+ この関数を呼び出さないデフォルトの状態では、出力先は標準出力( STDOUT )になります。
71
+
72
+ また、デフォルトの状態では、自動的にContent-Type, Content-Lengthヘッダが送出されます。
73
+
74
+ results:: 出力先の CTI::Results オブジェクト
75
+ =end
76
+
77
+ def set_results(results)
78
+ raise "set_results: Main content is already sent." if @state >= 2
79
+ @results = results
80
+ end
81
+
82
+ =begin rdoc
83
+ 変換結果の出力先ファイル名を指定します。
84
+
85
+ CTI::Session#set_results の簡易版です。
86
+ こちらは、1つだけ結果を出力するファイル名を直接設定することができます。
87
+
88
+ file:: 出力先ファイル名
89
+ =end
90
+
91
+ def set_output_as_file(file)
92
+ set_results(SingleResult.new(FileBuilder.new(file)))
93
+ end
94
+
95
+ =begin rdoc
96
+ 変換結果の出力先ディレクトリ名を指定します。
97
+
98
+ CTI::Session#set_results の簡易版です。
99
+ こちらは、複数の結果をファイルとして出力するディレクトリ名を直接設定することができます。
100
+ ファイル名は prefix ページ番号 suffix をつなげたものです。
101
+
102
+ dir:: 出力先ディレクトリ名
103
+ prefix:: 出力するファイルの名前の前に付ける文字列
104
+ suffix:: 出力するファイルの名前の後に付ける文字列
105
+ =end
106
+
107
+ def set_output_as_directory(dir, prefix = '', suffix = '')
108
+ set_results(DirectoryResults.new(dir, prefix, suffix))
109
+ end
110
+
111
+ =begin rdoc
112
+ 変換結果の出力先リソースを指定します。
113
+
114
+ CTI::Session#set_results の簡易版です。
115
+ こちらは、1つだけの結果出力先を直接設定することができます。
116
+
117
+ out:: 出力先 IO オブジェクト
118
+ =end
119
+
120
+ def set_output_as_stream(out)
121
+ set_results(SingleResult.new(StreamBuilder.new(out)))
122
+ end
123
+
124
+ =begin rdoc
125
+ エラーメッセージ受信のためのブロックを設定します。
126
+
127
+ CTI::Session#transcode および CTI::Session#transcode_server の前に呼び出してください。
128
+ ブロックの引数は、エラーコード(int)、メッセージ(string)、付属データ(array)です。
129
+
130
+ &messageFunc:: ブロック
131
+
132
+ 例:
133
+ session.receive_message do |code, message, args|
134
+ printf("%X %s\n", code, message)
135
+ p args
136
+ end
137
+ =end
138
+
139
+ def receive_message(&messageFunc)
140
+ raise "receive_message: Main content is already sent." if @state >= 2
141
+ @messageFunc = messageFunc;
142
+ end
143
+
144
+ =begin rdoc
145
+ 進行状況受信のためのブロックを設定します。
146
+
147
+ CTI::Session#transcode および CTI::Session#transcode_server の前に呼び出してください。
148
+ ブロックの引数は、全体のバイト数(int)、読み込み済みバイト数(int)です。
149
+
150
+ &progressFunc:: ブロック
151
+
152
+ 例:
153
+ session.receive_progress do |length, read|
154
+ puts "#{read} / #{length}"
155
+ end
156
+ =end
157
+
158
+ def receive_progress(&progressFunc)
159
+ raise "receive_progress: Main content is already sent." if @state >= 2
160
+ @progressFunc = progressFunc;
161
+ end
162
+
163
+ =begin rdoc
164
+ リソース解決のためのブロックを設定します。
165
+
166
+ CTI::Session#transcode および CTI::Session#transcode_server の前に呼び出してください。
167
+ ブロックの引数は、URI(string)、リソース出力クラス( CTI::Resource )です。
168
+
169
+ &resolverFunc:: ブロック
170
+
171
+ URIに対応するリソースが見つかった場合、 CTI::Resource#found メソッドを呼び出してください。
172
+ foundの呼び出しがない場合、リソースは見つからなかったものと扱われます。
173
+
174
+ 例:
175
+ session.resolver do |uri, r|
176
+ if File.exist?(uri)
177
+ r.found do |out|
178
+ copy_stream(File.open(uri), out)
179
+ end
180
+ end
181
+ end
182
+ =end
183
+
184
+ def resolver(&resolverFunc)
185
+ raise "resolver: Main content is already sent." if @state >= 2
186
+ @resolverFunc = resolverFunc;
187
+ @io.req_client_resource(resolverFunc ? 1 : 0)
188
+ end
189
+
190
+ =begin rdoc
191
+ 複数の結果を結合するモードを切り替えます。
192
+ モードが有効な場合、 CTI::Session#join の呼び出しで複数の結果を結合して返します。
193
+
194
+ CTI::Session#transcode および CTI::Session#transcode_server の前に呼び出してください。
195
+
196
+ continuous:: 有効にするにはtrue
197
+ =end
198
+
199
+ def set_continuous(continuous)
200
+ raise "set_continuous: Main content is already sent." if @state >= 2
201
+ @io.req_continuous(continuous ? 1 : 0)
202
+ end
203
+
204
+ =begin rdoc
205
+ プロパティを設定します。
206
+
207
+ セッションを作成した直後に呼び出してください。
208
+ 利用可能なプロパティの一覧は{資料集}[http://dl.cssj.jp/docs/copper/3.0/html/5100_io-properties.html]を参照してください。
209
+
210
+ name:: 名前
211
+ value:: 値
212
+ =end
213
+
214
+ def property(name, value)
215
+ raise "property: Main content is already sent." if @state >= 2
216
+ @io.req_property(name, value)
217
+ end
218
+
219
+ =begin rdoc
220
+ リソース送信処理を行います。
221
+ CTI::Session#transcode および CTI::Session#transcode_server の前に呼び出してください。
222
+
223
+ uri:: 仮想URI
224
+ opts:: リソースオプション(ハッシュ型で、'mime_type', 'encoding', 'length'というキーでデータ型、文字コード、長さを設定することができます。)
225
+ &block:: リソースを送信するためのブロックで、引数としてリソースの出力先ストリームが渡されます。
226
+
227
+ 戻り値:: &blockがない場合はリソースの出力先ストリームが返されます。
228
+
229
+ 例:
230
+ session.resource('test.css', {'mime_type' => 'test/css'}) do |out|
231
+ copy_stream(File.open('data/test.css'), out)
232
+ end
233
+ =end
234
+
235
+ def resource(uri, opts = {}, &block)
236
+ raise "resource: Main content is already sent." if @state >= 2
237
+ opts.default = 'text/css'
238
+ mime_type = opts['mime_type']
239
+ opts.default = ''
240
+ encoding = opts['encoding']
241
+ opts.default = -1
242
+ length = opts['length']
243
+ @io.req_resource(uri, mime_type, encoding, length)
244
+
245
+ out = ResourceOut.new(@io)
246
+ if block
247
+ begin
248
+ block.call(out)
249
+ ensure
250
+ out.close
251
+ end
252
+ else
253
+ return out
254
+ end
255
+ end
256
+
257
+ =begin rdoc
258
+ 変換対象の文書リソースを送信し、変換処理を行います。
259
+
260
+ uri:: 仮想URI
261
+ opts:: リソースオプション(ハッシュ型で、'mime_type', 'encoding', 'length'というキーでデータ型、文字コード、長さを設定することができます。)
262
+ &block:: リソースを送信するためのブロックで、引数としてリソースの出力先ストリームが渡されます。
263
+
264
+ 戻り値:: &blockがない場合はリソースの出力先ストリームが返されます。
265
+
266
+ 例:
267
+ session.transcode('.', {'mime_type' => 'text/html'}) do |out|
268
+ copy_stream(File.open('data/test.html'), out)
269
+ end
270
+ =end
271
+
272
+ def transcode(uri = '.', opts = {}, &block)
273
+ raise "transcode: Main content is already sent." if @state >= 2
274
+ opts.default = 'text/css'
275
+ mime_type = opts['mime_type']
276
+ opts.default = ''
277
+ encoding = opts['encoding']
278
+ opts.default = -1
279
+ length = opts['length']
280
+ state = 2
281
+ @io.req_start_main(uri, mime_type, encoding, length)
282
+
283
+ out = MainOut.new(@io, self)
284
+ if block
285
+ begin
286
+ block.call(out)
287
+ ensure
288
+ out.close
289
+ end
290
+ else
291
+ return out
292
+ end
293
+ end
294
+
295
+ =begin rdoc
296
+ サーバー側リソースを変換します。
297
+
298
+ uri:: URI
299
+ =end
300
+
301
+ def transcode_server(uri)
302
+ raise "transcode_server: Main content is already sent." if @state >= 2
303
+ @io.req_server_main(uri)
304
+ @state = 2
305
+ while build_next
306
+ end
307
+ end
308
+
309
+ =begin rdoc
310
+ <b>DEPRECATED:</b> {#transcode_server} を使って下さい。
311
+
312
+ サーバー側リソースを変換します。
313
+
314
+ uri:: URI
315
+ =end
316
+ def transcodeServer(uri)
317
+ transcode_server(uri)
318
+ end
319
+
320
+ =begin rdoc
321
+ 変換処理の中断を要求します。
322
+
323
+ mode:: 中断モード 0=生成済みのデータを出力して中断, 1=即時中断
324
+ =end
325
+
326
+ def abort(mode)
327
+ raise "abort: The session is already closed." if @state >= 3
328
+ @io.req_abort(mode)
329
+ end
330
+
331
+ =begin rdoc
332
+ 全ての状態をリセットします。
333
+ =end
334
+
335
+ def reset
336
+ raise "reset: The session is already closed." if @state && @state >= 3
337
+ if @io
338
+ @io.req_reset
339
+ end
340
+ @progressFunc = nil
341
+ @messageFunc = nil
342
+ @resolverFunc = nil
343
+ @results = SingleResult.new(StreamBuilder.new(STDOUT) do |length|
344
+ print "Content-Length: #{length}\r\n\r\n"
345
+ end) do |opts|
346
+ print "Content-Type: #{opts['mime_type']}\r\n"
347
+ end
348
+ @state = 1
349
+ end
350
+
351
+ =begin rdoc
352
+ 結果を結合します。
353
+
354
+ 先に CTI::Session#set_continuous を呼び出しておく必要があります。
355
+ =end
356
+
357
+ def join
358
+ raise "join: The session is already closed." if @state >= 3
359
+ @io.req_join
360
+ @state = 2
361
+ while build_next
362
+ end
363
+ end
364
+
365
+ =begin rdoc
366
+ セッションを閉じます。
367
+ =end
368
+
369
+ def close
370
+ raise "close: The session is already closed." if @state >= 3
371
+ @io.req_close;
372
+ @state = 3
373
+ end
374
+
375
+ def build_next
376
+ res = @io.res_next
377
+ case res['type']
378
+ when CTIP2::RES_START_DATA
379
+ if @builder
380
+ @builder.finish
381
+ @builder.dispose
382
+ end
383
+ @builder = @results.next_builder(res)
384
+
385
+ when CTIP2::RES_BLOCK_DATA
386
+ @builder.write(res['block_id'], res['bytes'])
387
+
388
+ when CTIP2::RES_ADD_BLOCK
389
+ @builder.add_block
390
+
391
+ when CTIP2::RES_INSERT_BLOCK
392
+ @builder.insert_block_before(res['block_id'])
393
+
394
+ when CTIP2::RES_CLOSE_BLOCK
395
+ @builder.close_block(res['block_id'])
396
+
397
+ when CTIP2::RES_DATA
398
+ @builder.serial_write(res['bytes'])
399
+
400
+ when CTIP2::RES_MESSAGE
401
+ @messageFunc.call(res['code'], res['message'], res['args']) if @messageFunc
402
+
403
+ when CTIP2::RES_MAIN_LENGTH
404
+ @mainLength = res['length']
405
+ @progressFunc.call(@mainLength, @mainRead) if @progressFunc
406
+
407
+ when CTIP2::RES_MAIN_READ
408
+ @mainRead = res['length']
409
+ @progressFunc.call(@mainLength, @mainRead) if @progressFunc
410
+
411
+ when CTIP2::RES_RESOURCE_REQUEST
412
+ uri = res['uri']
413
+ r = Resource.new(@io, uri)
414
+ @resolverFunc.call(uri, r) if @resolverFunc
415
+ r.finish
416
+ @io.req_missing_resource(uri) if r.missing
417
+
418
+ when CTIP2::RES_ABORT
419
+ if @builder
420
+ @builder.finish if res['mode'] == 0
421
+ @builder.dispose
422
+ @builder = nil
423
+ end
424
+ @mainLength = nil
425
+ @mainRead = nil
426
+ @state = 1
427
+ return false
428
+
429
+ when CTIP2::RES_EOF
430
+ @builder.finish
431
+ @builder.dispose
432
+ @builder = nil
433
+ @mainLength = nil
434
+ @mainRead = nil
435
+ @state = 1
436
+ return false
437
+
438
+ when CTIP2::RES_NEXT
439
+ @state = 1
440
+ return false
441
+ end
442
+ return true
443
+ end
444
+ end
445
+
446
+ protected
447
+
448
+ class ResourceOut
449
+ def initialize(io)
450
+ @io = io
451
+ @closed = false
452
+ end
453
+
454
+ def write(str)
455
+ e = str.encoding;
456
+ str.force_encoding(Encoding::ASCII_8BIT)
457
+ while str.bytesize > 0
458
+ data = str.slice!(0, CTIP2::CTI_BUFFER_SIZE)
459
+ @io.req_write(data)
460
+ end
461
+ str.force_encoding(e)
462
+ end
463
+
464
+ def close
465
+ unless @closed
466
+ @io.req_eof
467
+ @cloded = true
468
+ end
469
+ end
470
+ end
471
+
472
+ class MainOut
473
+ def initialize(io, session)
474
+ @io = io
475
+ @session = session
476
+ end
477
+
478
+ def write(str)
479
+ e = str.encoding;
480
+ str.force_encoding(Encoding::ASCII_8BIT)
481
+ # SSLSocket は write_nonblock を持つが nonblock= を持たない。
482
+ # 両方あるときだけ非ブロック書き込みを使う(2026-09-10。
483
+ # ctips:// で `undefined method 'nonblock='` になっていた)
484
+ bio = @io.respond_to?('write_nonblock') && @io.respond_to?('nonblock=')
485
+ while str.bytesize > 0
486
+ data = str.slice!(0, CTIP2::CTI_BUFFER_SIZE)
487
+ packet = [data.bytesize + 1, CTIP2::REQ_DATA].pack('NC') + data
488
+ while packet.bytesize > 0
489
+ rs, ws = IO::select([@io], [@io])
490
+ if packet.bytesize > 0 && ws[0]
491
+ if bio
492
+ len = @io.write_nonblock(packet)
493
+ @io.nonblock = false
494
+ else
495
+ len = @io.write(packet)
496
+ end
497
+ packet.slice!(0, len)
498
+ end
499
+ if rs[0]
500
+ @session.build_next
501
+ end
502
+ end
503
+ end
504
+ str.force_encoding(e)
505
+ end
506
+
507
+ def close
508
+ @io.req_eof
509
+ while @session.build_next
510
+ end
511
+ end
512
+ end
513
+
514
+ =begin rdoc
515
+ CTI::Session#resolver により設定したブロック内で、サーバーにリソースを送るためのオブジェクトです。
516
+ =end
517
+
518
+ class Resource
519
+ def initialize(io, uri)
520
+ @io = io
521
+ @uri = uri
522
+ @missing = true
523
+ end
524
+
525
+ def missing
526
+ @missing
527
+ end
528
+ =begin rdoc
529
+ サーバーから要求されたリソースが見つかった場合の処理をします。
530
+
531
+ opts:: リソースオプション(ハッシュ型で、'mime_type', 'encoding', 'length'というキーでデータ型、文字コード、長さを設定することができます。)
532
+ &block:: リソースを送信するためのブロックで、引数としてリソースの出力先ストリームが渡されます。
533
+
534
+ 戻り値:: &blockがない場合はリソースの出力先ストリームが返されます。
535
+
536
+ 例:
537
+ CTI::Session#resolver を参照してください。
538
+ =end
539
+
540
+ def found(opts = {}, &block)
541
+ opts.default = 'text/css'
542
+ mime_type = opts['mime_type']
543
+ opts.default = ''
544
+ encoding = opts['encoding']
545
+ opts.default = -1
546
+ length = opts['length']
547
+ @io.req_resource(@uri, mime_type, encoding, length)
548
+ @missing = false;
549
+ @out = ResourceOut.new(@io)
550
+ if block
551
+ begin
552
+ block.call(@out)
553
+ ensure
554
+ @out.close
555
+ @out = nil
556
+ end
557
+ else
558
+ return @out
559
+ end
560
+ end
561
+
562
+ def finish
563
+ @out.close if @out
564
+ end
565
+ end
566
+ end
data/src/code/CTI.rb ADDED
@@ -0,0 +1,53 @@
1
+ require File.expand_path('CTI/Driver', File.dirname(__FILE__))
2
+ =begin rdoc
3
+ :main:CTI
4
+ = CTI driver for Ruby
5
+ Version:: $Id: CTI.rb 1617 2022-01-24 07:26:44Z miyabe $
6
+
7
+ RubyでCopper PDF 2.1以降にアクセスするためのドライバです。
8
+ 以下のドキュメントを参照してください。
9
+
10
+ http://dl.cssj.jp/docs/copper/3.1/html/3424_ctip2_ruby.html
11
+ =end
12
+ module CTI
13
+ module_function
14
+ =begin rdoc
15
+ 指定されたURIに接続するためのドライバを返します。
16
+
17
+ uri:: 接続先アドレス
18
+
19
+ 返り値:: CTI::Driver オブジェクト
20
+ =end
21
+ def get_driver(uri)
22
+ return Driver.new
23
+ end
24
+
25
+ =begin rdoc
26
+ 指定されたURIに接続し、セッションを返します。
27
+
28
+ uri:: 接続先アドレス
29
+ options:: 接続オプション
30
+
31
+ 返り値:: CTI::Session オブジェクト
32
+ =end
33
+ def get_session(uri, options = {}, &block)
34
+ get_driver(uri).get_session(uri, options, &block)
35
+ end
36
+
37
+ =begin rdoc
38
+ 指定された inp から out へコピーします。コピーしたバイト数を返します。
39
+ inp には read メソッド、out には write メソッドが必要です。
40
+ 仕様が変わってしまったRubyのFileUtils::copy_stream, IO::copy_streamに代わるものです。
41
+
42
+ inp:: 転送元
43
+ out:: 転送先
44
+
45
+ =end
46
+ def copy_stream(inp, out)
47
+ read = 0;
48
+ while buff = inp.read(16 * 1024)
49
+ read += buff.length
50
+ out.write(buff)
51
+ end
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: copper-cti
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Zamasoft
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Ruby driver for connecting to the Copper PDF document conversion server
13
+ via the CTIP protocol.
14
+ email:
15
+ - info@zamasoft.jp
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - src/code/CTI.rb
21
+ - src/code/CTI/Builder/FileBuilder.rb
22
+ - src/code/CTI/Builder/NullBuilder.rb
23
+ - src/code/CTI/Builder/StreamBuilder.rb
24
+ - src/code/CTI/CTIP2/CTIP2.rb
25
+ - src/code/CTI/CTIP2/Utils.rb
26
+ - src/code/CTI/Driver.rb
27
+ - src/code/CTI/Results/DirectoryResults.rb
28
+ - src/code/CTI/Results/SingleResult.rb
29
+ - src/code/CTI/Session.rb
30
+ homepage: https://github.com/zamasoftnet/cti.ruby
31
+ licenses:
32
+ - Apache-2.0
33
+ metadata:
34
+ source_code_uri: https://github.com/zamasoftnet/cti.ruby
35
+ rdoc_options: []
36
+ require_paths:
37
+ - src/code
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.8.7
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 4.0.3
50
+ specification_version: 4
51
+ summary: CTI driver for Copper PDF server
52
+ test_files: []