dicom 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,356 +1,356 @@
1
- module DICOM
2
-
3
- class Parent
4
-
5
- private
6
-
7
-
8
- # Adds a binary string to (the end of) either the instance file or string.
9
- #
10
- # @param [String] string a pre-encoded string
11
- #
12
- def add_encoded(string)
13
- if @file
14
- @stream.write(string)
15
- else
16
- # Are we writing to a single (big) string, or multiple (smaller) strings?
17
- unless @segments
18
- @stream.add_last(string)
19
- else
20
- add_with_segmentation(string)
21
- end
22
- end
23
- end
24
-
25
- # Adds an encoded string to the output stream, while keeping track of the
26
- # accumulated size of the output stream, splitting it up as necessary, and
27
- # transferring the encoded string fragments to an array.
28
- #
29
- # @param [String] string a pre-encoded string
30
- #
31
- def add_with_segmentation(string)
32
- # As the encoded DICOM string will be cut in multiple, smaller pieces, we need to monitor the length of our encoded strings:
33
- if (string.length + @stream.length) > @max_size
34
- split_and_add(string)
35
- elsif (30 + @stream.length) > @max_size
36
- # End the current segment, and start on a new segment for this string.
37
- @segments << @stream.export
38
- @stream.add_last(string)
39
- else
40
- # We are nowhere near the limit, simply add the string:
41
- @stream.add_last(string)
42
- end
43
- end
44
-
45
- # Toggles the status for enclosed pixel data.
46
- #
47
- # @param [Element, Item, Sequence] element a data element
48
- #
49
- def check_encapsulated_image(element)
50
- # If DICOM object contains encapsulated pixel data, we need some special handling for its items:
51
- if element.tag == PIXEL_TAG and element.parent.is_a?(DObject)
52
- @enc_image = true if element.length <= 0
53
- end
54
- end
55
-
56
- # Writes DICOM content to a series of size-limited binary strings, which is returned in an array.
57
- # This is typically used in preparation of transmitting DICOM objects through network connections.
58
- #
59
- # @param [Integer] max_size the maximum segment string length
60
- # @param [Hash] options the options to use for encoding the DICOM strings
61
- # @option options [String] :syntax the transfer syntax used for the encoding settings of the post-meta part of the DICOM string
62
- # @return [Array<String>] the encoded DICOM strings
63
- #
64
- def encode_in_segments(max_size, options={})
65
- @max_size = max_size
66
- @transfer_syntax = options[:syntax]
67
- # Until a DICOM write has completed successfully the status is 'unsuccessful':
68
- @write_success = false
69
- # Default explicitness of start of DICOM file:
70
- @explicit = true
71
- # Default endianness of start of DICOM files (little endian):
72
- @str_endian = false
73
- # When the file switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
74
- @switched = false
75
- # Items contained under the Pixel Data element needs some special attention to write correctly:
76
- @enc_image = false
77
- # Create a Stream instance to handle the encoding of content to a binary string:
78
- @stream = Stream.new(nil, @str_endian)
79
- @segments = Array.new
80
- write_data_elements(children)
81
- # Extract the remaining string in our stream instance to our array of strings:
82
- @segments << @stream.export
83
- # Mark this write session as successful:
84
- @write_success = true
85
- return @segments
86
- end
87
-
88
- # Tests if the path/file is writable, creates any folders if necessary, and opens the file for writing.
89
- #
90
- # @param [String] file a path/file string
91
- #
92
- def open_file(file)
93
- # Check if file already exists:
94
- if File.exist?(file)
95
- # Is it writable?
96
- if File.writable?(file)
97
- @file = File.new(file, "wb")
98
- else
99
- # Existing file is not writable:
100
- logger.error("The program does not have permission or resources to create this file: #{file}")
101
- end
102
- else
103
- # File does not exist.
104
- # Check if this file's path contains a folder that does not exist, and therefore needs to be created:
105
- folders = file.split(File::SEPARATOR)
106
- if folders.length > 1
107
- # Remove last element (which should be the file string):
108
- folders.pop
109
- path = folders.join(File::SEPARATOR)
110
- # Check if this path exists:
111
- unless File.directory?(path)
112
- # We need to create (parts of) this path:
113
- require 'fileutils'
114
- FileUtils.mkdir_p(path)
115
- end
116
- end
117
- # The path to this non-existing file is verified, and we can proceed to create the file:
118
- @file = File.new(file, "wb")
119
- end
120
- end
121
-
122
- # Splits a pre-encoded string in parts and adds it to the segments instance
123
- # array.
124
- #
125
- # @param [String] string a pre-encoded string
126
- #
127
- def split_and_add(string)
128
- # Duplicate the string as not to ruin the binary of the data element with our slicing:
129
- segment = string.dup
130
- append = segment.slice!(0, @max_size-@stream.length)
131
- # Clear out the stream along with a small part of the string:
132
- @segments << @stream.export + append
133
- if (30 + segment.length) > @max_size
134
- # The remaining part of the string is bigger than the max limit, fill up more segments:
135
- # How many full segments will this string fill?
136
- number = (segment.length/@max_size.to_f).floor
137
- start_index = 0
138
- number.times {
139
- @segments << segment.slice(start_index, @max_size)
140
- start_index += @max_size
141
- }
142
- # The remaining part is added to the stream:
143
- @stream.add_last(segment.slice(start_index, segment.length - start_index))
144
- else
145
- # The rest of the string is small enough that it can be added to the stream:
146
- @stream.add_last(segment)
147
- end
148
- end
149
-
150
- # Encodes and writes a single data element.
151
- #
152
- # @param [Element, Item, Sequence] element a data element
153
- #
154
- def write_data_element(element)
155
- # Step 1: Write tag:
156
- write_tag(element.tag)
157
- # Step 2: Write [VR] and value length:
158
- write_vr_length(element.tag, element.vr, element.length)
159
- # Step 3: Write value (Insert the already encoded binary string):
160
- write_value(element.bin)
161
- check_encapsulated_image(element)
162
- end
163
-
164
- # Iterates through the data elements, encoding/writing one by one.
165
- # If an element has children, this method is repeated recursively.
166
- #
167
- # @note Group length data elements are NOT written (they are deprecated/retired in the DICOM standard).
168
- #
169
- # @param [Array<Element, Item, Sequence>] elements an array of data elements (sorted by their tags)
170
- #
171
- def write_data_elements(elements)
172
- elements.each do |element|
173
- # If this particular element has children, write these (recursively) before proceeding with elements at the current level:
174
- if element.is_parent?
175
- if element.children?
176
- # Sequence/Item with child elements:
177
- element.reset_length unless @enc_image
178
- write_data_element(element)
179
- write_data_elements(element.children)
180
- if @enc_image
181
- # Write a delimiter for the pixel tag, but not for its items:
182
- write_delimiter(element) if element.tag == PIXEL_TAG
183
- else
184
- write_delimiter(element)
185
- end
186
- else
187
- # Parent is childless:
188
- if element.bin
189
- write_data_element(element) if element.bin.length > 0
190
- elsif @include_empty_parents
191
- # Only write empty/childless parents if specifically indicated:
192
- write_data_element(element)
193
- write_delimiter(element)
194
- end
195
- end
196
- else
197
- # Ordinary Data Element:
198
- if element.tag.group_length?
199
- # Among group length elements, only write the meta group element (the others have been retired in the DICOM standard):
200
- write_data_element(element) if element.tag == "0002,0000"
201
- else
202
- write_data_element(element)
203
- end
204
- end
205
- end
206
- end
207
-
208
- # Encodes and writes an Item or Sequence delimiter.
209
- #
210
- # @param [Item, Sequence] element a parent element
211
- #
212
- def write_delimiter(element)
213
- delimiter_tag = (element.tag == ITEM_TAG ? ITEM_DELIMITER : SEQUENCE_DELIMITER)
214
- write_tag(delimiter_tag)
215
- write_vr_length(delimiter_tag, ITEM_VR, 0)
216
- end
217
-
218
- # Handles the encoding of DICOM information to string as well as writing it to file.
219
- #
220
- # @param [Hash] options the options to use for encoding the DICOM string
221
- # @option options [String] :file_name the path & name of the DICOM file which is to be written to disk
222
- # @option options [Boolean] :signature if true, the 128 byte preamble and 'DICM' signature is prepended to the encoded string
223
- # @option options [String] :syntax the transfer syntax used for the encoding settings of the post-meta part of the DICOM string
224
- #
225
- def write_elements(options={})
226
- # Check if we are able to create given file:
227
- open_file(options[:file_name])
228
- # Go ahead and write if the file was opened successfully:
229
- if @file
230
- # Initiate necessary variables:
231
- @transfer_syntax = options[:syntax]
232
- # Until a DICOM write has completed successfully the status is 'unsuccessful':
233
- @write_success = false
234
- # Default explicitness of start of DICOM file:
235
- @explicit = true
236
- # Default endianness of start of DICOM files (little endian):
237
- @str_endian = false
238
- # When the file switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
239
- @switched = false
240
- # Items contained under the Pixel Data element needs some special attention to write correctly:
241
- @enc_image = false
242
- # Create a Stream instance to handle the encoding of content to a binary string:
243
- @stream = Stream.new(nil, @str_endian)
244
- # Tell the Stream instance which file to write to:
245
- @stream.set_file(@file)
246
- # Write the DICOM signature:
247
- write_signature if options[:signature]
248
- write_data_elements(children)
249
- # As file has been written successfully, it can be closed.
250
- @file.close
251
- # Mark this write session as successful:
252
- @write_success = true
253
- end
254
- end
255
-
256
- # Writes the DICOM header signature (128 bytes + 'DICM').
257
- #
258
- def write_signature
259
- # Write the string "DICM" which along with the empty bytes that
260
- # will be put before it, identifies this as a valid DICOM file:
261
- identifier = @stream.encode("DICM", "STR")
262
- # Fill in 128 empty bytes:
263
- filler = @stream.encode("00"*128, "HEX")
264
- @stream.write(filler)
265
- @stream.write(identifier)
266
- end
267
-
268
- # Encodes and writes a tag (the first part of the data element).
269
- #
270
- # @param [String] tag a data element tag
271
- #
272
- def write_tag(tag)
273
- # Group 0002 is always little endian, but the rest of the file may be little or big endian.
274
- # When we shift from group 0002 to another group we need to update our endian/explicitness variables:
275
- switch_syntax_on_write if tag.group != META_GROUP and @switched == false
276
- # Write to binary string:
277
- bin_tag = @stream.encode_tag(tag)
278
- add_encoded(bin_tag)
279
- end
280
-
281
- # Writes the data element's pre-encoded value.
282
- #
283
- # @param [String] bin the binary string value of this data element
284
- #
285
- def write_value(bin)
286
- # This is pretty straightforward, just dump the binary data to the file/string:
287
- add_encoded(bin) if bin
288
- end
289
-
290
- # Encodes and writes the value representation (if it is to be written) and length value.
291
- # The encoding scheme to be applied here depends on explicitness, data element type and vr.
292
- #
293
- # @param [String] tag the tag of this data element
294
- # @param [String] vr the value representation of this data element
295
- # @param [Integer] length the data element's length
296
- #
297
- def write_vr_length(tag, vr, length)
298
- # Encode the length value (cover both scenarios of 2 and 4 bytes):
299
- length4 = @stream.encode(length, "SL")
300
- length2 = @stream.encode(length, "US")
301
- # Structure will differ, dependent on whether we have explicit or implicit encoding:
302
- # *****EXPLICIT*****:
303
- if @explicit == true
304
- # Step 1: Write VR (if it is to be written)
305
- unless ITEM_TAGS.include?(tag)
306
- # Write data element VR (2 bytes - since we are not dealing with an item related element):
307
- add_encoded(@stream.encode(vr, "STR"))
308
- end
309
- # Step 2: Write length
310
- # Three possible structures for value length here, dependent on data element vr:
311
- case vr
312
- when "OB","OW","OF","SQ","UN","UT"
313
- if @enc_image # (4 bytes)
314
- # Item under an encapsulated Pixel Data (7FE0,0010).
315
- add_encoded(length4)
316
- else # (6 bytes total)
317
- # Two reserved bytes first:
318
- add_encoded(@stream.encode("00"*2, "HEX"))
319
- # Value length (4 bytes):
320
- add_encoded(length4)
321
- end
322
- when ITEM_VR # (4 bytes)
323
- # For the item elements: "FFFE,E000", "FFFE,E00D" and "FFFE,E0DD"
324
- add_encoded(length4)
325
- else # (2 bytes)
326
- # For all the other data element vr, value length is 2 bytes:
327
- add_encoded(length2)
328
- end
329
- else
330
- # *****IMPLICIT*****:
331
- # No VR written.
332
- # Writing value length (4 bytes):
333
- add_encoded(length4)
334
- end
335
- end
336
-
337
- # Changes encoding variables as the file writing proceeds past the initial meta
338
- # group part (0002,xxxx) of the DICOM object.
339
- #
340
- def switch_syntax_on_write
341
- # Process the transfer syntax string to establish encoding settings:
342
- ts = LIBRARY.uid(@transfer_syntax)
343
- logger.warn("Invalid/unknown transfer syntax: #{@transfer_syntax} Will complete encoding the file, but an investigation of the result is recommended.") unless ts && ts.transfer_syntax?
344
- @rest_explicit = ts ? ts.explicit? : true
345
- @rest_endian = ts ? ts.big_endian? : false
346
- # Make sure we only run this method once:
347
- @switched = true
348
- # Update explicitness and endianness (pack/unpack variables):
349
- @explicit = @rest_explicit
350
- @str_endian = @rest_endian
351
- @stream.endian = @rest_endian
352
- end
353
-
354
- end
355
-
1
+ module DICOM
2
+
3
+ class Parent
4
+
5
+ private
6
+
7
+
8
+ # Adds a binary string to (the end of) either the instance file or string.
9
+ #
10
+ # @param [String] string a pre-encoded string
11
+ #
12
+ def add_encoded(string)
13
+ if @file
14
+ @stream.write(string)
15
+ else
16
+ # Are we writing to a single (big) string, or multiple (smaller) strings?
17
+ unless @segments
18
+ @stream.add_last(string)
19
+ else
20
+ add_with_segmentation(string)
21
+ end
22
+ end
23
+ end
24
+
25
+ # Adds an encoded string to the output stream, while keeping track of the
26
+ # accumulated size of the output stream, splitting it up as necessary, and
27
+ # transferring the encoded string fragments to an array.
28
+ #
29
+ # @param [String] string a pre-encoded string
30
+ #
31
+ def add_with_segmentation(string)
32
+ # As the encoded DICOM string will be cut in multiple, smaller pieces, we need to monitor the length of our encoded strings:
33
+ if (string.length + @stream.length) > @max_size
34
+ split_and_add(string)
35
+ elsif (30 + @stream.length) > @max_size
36
+ # End the current segment, and start on a new segment for this string.
37
+ @segments << @stream.export
38
+ @stream.add_last(string)
39
+ else
40
+ # We are nowhere near the limit, simply add the string:
41
+ @stream.add_last(string)
42
+ end
43
+ end
44
+
45
+ # Toggles the status for enclosed pixel data.
46
+ #
47
+ # @param [Element, Item, Sequence] element a data element
48
+ #
49
+ def check_encapsulated_image(element)
50
+ # If DICOM object contains encapsulated pixel data, we need some special handling for its items:
51
+ if element.tag == PIXEL_TAG and element.parent.is_a?(DObject)
52
+ @enc_image = true if element.length <= 0
53
+ end
54
+ end
55
+
56
+ # Writes DICOM content to a series of size-limited binary strings, which is returned in an array.
57
+ # This is typically used in preparation of transmitting DICOM objects through network connections.
58
+ #
59
+ # @param [Integer] max_size the maximum segment string length
60
+ # @param [Hash] options the options to use for encoding the DICOM strings
61
+ # @option options [String] :syntax the transfer syntax used for the encoding settings of the post-meta part of the DICOM string
62
+ # @return [Array<String>] the encoded DICOM strings
63
+ #
64
+ def encode_in_segments(max_size, options={})
65
+ @max_size = max_size
66
+ @transfer_syntax = options[:syntax]
67
+ # Until a DICOM write has completed successfully the status is 'unsuccessful':
68
+ @write_success = false
69
+ # Default explicitness of start of DICOM file:
70
+ @explicit = true
71
+ # Default endianness of start of DICOM files (little endian):
72
+ @str_endian = false
73
+ # When the file switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
74
+ @switched = false
75
+ # Items contained under the Pixel Data element needs some special attention to write correctly:
76
+ @enc_image = false
77
+ # Create a Stream instance to handle the encoding of content to a binary string:
78
+ @stream = Stream.new(nil, @str_endian)
79
+ @segments = Array.new
80
+ write_data_elements(children)
81
+ # Extract the remaining string in our stream instance to our array of strings:
82
+ @segments << @stream.export
83
+ # Mark this write session as successful:
84
+ @write_success = true
85
+ return @segments
86
+ end
87
+
88
+ # Tests if the path/file is writable, creates any folders if necessary, and opens the file for writing.
89
+ #
90
+ # @param [String] file a path/file string
91
+ #
92
+ def open_file(file)
93
+ # Check if file already exists:
94
+ if File.exist?(file)
95
+ # Is it writable?
96
+ if File.writable?(file)
97
+ @file = File.new(file, "wb")
98
+ else
99
+ # Existing file is not writable:
100
+ logger.error("The program does not have permission or resources to create this file: #{file}")
101
+ end
102
+ else
103
+ # File does not exist.
104
+ # Check if this file's path contains a folder that does not exist, and therefore needs to be created:
105
+ folders = file.split(File::SEPARATOR)
106
+ if folders.length > 1
107
+ # Remove last element (which should be the file string):
108
+ folders.pop
109
+ path = folders.join(File::SEPARATOR)
110
+ # Check if this path exists:
111
+ unless File.directory?(path)
112
+ # We need to create (parts of) this path:
113
+ require 'fileutils'
114
+ FileUtils.mkdir_p(path)
115
+ end
116
+ end
117
+ # The path to this non-existing file is verified, and we can proceed to create the file:
118
+ @file = File.new(file, "wb")
119
+ end
120
+ end
121
+
122
+ # Splits a pre-encoded string in parts and adds it to the segments instance
123
+ # array.
124
+ #
125
+ # @param [String] string a pre-encoded string
126
+ #
127
+ def split_and_add(string)
128
+ # Duplicate the string as not to ruin the binary of the data element with our slicing:
129
+ segment = string.dup
130
+ append = segment.slice!(0, @max_size-@stream.length)
131
+ # Clear out the stream along with a small part of the string:
132
+ @segments << @stream.export + append
133
+ if (30 + segment.length) > @max_size
134
+ # The remaining part of the string is bigger than the max limit, fill up more segments:
135
+ # How many full segments will this string fill?
136
+ number = (segment.length/@max_size.to_f).floor
137
+ start_index = 0
138
+ number.times {
139
+ @segments << segment.slice(start_index, @max_size)
140
+ start_index += @max_size
141
+ }
142
+ # The remaining part is added to the stream:
143
+ @stream.add_last(segment.slice(start_index, segment.length - start_index))
144
+ else
145
+ # The rest of the string is small enough that it can be added to the stream:
146
+ @stream.add_last(segment)
147
+ end
148
+ end
149
+
150
+ # Encodes and writes a single data element.
151
+ #
152
+ # @param [Element, Item, Sequence] element a data element
153
+ #
154
+ def write_data_element(element)
155
+ # Step 1: Write tag:
156
+ write_tag(element.tag)
157
+ # Step 2: Write [VR] and value length:
158
+ write_vr_length(element.tag, element.vr, element.length)
159
+ # Step 3: Write value (Insert the already encoded binary string):
160
+ write_value(element.bin)
161
+ check_encapsulated_image(element)
162
+ end
163
+
164
+ # Iterates through the data elements, encoding/writing one by one.
165
+ # If an element has children, this method is repeated recursively.
166
+ #
167
+ # @note Group length data elements are NOT written (they are deprecated/retired in the DICOM standard).
168
+ #
169
+ # @param [Array<Element, Item, Sequence>] elements an array of data elements (sorted by their tags)
170
+ #
171
+ def write_data_elements(elements)
172
+ elements.each do |element|
173
+ # If this particular element has children, write these (recursively) before proceeding with elements at the current level:
174
+ if element.is_parent?
175
+ if element.children?
176
+ # Sequence/Item with child elements:
177
+ element.reset_length unless @enc_image
178
+ write_data_element(element)
179
+ write_data_elements(element.children)
180
+ if @enc_image
181
+ # Write a delimiter for the pixel tag, but not for its items:
182
+ write_delimiter(element) if element.tag == PIXEL_TAG
183
+ else
184
+ write_delimiter(element)
185
+ end
186
+ else
187
+ # Parent is childless:
188
+ if element.bin
189
+ write_data_element(element) if element.bin.length > 0
190
+ elsif @include_empty_parents
191
+ # Only write empty/childless parents if specifically indicated:
192
+ write_data_element(element)
193
+ write_delimiter(element)
194
+ end
195
+ end
196
+ else
197
+ # Ordinary Data Element:
198
+ if element.tag.group_length?
199
+ # Among group length elements, only write the meta group element (the others have been retired in the DICOM standard):
200
+ write_data_element(element) if element.tag == "0002,0000"
201
+ else
202
+ write_data_element(element)
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ # Encodes and writes an Item or Sequence delimiter.
209
+ #
210
+ # @param [Item, Sequence] element a parent element
211
+ #
212
+ def write_delimiter(element)
213
+ delimiter_tag = (element.tag == ITEM_TAG ? ITEM_DELIMITER : SEQUENCE_DELIMITER)
214
+ write_tag(delimiter_tag)
215
+ write_vr_length(delimiter_tag, ITEM_VR, 0)
216
+ end
217
+
218
+ # Handles the encoding of DICOM information to string as well as writing it to file.
219
+ #
220
+ # @param [Hash] options the options to use for encoding the DICOM string
221
+ # @option options [String] :file_name the path & name of the DICOM file which is to be written to disk
222
+ # @option options [Boolean] :signature if true, the 128 byte preamble and 'DICM' signature is prepended to the encoded string
223
+ # @option options [String] :syntax the transfer syntax used for the encoding settings of the post-meta part of the DICOM string
224
+ #
225
+ def write_elements(options={})
226
+ # Check if we are able to create given file:
227
+ open_file(options[:file_name])
228
+ # Go ahead and write if the file was opened successfully:
229
+ if @file
230
+ # Initiate necessary variables:
231
+ @transfer_syntax = options[:syntax]
232
+ # Until a DICOM write has completed successfully the status is 'unsuccessful':
233
+ @write_success = false
234
+ # Default explicitness of start of DICOM file:
235
+ @explicit = true
236
+ # Default endianness of start of DICOM files (little endian):
237
+ @str_endian = false
238
+ # When the file switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
239
+ @switched = false
240
+ # Items contained under the Pixel Data element needs some special attention to write correctly:
241
+ @enc_image = false
242
+ # Create a Stream instance to handle the encoding of content to a binary string:
243
+ @stream = Stream.new(nil, @str_endian)
244
+ # Tell the Stream instance which file to write to:
245
+ @stream.set_file(@file)
246
+ # Write the DICOM signature:
247
+ write_signature if options[:signature]
248
+ write_data_elements(children)
249
+ # As file has been written successfully, it can be closed.
250
+ @file.close
251
+ # Mark this write session as successful:
252
+ @write_success = true
253
+ end
254
+ end
255
+
256
+ # Writes the DICOM header signature (128 bytes + 'DICM').
257
+ #
258
+ def write_signature
259
+ # Write the string "DICM" which along with the empty bytes that
260
+ # will be put before it, identifies this as a valid DICOM file:
261
+ identifier = @stream.encode("DICM", "STR")
262
+ # Fill in 128 empty bytes:
263
+ filler = @stream.encode("00"*128, "HEX")
264
+ @stream.write(filler)
265
+ @stream.write(identifier)
266
+ end
267
+
268
+ # Encodes and writes a tag (the first part of the data element).
269
+ #
270
+ # @param [String] tag a data element tag
271
+ #
272
+ def write_tag(tag)
273
+ # Group 0002 is always little endian, but the rest of the file may be little or big endian.
274
+ # When we shift from group 0002 to another group we need to update our endian/explicitness variables:
275
+ switch_syntax_on_write if tag.group != META_GROUP and @switched == false
276
+ # Write to binary string:
277
+ bin_tag = @stream.encode_tag(tag)
278
+ add_encoded(bin_tag)
279
+ end
280
+
281
+ # Writes the data element's pre-encoded value.
282
+ #
283
+ # @param [String] bin the binary string value of this data element
284
+ #
285
+ def write_value(bin)
286
+ # This is pretty straightforward, just dump the binary data to the file/string:
287
+ add_encoded(bin) if bin
288
+ end
289
+
290
+ # Encodes and writes the value representation (if it is to be written) and length value.
291
+ # The encoding scheme to be applied here depends on explicitness, data element type and vr.
292
+ #
293
+ # @param [String] tag the tag of this data element
294
+ # @param [String] vr the value representation of this data element
295
+ # @param [Integer] length the data element's length
296
+ #
297
+ def write_vr_length(tag, vr, length)
298
+ # Encode the length value (cover both scenarios of 2 and 4 bytes):
299
+ length4 = @stream.encode(length, "SL")
300
+ length2 = @stream.encode(length, "US")
301
+ # Structure will differ, dependent on whether we have explicit or implicit encoding:
302
+ # *****EXPLICIT*****:
303
+ if @explicit == true
304
+ # Step 1: Write VR (if it is to be written)
305
+ unless ITEM_TAGS.include?(tag)
306
+ # Write data element VR (2 bytes - since we are not dealing with an item related element):
307
+ add_encoded(@stream.encode(vr, "STR"))
308
+ end
309
+ # Step 2: Write length
310
+ # Three possible structures for value length here, dependent on data element vr:
311
+ case vr
312
+ when "OB","OW","OF","SQ","UN","UT"
313
+ if @enc_image # (4 bytes)
314
+ # Item under an encapsulated Pixel Data (7FE0,0010).
315
+ add_encoded(length4)
316
+ else # (6 bytes total)
317
+ # Two reserved bytes first:
318
+ add_encoded(@stream.encode("00"*2, "HEX"))
319
+ # Value length (4 bytes):
320
+ add_encoded(length4)
321
+ end
322
+ when ITEM_VR # (4 bytes)
323
+ # For the item elements: "FFFE,E000", "FFFE,E00D" and "FFFE,E0DD"
324
+ add_encoded(length4)
325
+ else # (2 bytes)
326
+ # For all the other data element vr, value length is 2 bytes:
327
+ add_encoded(length2)
328
+ end
329
+ else
330
+ # *****IMPLICIT*****:
331
+ # No VR written.
332
+ # Writing value length (4 bytes):
333
+ add_encoded(length4)
334
+ end
335
+ end
336
+
337
+ # Changes encoding variables as the file writing proceeds past the initial meta
338
+ # group part (0002,xxxx) of the DICOM object.
339
+ #
340
+ def switch_syntax_on_write
341
+ # Process the transfer syntax string to establish encoding settings:
342
+ ts = LIBRARY.uid(@transfer_syntax)
343
+ logger.warn("Invalid/unknown transfer syntax: #{@transfer_syntax} Will complete encoding the file, but an investigation of the result is recommended.") unless ts && ts.transfer_syntax?
344
+ @rest_explicit = ts ? ts.explicit? : true
345
+ @rest_endian = ts ? ts.big_endian? : false
346
+ # Make sure we only run this method once:
347
+ @switched = true
348
+ # Update explicitness and endianness (pack/unpack variables):
349
+ @explicit = @rest_explicit
350
+ @str_endian = @rest_endian
351
+ @stream.endian = @rest_endian
352
+ end
353
+
354
+ end
355
+
356
356
  end