Mercurial > hg > orthanc
comparison OrthancFramework/Sources/DicomParsing/DicomDirWriter.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | Core/DicomParsing/DicomDirWriter.cpp@2a170a8f1faf |
children | bf7b9edf6b81 |
comparison
equal
deleted
inserted
replaced
4043:6c6239aec462 | 4044:d25f4c0fa160 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 /*========================================================================= | |
38 | |
39 This file is based on portions of the following project: | |
40 | |
41 Program: DCMTK 3.6.0 | |
42 Module: http://dicom.offis.de/dcmtk.php.en | |
43 | |
44 Copyright (C) 1994-2011, OFFIS e.V. | |
45 All rights reserved. | |
46 | |
47 This software and supporting documentation were developed by | |
48 | |
49 OFFIS e.V. | |
50 R&D Division Health | |
51 Escherweg 2 | |
52 26121 Oldenburg, Germany | |
53 | |
54 Redistribution and use in source and binary forms, with or without | |
55 modification, are permitted provided that the following conditions | |
56 are met: | |
57 | |
58 - Redistributions of source code must retain the above copyright | |
59 notice, this list of conditions and the following disclaimer. | |
60 | |
61 - Redistributions in binary form must reproduce the above copyright | |
62 notice, this list of conditions and the following disclaimer in the | |
63 documentation and/or other materials provided with the distribution. | |
64 | |
65 - Neither the name of OFFIS nor the names of its contributors may be | |
66 used to endorse or promote products derived from this software | |
67 without specific prior written permission. | |
68 | |
69 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
70 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
71 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
72 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
73 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
74 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
75 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
76 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
77 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
78 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
79 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
80 | |
81 =========================================================================*/ | |
82 | |
83 | |
84 | |
85 /*** | |
86 | |
87 Validation: | |
88 | |
89 # sudo apt-get install dicom3tools | |
90 # dciodvfy DICOMDIR 2>&1 | less | |
91 # dcentvfy DICOMDIR 2>&1 | less | |
92 | |
93 http://www.dclunie.com/dicom3tools/dciodvfy.html | |
94 | |
95 DICOMDIR viewer working with Wine under Linux: | |
96 http://www.microdicom.com/ | |
97 | |
98 ***/ | |
99 | |
100 | |
101 #include "../PrecompiledHeaders.h" | |
102 #include "DicomDirWriter.h" | |
103 | |
104 #include "FromDcmtkBridge.h" | |
105 #include "ToDcmtkBridge.h" | |
106 | |
107 #include "../Compatibility.h" | |
108 #include "../Logging.h" | |
109 #include "../OrthancException.h" | |
110 #include "../TemporaryFile.h" | |
111 #include "../Toolbox.h" | |
112 #include "../SystemToolbox.h" | |
113 | |
114 #include <dcmtk/dcmdata/dcdicdir.h> | |
115 #include <dcmtk/dcmdata/dcmetinf.h> | |
116 #include <dcmtk/dcmdata/dcdeftag.h> | |
117 #include <dcmtk/dcmdata/dcuid.h> | |
118 #include <dcmtk/dcmdata/dcddirif.h> | |
119 #include <dcmtk/dcmdata/dcvrui.h> | |
120 #include <dcmtk/dcmdata/dcsequen.h> | |
121 #include <dcmtk/dcmdata/dcostrmf.h> | |
122 #include "dcmtk/dcmdata/dcvrda.h" /* for class DcmDate */ | |
123 #include "dcmtk/dcmdata/dcvrtm.h" /* for class DcmTime */ | |
124 | |
125 #include <memory> | |
126 | |
127 namespace Orthanc | |
128 { | |
129 class DicomDirWriter::PImpl | |
130 { | |
131 private: | |
132 bool utc_; | |
133 std::string fileSetId_; | |
134 bool extendedSopClass_; | |
135 TemporaryFile file_; | |
136 std::unique_ptr<DcmDicomDir> dir_; | |
137 | |
138 typedef std::pair<ResourceType, std::string> IndexKey; | |
139 typedef std::map<IndexKey, DcmDirectoryRecord* > Index; | |
140 Index index_; | |
141 | |
142 | |
143 DcmDicomDir& GetDicomDir() | |
144 { | |
145 if (dir_.get() == NULL) | |
146 { | |
147 dir_.reset(new DcmDicomDir(file_.GetPath().c_str(), | |
148 fileSetId_.c_str())); | |
149 //SetTagValue(dir_->getRootRecord(), DCM_SpecificCharacterSet, GetDicomSpecificCharacterSet(Encoding_Utf8)); | |
150 } | |
151 | |
152 return *dir_; | |
153 } | |
154 | |
155 | |
156 DcmDirectoryRecord& GetRoot() | |
157 { | |
158 return GetDicomDir().getRootRecord(); | |
159 } | |
160 | |
161 | |
162 static bool GetUtf8TagValue(std::string& result, | |
163 DcmItem& source, | |
164 Encoding encoding, | |
165 bool hasCodeExtensions, | |
166 const DcmTagKey& key) | |
167 { | |
168 DcmElement* element = NULL; | |
169 result.clear(); | |
170 | |
171 if (source.findAndGetElement(key, element).good()) | |
172 { | |
173 char* s = NULL; | |
174 if (element->isLeaf() && | |
175 element->getString(s).good()) | |
176 { | |
177 if (s != NULL) | |
178 { | |
179 result = Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions); | |
180 } | |
181 | |
182 return true; | |
183 } | |
184 } | |
185 | |
186 return false; | |
187 } | |
188 | |
189 | |
190 static void SetTagValue(DcmDirectoryRecord& target, | |
191 const DcmTagKey& key, | |
192 const std::string& valueUtf8) | |
193 { | |
194 std::string s = Toolbox::ConvertFromUtf8(valueUtf8, Encoding_Ascii); | |
195 | |
196 if (!target.putAndInsertString(key, s.c_str()).good()) | |
197 { | |
198 throw OrthancException(ErrorCode_InternalError); | |
199 } | |
200 } | |
201 | |
202 | |
203 | |
204 static bool CopyString(DcmDirectoryRecord& target, | |
205 DcmDataset& source, | |
206 Encoding encoding, | |
207 bool hasCodeExtensions, | |
208 const DcmTagKey& key, | |
209 bool optional, | |
210 bool copyEmpty) | |
211 { | |
212 if (optional && | |
213 !source.tagExistsWithValue(key) && | |
214 !(copyEmpty && source.tagExists(key))) | |
215 { | |
216 return false; | |
217 } | |
218 | |
219 std::string value; | |
220 bool found = GetUtf8TagValue(value, source, encoding, hasCodeExtensions, key); | |
221 | |
222 if (!found) | |
223 { | |
224 // We don't raise an exception if "!optional", even if this | |
225 // results in an invalid DICOM file | |
226 value.clear(); | |
227 } | |
228 | |
229 SetTagValue(target, key, value); | |
230 return found; | |
231 } | |
232 | |
233 | |
234 static void CopyStringType1(DcmDirectoryRecord& target, | |
235 DcmDataset& source, | |
236 Encoding encoding, | |
237 bool hasCodeExtensions, | |
238 const DcmTagKey& key) | |
239 { | |
240 CopyString(target, source, encoding, hasCodeExtensions, key, false, false); | |
241 } | |
242 | |
243 static void CopyStringType1C(DcmDirectoryRecord& target, | |
244 DcmDataset& source, | |
245 Encoding encoding, | |
246 bool hasCodeExtensions, | |
247 const DcmTagKey& key) | |
248 { | |
249 CopyString(target, source, encoding, hasCodeExtensions, key, true, false); | |
250 } | |
251 | |
252 static void CopyStringType2(DcmDirectoryRecord& target, | |
253 DcmDataset& source, | |
254 Encoding encoding, | |
255 bool hasCodeExtensions, | |
256 const DcmTagKey& key) | |
257 { | |
258 CopyString(target, source, encoding, hasCodeExtensions, key, false, true); | |
259 } | |
260 | |
261 static void CopyStringType3(DcmDirectoryRecord& target, | |
262 DcmDataset& source, | |
263 Encoding encoding, | |
264 bool hasCodeExtensions, | |
265 const DcmTagKey& key) | |
266 { | |
267 CopyString(target, source, encoding, hasCodeExtensions, key, true, true); | |
268 } | |
269 | |
270 | |
271 public: | |
272 PImpl() : | |
273 utc_(true), // By default, use UTC (universal time, not local time) | |
274 fileSetId_("ORTHANC_MEDIA"), | |
275 extendedSopClass_(false) | |
276 { | |
277 } | |
278 | |
279 bool IsUtcUsed() const | |
280 { | |
281 return utc_; | |
282 } | |
283 | |
284 | |
285 void SetUtcUsed(bool utc) | |
286 { | |
287 utc_ = utc; | |
288 } | |
289 | |
290 void EnableExtendedSopClass(bool enable) | |
291 { | |
292 if (enable) | |
293 { | |
294 LOG(WARNING) << "Generating a DICOMDIR with type 3 attributes, " | |
295 << "which leads to an Extended SOP Class"; | |
296 } | |
297 | |
298 extendedSopClass_ = enable; | |
299 } | |
300 | |
301 bool IsExtendedSopClass() const | |
302 { | |
303 return extendedSopClass_; | |
304 } | |
305 | |
306 void FillPatient(DcmDirectoryRecord& record, | |
307 DcmDataset& dicom, | |
308 Encoding encoding, | |
309 bool hasCodeExtensions) | |
310 { | |
311 // cf. "DicomDirInterface::buildPatientRecord()" | |
312 | |
313 CopyStringType1C(record, dicom, encoding, hasCodeExtensions, DCM_PatientID); | |
314 CopyStringType2(record, dicom, encoding, hasCodeExtensions, DCM_PatientName); | |
315 } | |
316 | |
317 void FillStudy(DcmDirectoryRecord& record, | |
318 DcmDataset& dicom, | |
319 Encoding encoding, | |
320 bool hasCodeExtensions) | |
321 { | |
322 // cf. "DicomDirInterface::buildStudyRecord()" | |
323 | |
324 std::string nowDate, nowTime; | |
325 SystemToolbox::GetNowDicom(nowDate, nowTime, utc_); | |
326 | |
327 std::string studyDate; | |
328 if (!GetUtf8TagValue(studyDate, dicom, encoding, hasCodeExtensions, DCM_StudyDate) && | |
329 !GetUtf8TagValue(studyDate, dicom, encoding, hasCodeExtensions, DCM_SeriesDate) && | |
330 !GetUtf8TagValue(studyDate, dicom, encoding, hasCodeExtensions, DCM_AcquisitionDate) && | |
331 !GetUtf8TagValue(studyDate, dicom, encoding, hasCodeExtensions, DCM_ContentDate)) | |
332 { | |
333 studyDate = nowDate; | |
334 } | |
335 | |
336 std::string studyTime; | |
337 if (!GetUtf8TagValue(studyTime, dicom, encoding, hasCodeExtensions, DCM_StudyTime) && | |
338 !GetUtf8TagValue(studyTime, dicom, encoding, hasCodeExtensions, DCM_SeriesTime) && | |
339 !GetUtf8TagValue(studyTime, dicom, encoding, hasCodeExtensions, DCM_AcquisitionTime) && | |
340 !GetUtf8TagValue(studyTime, dicom, encoding, hasCodeExtensions, DCM_ContentTime)) | |
341 { | |
342 studyTime = nowTime; | |
343 } | |
344 | |
345 /* copy attribute values from dataset to study record */ | |
346 SetTagValue(record, DCM_StudyDate, studyDate); | |
347 SetTagValue(record, DCM_StudyTime, studyTime); | |
348 CopyStringType2(record, dicom, encoding, hasCodeExtensions, DCM_StudyDescription); | |
349 CopyStringType1(record, dicom, encoding, hasCodeExtensions, DCM_StudyInstanceUID); | |
350 /* use type 1C instead of 1 in order to avoid unwanted overwriting */ | |
351 CopyStringType1C(record, dicom, encoding, hasCodeExtensions, DCM_StudyID); | |
352 CopyStringType2(record, dicom, encoding, hasCodeExtensions, DCM_AccessionNumber); | |
353 } | |
354 | |
355 void FillSeries(DcmDirectoryRecord& record, | |
356 DcmDataset& dicom, | |
357 Encoding encoding, | |
358 bool hasCodeExtensions) | |
359 { | |
360 // cf. "DicomDirInterface::buildSeriesRecord()" | |
361 | |
362 /* copy attribute values from dataset to series record */ | |
363 CopyStringType1(record, dicom, encoding, hasCodeExtensions, DCM_Modality); | |
364 CopyStringType1(record, dicom, encoding, hasCodeExtensions, DCM_SeriesInstanceUID); | |
365 /* use type 1C instead of 1 in order to avoid unwanted overwriting */ | |
366 CopyStringType1C(record, dicom, encoding, hasCodeExtensions, DCM_SeriesNumber); | |
367 | |
368 // Add extended (non-standard) type 3 tags, those are not generated by DCMTK | |
369 // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part02/sect_7.3.html | |
370 // https://groups.google.com/d/msg/orthanc-users/Y7LOvZMDeoc/9cp3kDgxAwAJ | |
371 if (extendedSopClass_) | |
372 { | |
373 CopyStringType3(record, dicom, encoding, hasCodeExtensions, DCM_SeriesDescription); | |
374 } | |
375 } | |
376 | |
377 void FillInstance(DcmDirectoryRecord& record, | |
378 DcmDataset& dicom, | |
379 Encoding encoding, | |
380 bool hasCodeExtensions, | |
381 DcmMetaInfo& metaInfo, | |
382 const char* path) | |
383 { | |
384 // cf. "DicomDirInterface::buildImageRecord()" | |
385 | |
386 /* copy attribute values from dataset to image record */ | |
387 CopyStringType1(record, dicom, encoding, hasCodeExtensions, DCM_InstanceNumber); | |
388 //CopyElementType1C(record, dicom, encoding, hasCodeExtensions, DCM_ImageType); | |
389 | |
390 // REMOVED since 0.9.7: copyElementType1C(dicom, DCM_ReferencedImageSequence, record); | |
391 | |
392 std::string sopClassUid, sopInstanceUid, transferSyntaxUid; | |
393 if (!GetUtf8TagValue(sopClassUid, dicom, encoding, hasCodeExtensions, DCM_SOPClassUID) || | |
394 !GetUtf8TagValue(sopInstanceUid, dicom, encoding, hasCodeExtensions, DCM_SOPInstanceUID) || | |
395 !GetUtf8TagValue(transferSyntaxUid, metaInfo, encoding, hasCodeExtensions, DCM_TransferSyntaxUID)) | |
396 { | |
397 throw OrthancException(ErrorCode_BadFileFormat); | |
398 } | |
399 | |
400 SetTagValue(record, DCM_ReferencedFileID, path); | |
401 SetTagValue(record, DCM_ReferencedSOPClassUIDInFile, sopClassUid); | |
402 SetTagValue(record, DCM_ReferencedSOPInstanceUIDInFile, sopInstanceUid); | |
403 SetTagValue(record, DCM_ReferencedTransferSyntaxUIDInFile, transferSyntaxUid); | |
404 } | |
405 | |
406 | |
407 | |
408 bool CreateResource(DcmDirectoryRecord*& target, | |
409 ResourceType level, | |
410 ParsedDicomFile& dicom, | |
411 const char* filename, | |
412 const char* path) | |
413 { | |
414 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); | |
415 | |
416 bool hasCodeExtensions; | |
417 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions); | |
418 | |
419 bool found; | |
420 std::string id; | |
421 E_DirRecType type; | |
422 | |
423 switch (level) | |
424 { | |
425 case ResourceType_Patient: | |
426 if (!GetUtf8TagValue(id, dataset, encoding, hasCodeExtensions, DCM_PatientID)) | |
427 { | |
428 // Be tolerant about missing patient ID. Fixes issue #124 | |
429 // (GET /studies/ID/media fails for certain dicom file). | |
430 id = ""; | |
431 } | |
432 | |
433 found = true; | |
434 type = ERT_Patient; | |
435 break; | |
436 | |
437 case ResourceType_Study: | |
438 found = GetUtf8TagValue(id, dataset, encoding, hasCodeExtensions, DCM_StudyInstanceUID); | |
439 type = ERT_Study; | |
440 break; | |
441 | |
442 case ResourceType_Series: | |
443 found = GetUtf8TagValue(id, dataset, encoding, hasCodeExtensions, DCM_SeriesInstanceUID); | |
444 type = ERT_Series; | |
445 break; | |
446 | |
447 case ResourceType_Instance: | |
448 found = GetUtf8TagValue(id, dataset, encoding, hasCodeExtensions, DCM_SOPInstanceUID); | |
449 type = ERT_Image; | |
450 break; | |
451 | |
452 default: | |
453 throw OrthancException(ErrorCode_InternalError); | |
454 } | |
455 | |
456 if (!found) | |
457 { | |
458 throw OrthancException(ErrorCode_BadFileFormat); | |
459 } | |
460 | |
461 IndexKey key = std::make_pair(level, std::string(id.c_str())); | |
462 Index::iterator it = index_.find(key); | |
463 | |
464 if (it != index_.end()) | |
465 { | |
466 target = it->second; | |
467 return false; // Already existing | |
468 } | |
469 | |
470 std::unique_ptr<DcmDirectoryRecord> record(new DcmDirectoryRecord(type, NULL, filename)); | |
471 | |
472 switch (level) | |
473 { | |
474 case ResourceType_Patient: | |
475 FillPatient(*record, dataset, encoding, hasCodeExtensions); | |
476 break; | |
477 | |
478 case ResourceType_Study: | |
479 FillStudy(*record, dataset, encoding, hasCodeExtensions); | |
480 break; | |
481 | |
482 case ResourceType_Series: | |
483 FillSeries(*record, dataset, encoding, hasCodeExtensions); | |
484 break; | |
485 | |
486 case ResourceType_Instance: | |
487 FillInstance(*record, dataset, encoding, hasCodeExtensions, *dicom.GetDcmtkObject().getMetaInfo(), path); | |
488 break; | |
489 | |
490 default: | |
491 throw OrthancException(ErrorCode_InternalError); | |
492 } | |
493 | |
494 CopyStringType1C(*record, dataset, encoding, hasCodeExtensions, DCM_SpecificCharacterSet); | |
495 | |
496 target = record.get(); | |
497 GetRoot().insertSub(record.release()); | |
498 index_[key] = target; | |
499 | |
500 return true; // Newly created | |
501 } | |
502 | |
503 void Read(std::string& s) | |
504 { | |
505 if (!GetDicomDir().write(DICOMDIR_DEFAULT_TRANSFERSYNTAX, | |
506 EET_UndefinedLength /*encodingType*/, | |
507 EGL_withoutGL /*groupLength*/).good()) | |
508 { | |
509 throw OrthancException(ErrorCode_InternalError); | |
510 } | |
511 | |
512 file_.Read(s); | |
513 } | |
514 | |
515 void SetFileSetId(const std::string& id) | |
516 { | |
517 dir_.reset(NULL); | |
518 fileSetId_ = id; | |
519 } | |
520 }; | |
521 | |
522 | |
523 DicomDirWriter::DicomDirWriter() : pimpl_(new PImpl) | |
524 { | |
525 } | |
526 | |
527 void DicomDirWriter::SetUtcUsed(bool utc) | |
528 { | |
529 pimpl_->SetUtcUsed(utc); | |
530 } | |
531 | |
532 bool DicomDirWriter::IsUtcUsed() const | |
533 { | |
534 return pimpl_->IsUtcUsed(); | |
535 } | |
536 | |
537 void DicomDirWriter::SetFileSetId(const std::string& id) | |
538 { | |
539 pimpl_->SetFileSetId(id); | |
540 } | |
541 | |
542 void DicomDirWriter::Add(const std::string& directory, | |
543 const std::string& filename, | |
544 ParsedDicomFile& dicom) | |
545 { | |
546 std::string path; | |
547 if (directory.empty()) | |
548 { | |
549 path = filename; | |
550 } | |
551 else | |
552 { | |
553 if (directory[directory.length() - 1] == '/' || | |
554 directory[directory.length() - 1] == '\\') | |
555 { | |
556 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
557 } | |
558 | |
559 path = directory + '\\' + filename; | |
560 } | |
561 | |
562 DcmDirectoryRecord* instance; | |
563 bool isNewInstance = pimpl_->CreateResource(instance, ResourceType_Instance, dicom, filename.c_str(), path.c_str()); | |
564 if (isNewInstance) | |
565 { | |
566 DcmDirectoryRecord* series; | |
567 bool isNewSeries = pimpl_->CreateResource(series, ResourceType_Series, dicom, filename.c_str(), NULL); | |
568 series->insertSub(instance); | |
569 | |
570 if (isNewSeries) | |
571 { | |
572 DcmDirectoryRecord* study; | |
573 bool isNewStudy = pimpl_->CreateResource(study, ResourceType_Study, dicom, filename.c_str(), NULL); | |
574 study->insertSub(series); | |
575 | |
576 if (isNewStudy) | |
577 { | |
578 DcmDirectoryRecord* patient; | |
579 pimpl_->CreateResource(patient, ResourceType_Patient, dicom, filename.c_str(), NULL); | |
580 patient->insertSub(study); | |
581 } | |
582 } | |
583 } | |
584 } | |
585 | |
586 void DicomDirWriter::Encode(std::string& target) | |
587 { | |
588 pimpl_->Read(target); | |
589 } | |
590 | |
591 | |
592 void DicomDirWriter::EnableExtendedSopClass(bool enable) | |
593 { | |
594 pimpl_->EnableExtendedSopClass(enable); | |
595 } | |
596 | |
597 | |
598 bool DicomDirWriter::IsExtendedSopClass() const | |
599 { | |
600 return pimpl_->IsExtendedSopClass(); | |
601 } | |
602 } |