ruby-psd 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/ruby-psd.rb +249 -0
  2. metadata +8 -7
  3. data/lib/main.rb +0 -0
@@ -0,0 +1,249 @@
1
+ class RubyPSD
2
+
3
+ # Official documents of PSD File Format is here.
4
+ # http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_20023
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ @width = 0
9
+ @height = 0
10
+ @layers = []
11
+ end
12
+ attr_accessor :width, :height, :layers
13
+
14
+ def get_file_header
15
+ [
16
+ [0x38, 0x42, 0x50, 0x53], # Signature[4] : 8BPS
17
+ [0x00, 0x01], #Version[2] : always it's must be 1
18
+ [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], #Reserved[6]
19
+ [0x00, 0x04], # The number of channels in the image, Usually it's RGBA so it will be 4
20
+ num2bytes(@height, 4), # Height[4]
21
+ num2bytes(@width, 4), # Width[4]
22
+ [0x00, 0x08], #Depth[2]: the number of bits per channel. Supported values are 1, 8, 16 and 32. opted 8bit as default.
23
+ [0x00, 0x03] #ColorMode: opted RGB Mode(3) as default.
24
+ ].flatten.pack("C*")
25
+ end
26
+
27
+ def get_color_mode_data
28
+ # I'm not sure about this specs so I makes it as only 4 byte length of zero
29
+ [0x00, 0x00, 0x00, 0x00].pack("C*")
30
+ end
31
+
32
+ def get_image_resources
33
+ # I'm not sure about this specs so I makes it as only 4 byte length of zero
34
+ [0x00, 0x00, 0x00, 0x00].pack("C*")
35
+ end
36
+
37
+ def get_layer_and_mask_information
38
+
39
+ layer_info = get_layer_info
40
+ global_layer_info = get_global_layer_info
41
+ additional_layer_info = get_additional_layer_info
42
+ size_of_layer_and_mask_information = layer_info.size + global_layer_info.size + additional_layer_info.size
43
+
44
+ [
45
+ num2bytes(size_of_layer_and_mask_information, 4),
46
+ layer_info,
47
+ global_layer_info,
48
+ additional_layer_info
49
+ ].flatten.pack("C*")
50
+
51
+ end
52
+
53
+ def get_layer_info
54
+
55
+ _LAYER_COUNT_LENGTH = 2
56
+ layer_counts = get_layer_counts
57
+ layer_records = get_layer_records
58
+ channel_image_data = get_channel_image_data(layer_counts)
59
+
60
+ size_of_layer_info = _LAYER_COUNT_LENGTH + layer_records.size + channel_image_data.size
61
+
62
+ [
63
+ num2bytes(size_of_layer_info, 4),
64
+ num2bytes(layer_counts, 2),
65
+ layer_records,
66
+ channel_image_data
67
+ ].flatten
68
+
69
+ end
70
+
71
+ def get_layer_counts
72
+ # 1 is the count of default art layer named "background"
73
+ 1 + (get_key_nums @layers) * 2
74
+ end
75
+
76
+ def get_key_nums(layers)
77
+ ret = layers.size
78
+ layers.each do |v|
79
+ if v.class == Array
80
+ ret += get_key_nums(v) - 1
81
+ end
82
+ end
83
+ ret
84
+ end
85
+
86
+ def get_layer_records
87
+ art_layer_record = get_art_layer_record
88
+ layers_array = convert_layers_to_array(@layers)
89
+ group_layer_records = layers_array.map do |name|
90
+ get_group_layer_record name
91
+ end
92
+ [art_layer_record, group_layer_records].flatten
93
+ end
94
+
95
+ def get_art_layer_record
96
+ get_layer_record "background", []
97
+ end
98
+
99
+ def convert_layers_to_array(arr)
100
+ def pick_up_from_array(ar, prev = nil)
101
+ ret = []
102
+ ar.each_with_index do |a, i|
103
+ if a.class == Array
104
+ ret += pick_up_from_array(a)
105
+ if prev != nil and ar[i+1].class != Array
106
+ ret << "</Layer group>"
107
+ prev = nil
108
+ end
109
+ else
110
+ if prev != nil
111
+ ret << "</Layer group>"
112
+ end
113
+ ret << a
114
+ prev = a
115
+ end
116
+ end
117
+ ret << "</Layer group>" if prev != nil
118
+ ret
119
+ end
120
+ (pick_up_from_array arr).flatten.reverse
121
+ end
122
+
123
+ def get_group_layer_record(name)
124
+ if name == "</Layer group>"
125
+ additional_data = [
126
+ [0x38, 0x42, 0x49, 0x4D], # Blend mode signature: '8BIM'
127
+ [0x6c, 0x73, 0x63, 0x74], # Section divider setting 'lscr'
128
+ [0x00, 0x00, 0x00, 0x04], # Length of this additional record
129
+ [0x00, 0x00, 0x00, 0x03] # Type. This is 3 = bounding section divider.
130
+ ]
131
+ else
132
+ additional_data = [
133
+ [0x38, 0x42, 0x49, 0x4D], # Blend mode signature: '8BIM'
134
+ [0x6c, 0x73, 0x63, 0x74], # Section divider setting 'lscr'
135
+ [0x00, 0x00, 0x00, 0x0c], # Length of this additional record. It might be 12.
136
+ [0x00, 0x00, 0x00, 0x01], # Type. This is 1 = bounding section divider.
137
+ # Following is only present if length = 12
138
+ [0x38, 0x42, 0x49, 0x4D], # Blend mode signature: '8BIM'
139
+ [0x70, 0x61, 0x73, 0x73], # Blend mode key: 'pass'
140
+ ]
141
+ end
142
+ get_layer_record name, additional_data.flatten
143
+ end
144
+
145
+ def get_layer_record (name, additional_data)
146
+
147
+ pascal_name = get_pascal_name(name, 4)
148
+ size_of_additional_data = 8 + pascal_name.size + additional_data.size
149
+ [
150
+ [0x00] * 16, #Rectangle containing the contents of the layer
151
+ [0x00, 0x04], #Number of channels in the layer ( Usually it will be 4 as RGBA )
152
+ # Channel information. Six bytes per channel, consisting of: 2 bytes for Channel ID
153
+ # 4 bytes for length of corresponding channel data
154
+ [0xFF, 0xFF] + [0x00, 0x00, 0x00, 0x02], #-1 = transparency
155
+ [0x00, 0x00] + [0x00, 0x00, 0x00, 0x02], #0 = red
156
+ [0x00, 0x01] + [0x00, 0x00, 0x00, 0x02], #1 = green
157
+ [0x00, 0x02] + [0x00, 0x00, 0x00, 0x02], #2 = blue
158
+ [0x38, 0x42, 0x49, 0x4D], # Blend mode signature: '8BIM'
159
+ [0x6E, 0x6F, 0x72, 0x6D], # Blend mode key: 'norm'
160
+ [0xFF], #Opacity. 0 = transparent ... 255 = opaque
161
+ [0x00], #Clipping: 0 = base, 1 = non-base
162
+ [0x08], #Flags: [00001000]
163
+ # bit 0 = transparency protected;
164
+ # bit 1 = visible;
165
+ # bit 2 = obsolete;
166
+ # bit 3 = 1 for Photoshop 5.0 and later, tells if bit 4 has useful information;
167
+ # bit 4 = pixel data irrelevant to appearance of document
168
+ [0x00], # Just Filler (zero)
169
+ num2bytes(size_of_additional_data, 4),
170
+ [0x00] * 4, # Layer mask data. It won't be.
171
+ [0x00] * 4, # Layer blending ranges. It won't be.
172
+ pascal_name,
173
+ additional_data
174
+ ].flatten
175
+
176
+ end
177
+
178
+ def get_pascal_name(name, padding)
179
+ arr = name.unpack("C*")
180
+ arr = [arr.size] + arr
181
+ if ((arr.size % padding) > 0)
182
+ arr += ([0] * (padding - (arr.size % padding)))
183
+ end
184
+ arr
185
+ end
186
+
187
+ def get_channel_image_data(layer_count)
188
+ [0x00] * 8 * layer_count
189
+ end
190
+
191
+ def get_global_layer_info
192
+ [0x00] * 4
193
+ end
194
+
195
+ def get_additional_layer_info
196
+ []
197
+ end
198
+
199
+ def get_image_data
200
+ image_data = generate_image_data
201
+ [
202
+ [0x00, 0x01],
203
+ image_data # Compression method: 1 = RLE compressed the image data starts with the byte
204
+ ].flatten.pack("C*")
205
+ end
206
+
207
+ def generate_image_data
208
+
209
+ packet = get_packet_bytes(@width)
210
+ channels = 4
211
+ [
212
+ [0x00, 0x00] * @height * channels,
213
+ packet * @height * channels
214
+ ]
215
+
216
+ end
217
+
218
+ def get_packet_bytes(width)
219
+ divided = width / 128
220
+ remainder = width % 128
221
+ ret = []
222
+ [divided, remainder]
223
+ divided.times do
224
+ ret << [0x101 - 128, 0xFF]
225
+ end
226
+ ret << [0x101 - remainder, 0xFF]
227
+ ret.flatten
228
+ end
229
+
230
+ def generate
231
+ psd = open @path, "w"
232
+ psd.print get_file_header
233
+ psd.print get_color_mode_data
234
+ psd.print get_image_resources
235
+ psd.print get_layer_and_mask_information
236
+ psd.print get_image_data
237
+ psd.close
238
+ end
239
+
240
+ def output_psd
241
+ end
242
+
243
+ def num2bytes (num, size)
244
+ num.to_s(16).rjust(size*2,"0").unpack("a2"*(size)).map{|a| "0x#{a}".to_i(16)}
245
+ end
246
+
247
+ end
248
+
249
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-psd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,16 +9,17 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-27 00:00:00.000000000 Z
12
+ date: 2013-02-28 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: ! 'This gem is made for generating and accessing psd file from ruby. '
15
- email: shunter1112@gmail.com
14
+ description: Generating psd skeleton file simple and by scriping of Ruby
15
+ email:
16
+ - shunter1112@gmail.com
16
17
  executables: []
17
18
  extensions: []
18
19
  extra_rdoc_files: []
19
20
  files:
20
- - lib/main.rb
21
- homepage: http://rubygems.org/gems/ruby-psd
21
+ - lib/ruby-psd.rb
22
+ homepage: http://github.com/shunter1112/ruby-psd
22
23
  licenses: []
23
24
  post_install_message:
24
25
  rdoc_options: []
@@ -41,5 +42,5 @@ rubyforge_project:
41
42
  rubygems_version: 1.8.25
42
43
  signing_key:
43
44
  specification_version: 3
44
- summary: Accessing and generating psd files.
45
+ summary: Generating psd skeleton file simple and by scriping of Ruby
45
46
  test_files: []
File without changes