dptm6 1.0.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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e63193f44881d5e5a8a61cbd10de60f0a18149f8881f1fbff8d3a4bb3cf26da4
4
+ data.tar.gz: 5ba36893056cc101acd5b25897aa6b596d1df678f98fa7aed07a9c34b362e91c
5
+ SHA512:
6
+ metadata.gz: 98f1a69940881ba2517ad09e12a6b330386c17c88d4e0c490f37017742948aae00d62ad592ce350a93a7fd7fc591ccfb5e8046778b21bf53062223cfa4ad6b83
7
+ data.tar.gz: 88e9d0f8b5e018751fc37939dbd0f45acc62ae24603593f6cf5b282a444fce9aa516d25f7897edc0f706f56fc3dc5c27d9283e910b9c20933b6ccd4c49dbb658
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in dptm6.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Masahiro Nomoto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # dptm6 (dclpdftonemerge for DCL 6)
2
+
3
+ このgemは、[地球流体電脳ライブラリ](https://www.gfd-dennou.org/library/dcl/)(DCL)によって描画・出力したPDFファイルのトーン(塗りつぶし)の断片を結合してファイルを軽くするコマンド `dptm6` を提供します。
4
+
5
+ DCLはバージョン5まで独自のPostScriptファイルを出力していましたが、バージョン6から[cairo](https://www.cairographics.org)を用いたPDF出力に変更されました。以前のバージョン用のアプリケーション `dptm2` は[GFD電脳Ruby小物置き場](http://davis.gfd-dennou.org/rubygadgets/ja/?%28Application%29+DCLのPSファイルのトーンを結合する2)で公開しています。
6
+
7
+ ## インストール
8
+
9
+ Rubyがインストールされた環境で以下のコマンドを実行してください。
10
+
11
+ $ gem install dptm6
12
+
13
+ ## 使い方
14
+
15
+ 大前提として、このコマンドはDCLで出力して未編集のPDFに対してのみ正しく動作します。
16
+
17
+ 基本的な使い方は、コマンドの後ろに変換したいPDFファイルを指定するだけです。変換後のファイル名は、元の名前に番号を付け加えたものになります。(番号は既存のファイルを上書きしないように選ばれます)
18
+
19
+ ```
20
+ $ ls *.pdf
21
+ dcl.pdf
22
+
23
+ $ dptm6 dcl.pdf
24
+ $ ls *.pdf
25
+ dcl.pdf dcl_1.pdf
26
+
27
+ $ dptm6 dcl.pdf
28
+ $ ls *.pdf
29
+ dcl.pdf dcl_1.pdf dcl_2.pdf
30
+ ```
31
+
32
+ 出力するファイル名を指定することもできます。入力ファイルの全ページをつなげたPDFができます。
33
+
34
+ $ dptm6 -o dcl-merged.pdf dcl-a.pdf dcl-b.pdf dcl-c.pdf
35
+
36
+ 入力ファイルの一部ページだけを抽出することもできます。(先頭を0ページ目と数えます)
37
+
38
+ $ dptm6 input.pdf[0,5...2,8..-1] #=> [0,5,4,3,8,9,...,n-1]
39
+
40
+ その他の説明はヘルプを参照してください。だたし、現在は開発時のデバッグ用オプションしかありません。
41
+
42
+ $ dptm6 -h
43
+
44
+ ## ライセンス
45
+
46
+ このgemは、[MITライセンス](https://opensource.org/licenses/MIT)の条件の下でオープンソースとして利用可能です。
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dptm6"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "dptm6/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dptm6"
8
+ spec.version = DPTM6::VERSION
9
+ spec.authors = ["Masahiro Nomoto"]
10
+ spec.email = ["hmmnrst@users.noreply.github.com"]
11
+
12
+ spec.summary = %q{dclpdftonemerge for DCL 6}
13
+ spec.description = %q{Merge tones in PDF made by DCL (Dennou Club Library) ver. 6}
14
+ spec.homepage = "https://github.com/hmmnrst/dptm6"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.16"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dptm6'
4
+ require 'dptm6/cli'
5
+
6
+ DPTM6::CLI.new(ARGV).exec
@@ -0,0 +1,4 @@
1
+ require "dptm6/version"
2
+
3
+ module DPTM6
4
+ end
@@ -0,0 +1,123 @@
1
+ require 'zlib'
2
+ require 'dptm6/pdf'
3
+
4
+ module DPTM6; class CLI
5
+ def initialize(args)
6
+ @options = { :compression_level => Zlib::DEFAULT_COMPRESSION, :optimization_level => 1 }
7
+ @files = []
8
+
9
+ args = args.dup
10
+ while (arg = args.shift)
11
+ case arg
12
+ when /\A-C(\d++)?/ # compression level
13
+ @options[:compression_level] = ($~[1] || Zlib::DEFAULT_COMPRESSION).to_i
14
+ when /\A-O(\d?+)/ # optimization level
15
+ @options[:optimization_level] = ($~[1] || 1).to_i
16
+ when /\A-o/ # output file name
17
+ @options[:outfile] = args.shift
18
+ when /\A-h/i
19
+ @options[:help] = true
20
+ else
21
+ file = arg.sub(/\[([^\[\]]*+)\]\z/, "")
22
+ str = ($~ ? $~[1] : "0..-1")
23
+ pages = str.gsub("\s", "").split(",").collect do |range|
24
+ md = range.match(/\A(-?+\d++)(?:(-|..|...)(-?+\d++))?+\z/)
25
+ raise "invalid page specification: [#{range}]" unless md
26
+
27
+ p1 = md[1].to_i
28
+ p2 = (md[3] || p1).to_i
29
+ case md[2]
30
+ when "..." then p1...p2
31
+ else p1..p2
32
+ end
33
+ end
34
+ @files << [file, pages]
35
+ end
36
+ end
37
+ end
38
+
39
+ def exec
40
+ if (@options[:help] || @files.empty?)
41
+ put_help
42
+ return
43
+ end
44
+
45
+ clevs = [:NO_COMPRESSION, :BEST_SPEED, :BEST_COMPRESSION, :DEFAULT_COMPRESSION].inject({}) do |hash,name|
46
+ hash[Zlib.const_get(name)] = name
47
+ hash
48
+ end
49
+ clev = @options[:compression_level]
50
+ clev = Zlib::DEFAULT_COMPRESSION unless (0..9).include?(clev)
51
+ STDERR.puts("#compression level : #{clevs[clev] || clev}")
52
+
53
+ optlev = @options[:optimization_level]
54
+ STDERR.puts("#optimization flag : #{optlev != 0}")
55
+
56
+ outfile = @options[:outfile] || find_nextfile(@files[0][0])
57
+ STDERR.puts("#output file name : #{outfile}")
58
+
59
+ pdf2 = PDF::File.create(outfile)
60
+ head = nil
61
+
62
+ @files.each do |file,pages|
63
+ pdf = PDF::File.open(file)
64
+ unless head
65
+ head = pdf.get_object(0)
66
+ head.move_to(pdf2, 0).write
67
+ end
68
+
69
+ n = pdf.pages.size
70
+ pages.each do |range|
71
+ first = range.first % n
72
+ last = range.last % n
73
+ inc = (first <= last ? 1 : -1)
74
+ last -= inc if range.exclude_end?
75
+
76
+ first.step(last, inc) do |i|
77
+ STDERR.puts("processing #{file}[#{i}]")
78
+ image = pdf.get_dclimage(i)
79
+ image.set_deflevel(clev)
80
+ image.parse if (optlev > 0)
81
+ image.move_to(pdf2, nil)
82
+ image.write
83
+ end
84
+ end
85
+ pdf.close
86
+ end
87
+
88
+ pdf2.write_info
89
+ pdf2.write_xref
90
+ pdf2.close
91
+ end
92
+
93
+ def find_nextfile(basefile)
94
+ pos = basefile.rindex(".pdf")
95
+ file = nil
96
+ (1..99).each do |i|
97
+ file = basefile.dup.insert(pos, "_#{i}")
98
+ return file unless File.exist?(file)
99
+ end
100
+ raise "failed to create a new filename. (last candidate: #{file})"
101
+ end
102
+
103
+ def put_help
104
+ STDERR.puts(<<-EOS)
105
+ usage: ruby #{File.basename(__FILE__)} [-C[n]] [-O[n]] [-o output.pdf] input.pdf...
106
+
107
+ options
108
+ -C[n] : compression level
109
+ 0 : NO_COMPRESSION
110
+ 1 - 9 : BEST_SPEED - BEST_COMPRESSION
111
+ others : DEFAULT_COMPRESSION (default)
112
+ -O[n] : optimization flag
113
+ 0 : off
114
+ others : on (default)
115
+ -o output.pdf : output filename
116
+ If not specified, "input_%d.pdf" is used instead.
117
+
118
+ input files
119
+ Filenames can have page specifications.
120
+ example: input.pdf[0,5...2,8..-1] #=> [0,5,4,3,8,9,...,n-1]
121
+ EOS
122
+ end
123
+ end; end
@@ -0,0 +1,93 @@
1
+ class DPTM6::Path
2
+ class Node
3
+ attr_reader :x, :y, :op
4
+ attr_accessor :prev, :next
5
+
6
+ def initialize(x, y, op)
7
+ @x = x.to_f
8
+ @y = y.to_f
9
+ @op = op.to_sym
10
+ end
11
+
12
+ def connect(next_node)
13
+ self.next = next_node
14
+ next_node.prev = self
15
+ self
16
+ end
17
+
18
+ def reconnect(opp_node)
19
+ # assume that self.x == opp_node.x && self.y == opp_node.y
20
+ if (self.next.x == opp_node.prev.x && self.next.y == opp_node.prev.y)
21
+ opp_node.prev.connect(self.next)
22
+ self.connect(opp_node)
23
+ self
24
+ else
25
+ nil
26
+ end
27
+ end
28
+
29
+ def output(op = @op)
30
+ @op = nil
31
+ "%g %g %s " % [@x, @y, op]
32
+ end
33
+ end
34
+
35
+ def initialize(nodes = [])
36
+ @nodes = nodes
37
+ end
38
+
39
+ def add(buf_node)
40
+ (buf_node << buf_node[0]).each_cons(2) { |a,b| @nodes << a.connect(b) } if (buf_node.length >= 2)
41
+ end
42
+
43
+ def optimize
44
+ return self unless @nodes
45
+
46
+ # reconnect nodes (total number of nodes is not changed)
47
+ @nodes.group_by(&:y).each do |y,nodes|
48
+ nodes.group_by(&:x).each do |x,buf|
49
+ while (n1 = buf.pop)
50
+ buf.each { |n2| break if n1.reconnect(n2) }
51
+ end
52
+ end
53
+ end
54
+
55
+ # remove needless nodes
56
+ @nodes.reject! do |node|
57
+ d1x = node.prev.x - node.x
58
+ d1y = node.prev.y - node.y
59
+ d2x = node.next.x - node.x
60
+ d2y = node.next.y - node.y
61
+ if (d1x * d2y == d2x * d1y)
62
+ node.prev.connect(node.next)
63
+ true
64
+ else
65
+ false
66
+ end
67
+ end
68
+
69
+ self
70
+ end
71
+
72
+ def output_fill(buf = '')
73
+ return buf if (@nodes.length < 2)
74
+ buf << "\n" unless (buf[-1] == "\n")
75
+ nodes = @nodes.dup
76
+ while (node0 = node = nodes.shift)
77
+ next unless node.op
78
+ buf << node.output(:m)
79
+ until ((node = node.next) == node0)
80
+ buf << node.output(:l)
81
+ end
82
+ buf << "h\n"
83
+ end
84
+ buf << "B\n"
85
+ end
86
+
87
+ def output_stroke(buf = '', close = false)
88
+ return buf if (@nodes.length < 1)
89
+ @nodes.each { |node| buf << node.output }
90
+ buf << "h " if close
91
+ buf << "S\n"
92
+ end
93
+ end
@@ -0,0 +1,389 @@
1
+ require 'zlib'
2
+ require 'dptm6/path'
3
+
4
+ module DPTM6; module PDF
5
+ REGEXP_DATA = /\A(?:(?'dict'<<(?>\g'dict'|[^<>]++)*+>>\s*+)|(?'num'\s*\d+\s*+))/m
6
+
7
+ class File
8
+ attr_reader :fp, :trailer
9
+ attr_accessor :xref, :pages
10
+ private_class_method :new
11
+
12
+ @@obj0_pages = nil
13
+ @@obj0_info = nil
14
+ @@obj0_root = nil
15
+
16
+ def self.open(file)
17
+ new(file, "rb")
18
+ end
19
+
20
+ def self.create(file)
21
+ new(file, "wb")
22
+ end
23
+
24
+ def initialize(file, mode)
25
+ @fp = Kernel.open(file, mode)
26
+
27
+ if (mode[0] == "r")
28
+ read_xref
29
+ read_info
30
+ else
31
+ @xref = []
32
+ @pages = []
33
+ end
34
+ end
35
+
36
+ def close
37
+ @fp.close
38
+ end
39
+
40
+ def get_dclimage(num)
41
+ PDF::DCLImage.new(self, num)
42
+ end
43
+
44
+ def get_object(num, type = PDF::Object)
45
+ type.new(self, num)
46
+ end
47
+
48
+ def get_object_by_key(dict, key, type = PDF::Object)
49
+ ary = dict.scan(/#{key}\s++(\d++)\s++\d++\s++R/m)
50
+ return nil if ary.empty?
51
+
52
+ num = ary[0][0].to_i
53
+ get_object(num, type)
54
+ end
55
+
56
+ def write_info
57
+ str = @pages.collect { |i| "#{i} 0 R " }.join
58
+ @@obj0_pages.data[/\/Kids\s++\[\s++([\d\sR]++)\]/,1] = str
59
+ @@obj0_pages.data[/\/Count\s++(\d++)/,1] = @pages.size.to_s
60
+ @@obj0_pages.move_to(self, 1).write
61
+
62
+ @@obj0_info.move_to(self, @xref.size).write
63
+
64
+ @@obj0_root.move_to(self, @xref.size).write
65
+ end
66
+
67
+ def write_xref
68
+ pos = @fp.pos
69
+ size = @xref.size
70
+
71
+ @fp.write("xref\n")
72
+ @fp.write("#{0} #{size}\n")
73
+ @xref.sort_by(&:last).each do |i,j,k|
74
+ @fp.write("%010d %05d %c \n" % (k == 0 ? [i, 65535, "f"] : [i, 0, "n"]))
75
+ end
76
+
77
+ @fp.write(<<-EOS)
78
+ trailer
79
+ << /Size #{size}
80
+ /Root #{size-1} 0 R
81
+ /Info #{size-2} 0 R
82
+ >>
83
+ EOS
84
+
85
+ @fp.write(<<-EOS)
86
+ startxref
87
+ #{pos}
88
+ %%EOF
89
+ EOS
90
+ end
91
+
92
+ private
93
+
94
+ def read_xref
95
+ @fp.pos = @fp.size - 40
96
+ str = @fp.read
97
+ pos_xref = str[/startxref\s++(\d++)/m,1].to_i
98
+
99
+ # get beginning positions of objects
100
+ @fp.pos = pos_xref
101
+ str = @fp.gets.chomp!
102
+ if (str != "xref")
103
+ raise "invalid data (expected 'xref')"
104
+ end
105
+ a, b, = @fp.gets.split.collect(&:to_i)
106
+ pos_obj = b.times.collect { @fp.gets.split[0].to_i }
107
+
108
+ # get ending positions of objects
109
+ tmp = pos_obj.each_with_index.sort_by(&:first) << [pos_xref, b]
110
+ tmp = tmp.each_cons(2).collect { |a,b| [a[0], b[0] - a[0], a[1]] }
111
+ @xref = tmp.sort_by(&:last)
112
+
113
+ @fp.gets
114
+ @trailer = @fp.read.slice(PDF::REGEXP_DATA)
115
+ end
116
+
117
+ def read_info
118
+ @obj_root = get_object_by_key(@trailer, '/Root')
119
+ @obj_info = get_object_by_key(@trailer, '/Info')
120
+ @obj_pages = get_object_by_key(@obj_root.data, '/Pages')
121
+ @@obj0_root ||= @obj_root
122
+ @@obj0_info ||= @obj_info
123
+ @@obj0_pages ||= @obj_pages
124
+
125
+ str = @obj_pages.data[/\/Kids\s++\[\s++([\d\sR]++)\]/m,1] #=> (\d+ 0 R )*
126
+ @pages = str.scan(/(\d++)\s++(\d++)\s++(R)/).collect { |a| a[0].to_i }
127
+ end
128
+ end
129
+
130
+ class Object
131
+ attr_reader :pdf, :num
132
+ attr_accessor :data
133
+
134
+ def initialize(pdf, num)
135
+ @pdf = pdf
136
+ @num = num
137
+ read
138
+ end
139
+
140
+ def to_string
141
+ return @data if (@num == 0 && @data =~ /%PDF/)
142
+
143
+ "#{@num} #{0} obj\n" << @data << "endobj\n"
144
+ end
145
+
146
+ def move_to(pdf, num)
147
+ @pdf = pdf
148
+ @num = num
149
+ self
150
+ end
151
+
152
+ def write
153
+ fp = @pdf.fp
154
+ pos = fp.pos
155
+ fp.write(to_string)
156
+ len = fp.pos - pos
157
+ @pdf.xref << [pos, len, @num]
158
+ end
159
+
160
+ def replace_pagenum(hash)
161
+ hash.each do |key,num|
162
+ @data[/#{key}\s++(\d++)\s++\d++\s++R/,1] = num.to_s
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def read
169
+ fp = @pdf.fp
170
+ pos, len, = @pdf.xref[@num]
171
+
172
+ fp.pos = pos
173
+ buf = fp.read(len)
174
+
175
+ if (buf =~ /\A%PDF/)
176
+ @data = buf
177
+ return
178
+ end
179
+
180
+ buf.slice!(/\A[^\n]++\n/m)
181
+ @data = buf.slice!(PDF::REGEXP_DATA)
182
+ end
183
+ end
184
+
185
+ class PageObject < Object
186
+ def to_string
187
+ replace_pagenum('/Contents' => @obj_content.num,
188
+ '/Resources' => @obj_resource.num)
189
+ super
190
+ end
191
+
192
+ def write
193
+ @pdf.pages << @num
194
+ super
195
+ end
196
+
197
+ private
198
+
199
+ def read
200
+ super
201
+ @obj_content = @pdf.get_object_by_key(@data, '/Contents' , PDF::StreamObject )
202
+ @obj_resource = @pdf.get_object_by_key(@data, '/Resources', PDF::ResourceObject)
203
+ end
204
+ end
205
+
206
+ class ResourceObject < Object
207
+ attr_reader :x_objects
208
+
209
+ def to_string
210
+ unless @x_objects.empty?
211
+ ary = @x_objects.collect { |key,obj| "#{key} #{obj.num} 0 R" }
212
+ @data[/\/XObject\s++(<<[^<>]*+>>)/,1] = "<< #{ary.join(' ')} >>"
213
+ end
214
+ super
215
+ end
216
+
217
+ private
218
+
219
+ def read
220
+ super
221
+ # /XObject << /x5 5 0 R /x6 6 0 R >>
222
+ @x_objects = {}
223
+ if (@data =~ /\/XObject\s++<<([^<>]++)>>/m)
224
+ $~[1].scan(/(\/\w++)\s++(\d++)\s++(\d++)\s++(R)/) do |a|
225
+ @x_objects[a[0]] = @pdf.get_object(a[1].to_i, PDF::StreamObject)
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ class StreamObject < Object
232
+ attr_reader :obj_length
233
+
234
+ def set_deflevel(level)
235
+ @def_level = level if @stream
236
+ end
237
+
238
+ def stream
239
+ Zlib.inflate(@stream)
240
+ end
241
+
242
+ def stream=(str)
243
+ @stream = Zlib.deflate(str, @def_level)
244
+ end
245
+
246
+ def to_string
247
+ replace_pagenum('/Length' => @obj_length.num)
248
+
249
+ buf = "#{@num} #{0} obj\n"
250
+ buf << @data
251
+ if @stream
252
+ @obj_length.data = " #{@stream.length}\n"
253
+ buf << "stream\n" << @stream << "\nendstream\n"
254
+ end
255
+ buf << "endobj\n"
256
+ end
257
+
258
+ def write
259
+ super
260
+ @obj_length.write
261
+ end
262
+
263
+ private
264
+
265
+ def read
266
+ fp = @pdf.fp
267
+ pos, len, = @pdf.xref[@num]
268
+
269
+ fp.pos = pos
270
+ buf = fp.read(len)
271
+
272
+ buf.slice!(/\A[^\n]++\n/m)
273
+ @data = buf.slice!(PDF::REGEXP_DATA)
274
+
275
+ @obj_length = @pdf.get_object_by_key(@data, '/Length')
276
+ length = @obj_length.data.to_i
277
+
278
+ if @data.include?("/FlateDecode")
279
+ buf.slice!(/\A[^\n]++\n/m) #=> "stream\n"
280
+ @stream = buf[0,length]
281
+ @def_level = Zlib::DEFAULT_COMPRESSION
282
+ end
283
+ end
284
+ end
285
+
286
+ class DCLImage < PageObject
287
+ def initialize(pdf, num)
288
+ super(pdf, pdf.pages[num])
289
+ end
290
+
291
+ def move_to(pdf, num)
292
+ # OBJECT / NUMBER
293
+ # psimage +1
294
+ # length +2
295
+ # resource +0
296
+ # page +3+nx
297
+ # xobj_i +3+i
298
+ # length +3+nx+1+i
299
+ m = pdf.xref.size + 1
300
+ nx = @obj_resource.x_objects.size
301
+ @obj_content. move_to(pdf, m + 1)
302
+ @obj_content.obj_length.move_to(pdf, m + 2)
303
+ @obj_resource. move_to(pdf, m )
304
+ super(pdf, m + 3 + nx)
305
+ @obj_resource.x_objects.each_with_index do |(key,obj),i|
306
+ obj. move_to(pdf, m + 3 + i)
307
+ obj.obj_length.move_to(pdf, m + 4 + nx + i)
308
+ end
309
+ self
310
+ end
311
+
312
+ def set_deflevel(level)
313
+ @obj_content.set_deflevel(level)
314
+ end
315
+
316
+ def write
317
+ @obj_content. write
318
+ @obj_resource.write
319
+ super
320
+ @obj_resource.x_objects.each_value(&:write)
321
+ end
322
+
323
+ REGEXP_PARSE = Regexp.compile('(?:(\[[^\[\]]*+\])\s++)?+' << # array
324
+ '(?:(-?+\d++(?:\.\d++)?+)\s++)?+' * 6 << # numbers
325
+ '(?:(/\w++)\s++)?+' << # set ExtGState parameters ("'/a0 'gs ")
326
+ '(\w++)\s++' << # operator (required)
327
+ '((?:/\w++\s++\w++\s++){2})?+' ) # draw XObject ("... cm '/a0 gs /x5 Do '")
328
+ def parse
329
+ stream_new = "q\n"
330
+
331
+ # previous values
332
+ concat = nil
333
+ color = { :rg => nil, :RG => nil }
334
+ flag_x = false
335
+
336
+ buf_node = []
337
+ path = Path.new
338
+ str_check = @obj_content.stream.gsub(REGEXP_PARSE) do |a|
339
+ md = $~
340
+ op = md[-2].to_sym
341
+ case op
342
+ when :m, :l # moveto, lineto
343
+ buf_node << Path::Node.new(md[2], md[3], op)
344
+ when :h, :B # close, fill and stroke
345
+ if flag_x # frame of XObject
346
+ flag_x = false
347
+ Path.new(buf_node).output_stroke(stream_new, true)
348
+ path = Path.new
349
+ else
350
+ path.add(buf_node)
351
+ end
352
+ buf_node = []
353
+ when :S # stroke
354
+ path.optimize.output_fill(stream_new)
355
+ path = Path.new
356
+ Path.new(buf_node).output_stroke(stream_new)
357
+ buf_node = []
358
+ when :rg, :RG # set RGB for stroking / non-stroking
359
+ rgbstr = md[2..4].join(' ')
360
+ if (rgbstr == color[op])
361
+ else
362
+ path.optimize.output_fill(stream_new)
363
+ path = Path.new
364
+ color[op] = rgbstr
365
+ stream_new << a
366
+ end
367
+ when :cm # concat matrix
368
+ if md[-1] # insert XObject
369
+ flag_x = true
370
+ stream_new << "q\n"
371
+ stream_new << "0 1 1 0 -842 0 cm\n" if concat # cancel concat matrix for paths
372
+ stream_new << a << "Q\n"
373
+ else
374
+ stream_new << "q " << (concat = a) unless concat
375
+ end
376
+ when :q, :Q # gsave, grestore
377
+ else # others : copy
378
+ stream_new << a
379
+ end
380
+ '' # delete parsed string
381
+ end
382
+ STDERR.puts("WARNING: Some script could not be parsed -- #{str_check.inspect}") if (str_check != '')
383
+
384
+ stream_new << "Q\n"
385
+ @obj_content.stream = stream_new
386
+ self
387
+ end
388
+ end
389
+ end; end
@@ -0,0 +1,3 @@
1
+ module DPTM6
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dptm6
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Masahiro Nomoto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-11-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Merge tones in PDF made by DCL (Dennou Club Library) ver. 6
56
+ email:
57
+ - hmmnrst@users.noreply.github.com
58
+ executables:
59
+ - dptm6
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - ".travis.yml"
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - dptm6.gemspec
73
+ - exe/dptm6
74
+ - lib/dptm6.rb
75
+ - lib/dptm6/cli.rb
76
+ - lib/dptm6/path.rb
77
+ - lib/dptm6/pdf.rb
78
+ - lib/dptm6/version.rb
79
+ homepage: https://github.com/hmmnrst/dptm6
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.7.6
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: dclpdftonemerge for DCL 6
103
+ test_files: []