1 /*
2  *
3  *  Copyright (C) 2000-2020, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module: dcmsr
15  *
16  *  Author: Joerg Riesmeier
17  *
18  *  Purpose:
19  *    classes: DSRDocument
20  *
21  */
22 
23 
24 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
25 
26 #include "dcmtk/dcmsr/dsrdoc.h"
27 #include "dcmtk/dcmsr/dsrxmld.h"
28 #include "dcmtk/dcmsr/dsrpnmtn.h"
29 #include "dcmtk/dcmsr/dsrdattn.h"
30 #include "dcmtk/dcmsr/dsrdtitn.h"
31 #include "dcmtk/dcmsr/dsrtimtn.h"
32 
33 #include "dcmtk/dcmdata/dcdeftag.h"
34 #include "dcmtk/dcmdata/dcuid.h"
35 #include "dcmtk/dcmdata/dcvrdt.h"
36 
37 
38 /*---------------------*
39  *  macro definitions  *
40  *---------------------*/
41 
42 #define DCMSR_PRINT_HEADER_FIELD_START(header_name, delimiter)         \
43     DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_HEADER_NAME)   \
44     stream << header_name;                                             \
45     DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_DELIMITER)     \
46     stream << delimiter;                                               \
47     DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_HEADER_VALUE)
48 
49 #define DCMSR_PRINT_HEADER_FIELD_END                                   \
50     DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_RESET)         \
51     stream << OFendl;
52 
53 
54 /*------------------*
55  *  implementation  *
56  *------------------*/
57 
DSRDocument(const E_DocumentType documentType)58 DSRDocument::DSRDocument(const E_DocumentType documentType)
59   : DocumentTree(documentType),
60     FinalizedFlag(OFFalse),
61     PreliminaryFlagEnum(PF_invalid),
62     CompletionFlagEnum(CF_invalid),
63     VerificationFlagEnum(VF_invalid),
64     SpecificCharacterSetEnum(CS_default),
65     SOPClassUID(DCM_SOPClassUID),
66     SOPInstanceUID(DCM_SOPInstanceUID),
67     SpecificCharacterSet(DCM_SpecificCharacterSet),
68     InstanceCreationDate(DCM_InstanceCreationDate),
69     InstanceCreationTime(DCM_InstanceCreationTime),
70     InstanceCreatorUID(DCM_InstanceCreatorUID),
71     CodingSchemeIdentification(),
72     TimezoneOffsetFromUTC(DCM_TimezoneOffsetFromUTC),
73     StudyInstanceUID(DCM_StudyInstanceUID),
74     StudyDate(DCM_StudyDate),
75     StudyTime(DCM_StudyTime),
76     ReferringPhysicianName(DCM_ReferringPhysicianName),
77     StudyID(DCM_StudyID),
78     AccessionNumber(DCM_AccessionNumber),
79     StudyDescription(DCM_StudyDescription),
80     PatientName(DCM_PatientName),
81     PatientID(DCM_PatientID),
82     IssuerOfPatientID(DCM_IssuerOfPatientID),
83     PatientBirthDate(DCM_PatientBirthDate),
84     PatientSex(DCM_PatientSex),
85     Manufacturer(DCM_Manufacturer),
86     ManufacturerModelName(DCM_ManufacturerModelName),
87     DeviceSerialNumber(DCM_DeviceSerialNumber),
88     SoftwareVersions(DCM_SoftwareVersions),
89     SynchronizationFrameOfReferenceUID(DCM_SynchronizationFrameOfReferenceUID),
90     SynchronizationTrigger(DCM_SynchronizationTrigger),
91     AcquisitionTimeSynchronized(DCM_AcquisitionTimeSynchronized),
92     Modality(DCM_Modality),
93     SeriesInstanceUID(DCM_SeriesInstanceUID),
94     SeriesNumber(DCM_SeriesNumber),
95     SeriesDate(DCM_SeriesDate),
96     SeriesTime(DCM_SeriesTime),
97     ProtocolName(DCM_ProtocolName),
98     SeriesDescription(DCM_SeriesDescription),
99     ReferencedPerformedProcedureStep(DCM_ReferencedPerformedProcedureStepSequence),
100     InstanceNumber(DCM_InstanceNumber),
101     PreliminaryFlag(DCM_PreliminaryFlag),
102     CompletionFlag(DCM_CompletionFlag),
103     CompletionFlagDescription(DCM_CompletionFlagDescription),
104     VerificationFlag(DCM_VerificationFlag),
105     ContentDate(DCM_ContentDate),
106     ContentTime(DCM_ContentTime),
107     VerifyingObserver(DCM_VerifyingObserverSequence),
108     PredecessorDocuments(DCM_PredecessorDocumentsSequence),
109     IdenticalDocuments(DCM_IdenticalDocumentsSequence),
110     PerformedProcedureCode(DCM_PerformedProcedureCodeSequence),
111     CurrentRequestedProcedureEvidence(DCM_CurrentRequestedProcedureEvidenceSequence),
112     PertinentOtherEvidence(DCM_PertinentOtherEvidenceSequence),
113     ReferencedInstances()
114 {
115     DCMSR_DEBUG("Initializing all DICOM header attributes");
116     /* set initial values for a new SOP instance */
117     updateAttributes(OFTrue /*updateAll*/, OFFalse /*verboseMode*/);
118 }
119 
120 
~DSRDocument()121 DSRDocument::~DSRDocument()
122 {
123 }
124 
125 
clear()126 void DSRDocument::clear()
127 {
128     /* clear SR document tree */
129     DocumentTree.clear();
130     FinalizedFlag = OFFalse;
131     /* clear enumerated values */
132     PreliminaryFlagEnum = PF_invalid;
133     CompletionFlagEnum = CF_invalid;
134     VerificationFlagEnum = VF_invalid;
135     SpecificCharacterSetEnum = CS_default;
136     /* clear all DICOM attributes */
137     SOPClassUID.clear();
138     SOPInstanceUID.clear();
139     SpecificCharacterSet.clear();
140     InstanceCreationDate.clear();
141     InstanceCreationTime.clear();
142     InstanceCreatorUID.clear();
143     CodingSchemeIdentification.clear();
144     TimezoneOffsetFromUTC.clear();
145     StudyInstanceUID.clear();
146     StudyDate.clear();
147     StudyTime.clear();
148     ReferringPhysicianName.clear();
149     StudyID.clear();
150     AccessionNumber.clear();
151     StudyDescription.clear();
152     PatientName.clear();
153     PatientID.clear();
154     IssuerOfPatientID.clear();
155     PatientBirthDate.clear();
156     PatientSex.clear();
157     Manufacturer.clear();
158     ManufacturerModelName.clear();
159     DeviceSerialNumber.clear();
160     SoftwareVersions.clear();
161     SynchronizationFrameOfReferenceUID.clear();
162     SynchronizationTrigger.clear();
163     AcquisitionTimeSynchronized.clear();
164     Modality.clear();
165     SeriesInstanceUID.clear();
166     SeriesNumber.clear();
167     SeriesDate.clear();
168     SeriesTime.clear();
169     ProtocolName.clear();
170     SeriesDescription.clear();
171     ReferencedPerformedProcedureStep.clear();
172     InstanceNumber.clear();
173     PreliminaryFlag.clear();
174     CompletionFlag.clear();
175     CompletionFlagDescription.clear();
176     VerificationFlag.clear();
177     ContentDate.clear();
178     ContentTime.clear();
179     VerifyingObserver.clear();
180     PerformedProcedureCode.clear();
181     /* clear list structures */
182     PredecessorDocuments.clear();
183     IdenticalDocuments.clear();
184     CurrentRequestedProcedureEvidence.clear();
185     PertinentOtherEvidence.clear();
186     ReferencedInstances.clear();
187 }
188 
189 
isValid()190 OFBool DSRDocument::isValid()
191 {
192     /* document is valid if the document tree is valid and ... */
193     return DocumentTree.isValid() && !SOPClassUID.isEmpty() && !SOPInstanceUID.isEmpty();
194 }
195 
196 
isFinalized() const197 OFBool DSRDocument::isFinalized() const
198 {
199     return FinalizedFlag;
200 }
201 
202 
print(STD_NAMESPACE ostream & stream,const size_t flags)203 OFCondition DSRDocument::print(STD_NAMESPACE ostream &stream,
204                                const size_t flags)
205 {
206     OFCondition result = SR_EC_InvalidDocument;
207     if (isValid())
208     {
209         OFString tmpString, string2;
210         /* update only some DICOM attributes */
211         updateAttributes(OFFalse /*updateAll*/);
212         /* check whether general SR modules are used */
213         const OFBool usesGeneralSRModules = usesSRDocumentGeneralModule(getDocumentType());
214 
215         // --- print some general document information ---
216 
217         if (!(flags & PF_printNoDocumentHeader))
218         {
219             /* document type/title */
220             DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_DOCUMENT_TYPE)
221             stream << documentTypeToDocumentTitle(getDocumentType(), tmpString);
222             DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_RESET)
223             stream << OFendl << OFendl;
224             /* patient related information */
225             if (!PatientName.isEmpty())
226             {
227                 DCMSR_PRINT_HEADER_FIELD_START("Patient            ", " : ")
228                 stream << getPrintStringFromElement(PatientName, tmpString);
229                 OFString patientStr;
230                 if (!PatientSex.isEmpty())
231                     patientStr += getPrintStringFromElement(PatientSex, tmpString);
232                 if (!PatientBirthDate.isEmpty())
233                 {
234                     if (!patientStr.empty())
235                         patientStr += ", ";
236                     patientStr += dicomToReadableDate(getStringValueFromElement(PatientBirthDate, tmpString), string2);
237                 }
238                 if (!PatientID.isEmpty())
239                 {
240                     if (!patientStr.empty())
241                         patientStr += ", ";
242                     patientStr += '#';
243                     patientStr += getPrintStringFromElement(PatientID, tmpString);
244                     if (!IssuerOfPatientID.isEmpty())
245                     {
246                         patientStr += ":";
247                         patientStr += getPrintStringFromElement(IssuerOfPatientID, tmpString);
248                     }
249                 }
250                 if (!patientStr.empty())
251                     stream << " (" << patientStr << ")";
252                 DCMSR_PRINT_HEADER_FIELD_END
253             }
254             /* referring physician */
255             if (!ReferringPhysicianName.isEmpty())
256             {
257                 DCMSR_PRINT_HEADER_FIELD_START("Referring Physician", " : ")
258                 stream << getPrintStringFromElement(ReferringPhysicianName, tmpString);
259                 DCMSR_PRINT_HEADER_FIELD_END
260             }
261             /* study-related information */
262             if (!StudyDescription.isEmpty())
263             {
264                 DCMSR_PRINT_HEADER_FIELD_START("Study              ", " : ")
265                 stream << getPrintStringFromElement(StudyDescription, tmpString);
266                 if (!StudyID.isEmpty())
267                     stream << " (#" << getPrintStringFromElement(StudyID, tmpString) << ")";
268                 DCMSR_PRINT_HEADER_FIELD_END
269             }
270             /* series-related information */
271             if (!SeriesDescription.isEmpty())
272             {
273                 DCMSR_PRINT_HEADER_FIELD_START("Series             ", " : ")
274                 stream << getPrintStringFromElement(SeriesDescription, tmpString);
275                 if (!SeriesNumber.isEmpty())
276                     stream << " (#" << getPrintStringFromElement(SeriesNumber, tmpString) << ")";
277                 DCMSR_PRINT_HEADER_FIELD_END
278             }
279             /* protocol name */
280             if (!ProtocolName.isEmpty())
281             {
282                 DCMSR_PRINT_HEADER_FIELD_START("Protocol           ", " : ")
283                 stream << getPrintStringFromElement(ProtocolName, tmpString);
284                 DCMSR_PRINT_HEADER_FIELD_END
285             }
286             /* manufacturer and device */
287             if (!Manufacturer.isEmpty())
288             {
289                 DCMSR_PRINT_HEADER_FIELD_START("Manufacturer       ", " : ")
290                 stream << getPrintStringFromElement(Manufacturer, tmpString);
291                 OFString deviceStr;
292                 if (!ManufacturerModelName.isEmpty())
293                     deviceStr += getPrintStringFromElement(ManufacturerModelName, tmpString);
294                 if (!DeviceSerialNumber.isEmpty())
295                 {
296                     if (!deviceStr.empty())
297                         deviceStr += ", ";
298                     deviceStr += '#';
299                     deviceStr += getPrintStringFromElement(DeviceSerialNumber, tmpString);
300                 }
301                 if (!deviceStr.empty())
302                     stream << " (" << deviceStr << ")";
303                 DCMSR_PRINT_HEADER_FIELD_END
304             }
305             /* not all SR IODs contain the SR Document General Module */
306             if (usesGeneralSRModules)
307             {
308                 /* preliminary flag */
309                 if (!PreliminaryFlag.isEmpty())
310                 {
311                     DCMSR_PRINT_HEADER_FIELD_START("Preliminary Flag   ", " : ")
312                     stream << getStringValueFromElement(PreliminaryFlag, tmpString);
313                     DCMSR_PRINT_HEADER_FIELD_END
314                 }
315                 /* completion flag */
316                 DCMSR_PRINT_HEADER_FIELD_START("Completion Flag    ", " : ")
317                 stream << getStringValueFromElement(CompletionFlag, tmpString);
318                 DCMSR_PRINT_HEADER_FIELD_END
319                 if (!CompletionFlagDescription.isEmpty())
320                 {
321                     DCMSR_PRINT_HEADER_FIELD_START("                   ", "   ")
322                     stream << getPrintStringFromElement(CompletionFlagDescription, tmpString);
323                     DCMSR_PRINT_HEADER_FIELD_END
324                 }
325                 /* predecessor documents */
326                 if (!PredecessorDocuments.isEmpty())
327                 {
328                     DCMSR_PRINT_HEADER_FIELD_START("Predecessor Docs   ", " : ")
329                     stream << PredecessorDocuments.getNumberOfInstances();
330                     DCMSR_PRINT_HEADER_FIELD_END
331                 }
332             }
333             /* identical documents */
334             if (!IdenticalDocuments.isEmpty())
335             {
336                 DCMSR_PRINT_HEADER_FIELD_START("Identical Docs     ", " : ")
337                 stream << IdenticalDocuments.getNumberOfInstances();
338                 DCMSR_PRINT_HEADER_FIELD_END
339             }
340             /* referenced instances */
341             if (!ReferencedInstances.isEmpty())
342             {
343                 DCMSR_PRINT_HEADER_FIELD_START("References Objects ", " : ")
344                 stream << ReferencedInstances.getNumberOfItems();
345                 DCMSR_PRINT_HEADER_FIELD_END
346             }
347             if (usesGeneralSRModules)
348             {
349                 /* verification flag */
350                 DCMSR_PRINT_HEADER_FIELD_START("Verification Flag  ", " : ")
351                 stream << getStringValueFromElement(VerificationFlag, tmpString);
352                 DCMSR_PRINT_HEADER_FIELD_END
353                 /* verifying observer */
354                 const size_t obsCount = getNumberOfVerifyingObservers();
355                 for (size_t i = 1; i <= obsCount; i++)
356                 {
357                     OFString dateTime, obsName, organization;
358                     DSRCodedEntryValue obsCode;
359                     if (getVerifyingObserver(i, dateTime, obsName, obsCode, organization).good())
360                     {
361                         if (i == 1)
362                         {
363                             DCMSR_PRINT_HEADER_FIELD_START("Verifying Observers", " : ")
364                         } else {
365                             DCMSR_PRINT_HEADER_FIELD_START("                   ", "   ")
366                         }
367                         stream << dicomToReadableDateTime(dateTime, tmpString) << ", " << obsName;
368                         if (obsCode.isValid() || (flags & PF_printInvalidCodes))
369                         {
370                             stream << " ";
371                             obsCode.print(stream, (flags & PF_printAllCodes) > 0 /*printCodeValue*/, flags);
372                         }
373                         stream << ", " << organization;
374                         DCMSR_PRINT_HEADER_FIELD_END
375                     }
376                 }
377             }
378             /* content date and time */
379             if (!ContentDate.isEmpty() && !ContentTime.isEmpty())
380             {
381                 DCMSR_PRINT_HEADER_FIELD_START("Content Date/Time  ", " : ")
382                 stream << dicomToReadableDate(getStringValueFromElement(ContentDate, tmpString), string2) << " ";
383                 stream << dicomToReadableTime(getStringValueFromElement(ContentTime, tmpString), string2);
384                 DCMSR_PRINT_HEADER_FIELD_END
385             }
386             stream << OFendl;
387         }
388 
389         // --- dump document tree to stream ---
390         result = DocumentTree.print(stream, flags);
391     }
392     return result;
393 }
394 
395 
checkDatasetForReading(DcmItem & dataset,E_DocumentType & documentType)396 OFCondition DSRDocument::checkDatasetForReading(DcmItem &dataset,
397                                                 E_DocumentType &documentType)
398 {
399     OFCondition result = EC_Normal;
400     OFString tmpString;
401     DcmUniqueIdentifier sopClassUID(DCM_SOPClassUID);
402     DcmCodeString modality(DCM_Modality);
403     /* check SOP class UID */
404     result = getAndCheckElementFromDataset(dataset, sopClassUID, "1", "1", "SOPCommonModule");
405     if (result.good())
406     {
407         documentType = sopClassUIDToDocumentType(getStringValueFromElement(sopClassUID, tmpString));
408         DCMSR_DEBUG("Value of SOP Class UID: " << tmpString);
409         if (documentType == DT_invalid)
410         {
411             DCMSR_ERROR("SOP Class UID does not match one of the known SR document classes");
412             result = SR_EC_UnknownDocumentType;
413         }
414         else if (!isDocumentTypeSupported(documentType))
415         {
416             DCMSR_ERROR("Unsupported SOP Class UID (not yet implemented)");
417             result = SR_EC_UnsupportedValue;
418         }
419     } else {
420         /* no SOP Class UID means no document type */
421         documentType = DT_invalid;
422     }
423     /* check modality */
424     if (result.good())
425     {
426         if (usesKeyObjectDocumentSeriesModule(documentType))
427             result = getAndCheckElementFromDataset(dataset, modality, "1", "1", "KeyObjectDocumentSeriesModule");
428         else
429             result = getAndCheckElementFromDataset(dataset, modality, "1", "1", "SRDocumentSeriesModule");
430         if (result.good())
431         {
432             if (getStringValueFromElement(modality, tmpString) != documentTypeToModality(documentType))
433             {
434                 DCMSR_ERROR("Modality does not match '" << documentTypeToModality(documentType) << "' for "
435                     << documentTypeToReadableName(documentType));
436             }
437         }
438     }
439     return result;
440 }
441 
442 
read(DcmItem & dataset,const size_t flags)443 OFCondition DSRDocument::read(DcmItem &dataset,
444                               const size_t flags)
445 {
446     OFCondition result = EC_Normal;
447     E_DocumentType documentType = DT_invalid;
448     DCMSR_DEBUG("Reading SR document from DICOM dataset");
449     /* re-initialize SR document */
450     clear();
451     /* check SOP class UID and modality first */
452     result = checkDatasetForReading(dataset, documentType);
453     /* dataset is OK */
454     if (result.good())
455     {
456         OFString tmpString;
457         OFCondition searchCond = EC_Normal;
458         OFCondition obsSearchCond = EC_Normal;
459 
460         /* type 3 element and attributes which have already been checked are not checked */
461 
462         // --- SOP Common Module ---
463         getElementFromDataset(dataset, SOPClassUID);   /* already checked */
464         getAndCheckElementFromDataset(dataset, SOPInstanceUID, "1", "1", "SOPCommonModule");
465         getAndCheckElementFromDataset(dataset, SpecificCharacterSet, "1-n", "1C", "SOPCommonModule");
466         getStringValueFromElement(SpecificCharacterSet, tmpString, -1 /* all components */);
467         /* currently, the VR checker in 'dcmdata' only supports ASCII and Latin-1 */
468         if (!tmpString.empty() && (tmpString != "ISO_IR 6") && (tmpString != "ISO_IR 100"))
469             DCMSR_WARN("The VR checker does not support this Specific Character Set: " << tmpString);
470         getAndCheckElementFromDataset(dataset, InstanceCreationDate, "1", "3", "SOPCommonModule");
471         getAndCheckElementFromDataset(dataset, InstanceCreationTime, "1", "3", "SOPCommonModule");
472         getAndCheckElementFromDataset(dataset, InstanceCreatorUID, "1", "3", "SOPCommonModule");
473         CodingSchemeIdentification.read(dataset, flags);
474         if (requiresTimezoneModule(documentType))
475         {
476             // --- Timezone Module ---
477             getAndCheckElementFromDataset(dataset, TimezoneOffsetFromUTC, "1", "1", "TimezoneModule");
478         } else {
479             // --- SOP Common Module ---
480             getAndCheckElementFromDataset(dataset, TimezoneOffsetFromUTC, "1", "3", "SOPCommonModule");
481         }
482 
483         // --- General Study and Patient Module ---
484         readStudyData(dataset, flags);
485 
486         if (requiresEnhancedEquipmentModule(documentType))
487         {
488             // --- Enhanced General Equipment Module ---
489             getAndCheckElementFromDataset(dataset, Manufacturer, "1", "1", "EnhancedGeneralEquipmentModule");
490             getAndCheckElementFromDataset(dataset, ManufacturerModelName, "1", "1", "EnhancedGeneralEquipmentModule");
491             getAndCheckElementFromDataset(dataset, DeviceSerialNumber, "1", "1", "EnhancedGeneralEquipmentModule");
492             getAndCheckElementFromDataset(dataset, SoftwareVersions, "1-n", "1", "EnhancedGeneralEquipmentModule");
493         } else {
494             // --- General Equipment Module ---
495             getAndCheckElementFromDataset(dataset, Manufacturer, "1", "2", "GeneralEquipmentModule");
496             getAndCheckElementFromDataset(dataset, ManufacturerModelName, "1", "3", "GeneralEquipmentModule");
497             getAndCheckElementFromDataset(dataset, DeviceSerialNumber, "1", "3", "GeneralEquipmentModule");
498             getAndCheckElementFromDataset(dataset, SoftwareVersions, "1-n", "3", "GeneralEquipmentModule");
499         }
500 
501         // --- Synchronization Module ---
502         if (requiresSynchronizationModule(documentType) /* either the IOD requires this module */ ||
503             dataset.tagExistsWithValue(DCM_SynchronizationFrameOfReferenceUID) || dataset.tagExistsWithValue(DCM_SynchronizationTrigger) ||
504             dataset.tagExistsWithValue(DCM_AcquisitionTimeSynchronized) /* or all attributes should be absent */ )
505         {
506             getAndCheckElementFromDataset(dataset, SynchronizationFrameOfReferenceUID, "1", "1", "SynchronizationModule");
507             getAndCheckElementFromDataset(dataset, SynchronizationTrigger, "1", "1", "SynchronizationModule");
508             getAndCheckElementFromDataset(dataset, AcquisitionTimeSynchronized, "1", "1", "SynchronizationModule");
509         }
510 
511         // --- SR Document Series Module / Key Object Document Series Module ---
512         getElementFromDataset(dataset, Modality);   /* already checked */
513         if (usesKeyObjectDocumentSeriesModule(documentType))
514         {
515             getAndCheckElementFromDataset(dataset, SeriesInstanceUID, "1", "1", "KeyObjectDocumentSeriesModule");
516             getAndCheckElementFromDataset(dataset, SeriesNumber, "1", "1", "KeyObjectDocumentSeriesModule");
517             getAndCheckElementFromDataset(dataset, SeriesDate, "1", "3", "KeyObjectDocumentSeriesModule");
518             getAndCheckElementFromDataset(dataset, SeriesTime, "1", "3", "KeyObjectDocumentSeriesModule");
519             getAndCheckElementFromDataset(dataset, ProtocolName, "1", "3", "KeyObjectDocumentSeriesModule");
520             getAndCheckElementFromDataset(dataset, SeriesDescription, "1", "3", "KeyObjectDocumentSeriesModule");
521             /* need to check sequence in two steps (avoids additional getAndCheck... method) */
522             searchCond = getElementFromDataset(dataset, ReferencedPerformedProcedureStep);
523             checkElementValue(ReferencedPerformedProcedureStep, "1", "2", searchCond, "KeyObjectDocumentSeriesModule");
524         } else {
525             getAndCheckElementFromDataset(dataset, SeriesInstanceUID, "1", "1", "SRDocumentSeriesModule");
526             getAndCheckElementFromDataset(dataset, SeriesNumber, "1", "1", "SRDocumentSeriesModule");
527             getAndCheckElementFromDataset(dataset, SeriesDate, "1", "3", "SRDocumentSeriesModule");
528             getAndCheckElementFromDataset(dataset, SeriesTime, "1", "3", "SRDocumentSeriesModule");
529             getAndCheckElementFromDataset(dataset, ProtocolName, "1", "3", "SRDocumentSeriesModule");
530             getAndCheckElementFromDataset(dataset, SeriesDescription, "1", "3", "SRDocumentSeriesModule");
531             /* need to check sequence in two steps (avoids additional getAndCheck... method) */
532             searchCond = getElementFromDataset(dataset, ReferencedPerformedProcedureStep);
533             checkElementValue(ReferencedPerformedProcedureStep, "1", "2", searchCond, "SRDocumentSeriesModule");
534         }
535         /* remove possible signature sequences */
536         removeAttributeFromSequence(ReferencedPerformedProcedureStep, DCM_MACParametersSequence);
537         removeAttributeFromSequence(ReferencedPerformedProcedureStep, DCM_DigitalSignaturesSequence);
538 
539         // --- SR Document General Module / Key Object Document Module ---
540         if (usesKeyObjectDocumentModule(documentType))
541         {
542             getAndCheckElementFromDataset(dataset, InstanceNumber, "1", "1", "KeyObjectDocumentModule");
543             getAndCheckElementFromDataset(dataset, ContentDate, "1", "1", "KeyObjectDocumentModule");
544             getAndCheckElementFromDataset(dataset, ContentTime, "1", "1", "KeyObjectDocumentModule");
545         } else {
546             getAndCheckElementFromDataset(dataset, InstanceNumber, "1", "1", "SRDocumentGeneralModule");
547             getAndCheckElementFromDataset(dataset, ContentDate, "1", "1", "SRDocumentGeneralModule");
548             getAndCheckElementFromDataset(dataset, ContentTime, "1", "1", "SRDocumentGeneralModule");
549             getAndCheckElementFromDataset(dataset, PreliminaryFlag, "1", "3", "SRDocumentGeneralModule");
550             getAndCheckElementFromDataset(dataset, CompletionFlag, "1", "1", "SRDocumentGeneralModule");
551             getAndCheckElementFromDataset(dataset, CompletionFlagDescription, "1", "3", "SRDocumentGeneralModule");
552             getAndCheckElementFromDataset(dataset, VerificationFlag, "1", "1", "SRDocumentGeneralModule");
553             obsSearchCond = getElementFromDataset(dataset, VerifyingObserver);
554             PredecessorDocuments.read(dataset, flags);
555             /* need to check sequence in two steps (avoids additional getAndCheck... method) */
556             searchCond = getElementFromDataset(dataset, PerformedProcedureCode);
557             checkElementValue(PerformedProcedureCode, "1-n", "2", searchCond, "SRDocumentGeneralModule");
558             PertinentOtherEvidence.read(dataset, flags);
559             ReferencedInstances.read(dataset, flags);
560         }
561         IdenticalDocuments.read(dataset, flags);
562         CurrentRequestedProcedureEvidence.read(dataset, flags);
563         /* remove possible signature sequences */
564         removeAttributeFromSequence(VerifyingObserver, DCM_MACParametersSequence);
565         removeAttributeFromSequence(VerifyingObserver, DCM_DigitalSignaturesSequence);
566         removeAttributeFromSequence(PerformedProcedureCode, DCM_MACParametersSequence);
567         removeAttributeFromSequence(PerformedProcedureCode, DCM_DigitalSignaturesSequence);
568 
569         /* update internal enumerated values and perform additional checks */
570 
571         /* not all SR IODs contain the SR Document General Module */
572         if (usesSRDocumentGeneralModule(documentType))
573         {
574             /* get and check PreliminaryFlag (if present) */
575             if (!PreliminaryFlag.isEmpty())
576             {
577                 PreliminaryFlagEnum = enumeratedValueToPreliminaryFlag(getStringValueFromElement(PreliminaryFlag, tmpString));
578                 if (PreliminaryFlagEnum == PF_invalid)
579                     printUnknownValueWarningMessage("PreliminaryFlag", tmpString.c_str());
580             }
581             /* get and check CompletionFlag */
582             CompletionFlagEnum = enumeratedValueToCompletionFlag(getStringValueFromElement(CompletionFlag, tmpString));
583             if (CompletionFlagEnum == CF_invalid)
584                 printUnknownValueWarningMessage("CompletionFlag", tmpString.c_str());
585             else if ((documentType == DT_XRayRadiationDoseSR) && (CompletionFlagEnum != CF_Complete))
586                 DCMSR_WARN("Invalid value for Completion Flag, should be 'COMPLETE' for X-Ray Radiation Dose SR");
587             /* get and check VerificationFlag / VerifyingObserverSequence */
588             VerificationFlagEnum = enumeratedValueToVerificationFlag(getStringValueFromElement(VerificationFlag, tmpString));
589             if (VerificationFlagEnum == VF_invalid)
590                 printUnknownValueWarningMessage("VerificationFlag", tmpString.c_str());
591             else if (VerificationFlagEnum == VF_Verified)
592                 checkElementValue(VerifyingObserver, "1-n", "1", obsSearchCond, "SRDocumentGeneralModule");
593         }
594         getStringValueFromElement(SpecificCharacterSet, tmpString, -1 /* all components */);
595         SpecificCharacterSetEnum = definedTermToCharacterSet(tmpString);
596         /* check SpecificCharacterSet */
597         if ((SpecificCharacterSetEnum == CS_invalid) && !tmpString.empty())
598             printUnknownValueWarningMessage("SpecificCharacterSet", tmpString.c_str());
599 
600         /* read SR document tree */
601         if (result.good())
602             result = DocumentTree.read(dataset, documentType, flags);
603     }
604     return result;
605 }
606 
607 
readPatientData(DcmItem & dataset,const size_t)608 OFCondition DSRDocument::readPatientData(DcmItem &dataset,
609                                          const size_t /*flags*/)
610 {
611     // --- Patient Module ---
612     getAndCheckElementFromDataset(dataset, PatientName, "1", "2", "PatientModule");
613     getAndCheckElementFromDataset(dataset, PatientID, "1", "2", "PatientModule");
614     getAndCheckElementFromDataset(dataset, IssuerOfPatientID, "1", "3", "PatientModule");
615     getAndCheckElementFromDataset(dataset, PatientBirthDate, "1", "2", "PatientModule");
616     getAndCheckElementFromDataset(dataset, PatientSex, "1", "2", "PatientModule");
617     /* always return success */
618     return EC_Normal;
619 }
620 
621 
readStudyData(DcmItem & dataset,const size_t flags)622 OFCondition DSRDocument::readStudyData(DcmItem &dataset,
623                                        const size_t flags)
624 {
625     // --- General Study Module ---
626     getAndCheckElementFromDataset(dataset, StudyInstanceUID, "1", "1", "GeneralStudyModule");
627     getAndCheckElementFromDataset(dataset, StudyDate, "1", "2", "GeneralStudyModule");
628     getAndCheckElementFromDataset(dataset, StudyTime, "1", "2", "GeneralStudyModule");
629     getAndCheckElementFromDataset(dataset, ReferringPhysicianName, "1", "2", "GeneralStudyModule");
630     getAndCheckElementFromDataset(dataset, StudyID, "1", "2", "GeneralStudyModule");
631     getAndCheckElementFromDataset(dataset, AccessionNumber, "1", "2", "GeneralStudyModule");
632     getAndCheckElementFromDataset(dataset, StudyDescription, "1", "3", "GeneralStudyModule");
633     /* also read data from Patient Module */
634     return readPatientData(dataset, flags);
635 }
636 
637 
write(DcmItem & dataset,DcmStack * markedItems)638 OFCondition DSRDocument::write(DcmItem &dataset,
639                                DcmStack *markedItems)
640 {
641     OFCondition result = EC_Normal;
642     /* only write valid documents */
643     if (isValid())
644     {
645         DCMSR_DEBUG("Writing SR document to DICOM dataset");
646         /* update all DICOM attributes */
647         updateAttributes();
648 
649         /* checking particular values */
650         if ((getDocumentType() == DT_XRayRadiationDoseSR) && (CompletionFlagEnum != CF_Complete))
651             DCMSR_WARN("Invalid value for Completion Flag, should be 'COMPLETE' for X-Ray Radiation Dose SR");
652 
653         /* write general document attributes */
654 
655         // --- SOP Common Module ---
656         addElementToDataset(result, dataset, new DcmUniqueIdentifier(SOPClassUID), "1", "1", "SOPCommonModule");
657         addElementToDataset(result, dataset, new DcmUniqueIdentifier(SOPInstanceUID), "1", "1", "SOPCommonModule");
658         /* never write specific character set for ASCII (default character repertoire) */
659         if (SpecificCharacterSetEnum != CS_ASCII)
660             addElementToDataset(result, dataset, new DcmCodeString(SpecificCharacterSet), "1-n", "1C", "SOPCommonModule");
661         addElementToDataset(result, dataset, new DcmDate(InstanceCreationDate), "1", "3", "SOPCommonModule");
662         addElementToDataset(result, dataset, new DcmTime(InstanceCreationTime), "1", "3", "SOPCommonModule");
663         addElementToDataset(result, dataset, new DcmUniqueIdentifier(InstanceCreatorUID), "1", "3", "SOPCommonModule");
664         CodingSchemeIdentification.write(dataset);
665         if (requiresTimezoneModule(getDocumentType()))
666         {
667             // --- Timezone Module ---
668             addElementToDataset(result, dataset, new DcmShortString(TimezoneOffsetFromUTC), "1", "1", "TimezoneModule");
669         } else {
670             // --- SOP Common Module ---
671             addElementToDataset(result, dataset, new DcmShortString(TimezoneOffsetFromUTC), "1", "3", "SOPCommonModule");
672         }
673 
674         // --- General Study Module ---
675         addElementToDataset(result, dataset, new DcmUniqueIdentifier(StudyInstanceUID), "1", "1", "GeneralStudyModule");
676         addElementToDataset(result, dataset, new DcmDate(StudyDate), "1", "2", "GeneralStudyModule");
677         addElementToDataset(result, dataset, new DcmTime(StudyTime), "1", "2", "GeneralStudyModule");
678         addElementToDataset(result, dataset, new DcmPersonName(ReferringPhysicianName), "1", "2", "GeneralStudyModule");
679         addElementToDataset(result, dataset, new DcmShortString(StudyID), "1", "2", "GeneralStudyModule");
680         addElementToDataset(result, dataset, new DcmShortString(AccessionNumber), "1", "2", "GeneralStudyModule");
681         addElementToDataset(result, dataset, new DcmLongString(StudyDescription), "1", "3", "GeneralStudyModule");
682 
683         // --- Patient Module ---
684         addElementToDataset(result, dataset, new DcmPersonName(PatientName), "1", "2", "PatientModule");
685         addElementToDataset(result, dataset, new DcmLongString(PatientID), "1", "2", "PatientModule");
686         addElementToDataset(result, dataset, new DcmLongString(IssuerOfPatientID), "1", "3", "PatientModule");
687         addElementToDataset(result, dataset, new DcmDate(PatientBirthDate), "1", "2", "PatientModule");
688         addElementToDataset(result, dataset, new DcmCodeString(PatientSex), "1", "2", "PatientModule");
689 
690         if (requiresEnhancedEquipmentModule(getDocumentType()))
691         {
692             // --- Enhanced General Equipment Module ---
693             addElementToDataset(result, dataset, new DcmLongString(Manufacturer), "1", "1", "EnhancedGeneralEquipmentModule");
694             addElementToDataset(result, dataset, new DcmLongString(ManufacturerModelName), "1", "1", "EnhancedGeneralEquipmentModule");
695             addElementToDataset(result, dataset, new DcmLongString(DeviceSerialNumber), "1", "1", "EnhancedGeneralEquipmentModule");
696             addElementToDataset(result, dataset, new DcmLongString(SoftwareVersions), "1-n", "1", "EnhancedGeneralEquipmentModule");
697         } else {
698             // --- General Equipment Module ---
699             addElementToDataset(result, dataset, new DcmLongString(Manufacturer), "1", "2", "GeneralEquipmentModule");
700             addElementToDataset(result, dataset, new DcmLongString(ManufacturerModelName), "1", "3", "GeneralEquipmentModule");
701             addElementToDataset(result, dataset, new DcmLongString(DeviceSerialNumber), "1", "3", "GeneralEquipmentModule");
702             addElementToDataset(result, dataset, new DcmLongString(SoftwareVersions), "1-n", "3", "GeneralEquipmentModule");
703         }
704 
705         // --- Synchronization Module ---
706         if (requiresSynchronizationModule(getDocumentType()) /* module required for some IODs */ ||
707             !SynchronizationFrameOfReferenceUID.isEmpty() || !SynchronizationTrigger.isEmpty() || !AcquisitionTimeSynchronized.isEmpty())
708         {
709             addElementToDataset(result, dataset, new DcmUniqueIdentifier(SynchronizationFrameOfReferenceUID), "1", "1", "SynchronizationModule");
710             addElementToDataset(result, dataset, new DcmCodeString(SynchronizationTrigger), "1", "1", "SynchronizationModule");
711             addElementToDataset(result, dataset, new DcmCodeString(AcquisitionTimeSynchronized), "1", "1", "SynchronizationModule");
712         }
713 
714         // --- SR Document Series Module / Key Object Document Series Module ---
715         if (usesKeyObjectDocumentSeriesModule(getDocumentType()))
716         {
717             addElementToDataset(result, dataset, new DcmCodeString(Modality), "1", "1", "KeyObjectDocumentSeriesModule");
718             addElementToDataset(result, dataset, new DcmUniqueIdentifier(SeriesInstanceUID), "1", "1", "KeyObjectDocumentSeriesModule");
719             addElementToDataset(result, dataset, new DcmIntegerString(SeriesNumber), "1", "1", "KeyObjectDocumentSeriesModule");
720             addElementToDataset(result, dataset, new DcmDate(SeriesDate), "1", "3", "KeyObjectDocumentSeriesModule");
721             addElementToDataset(result, dataset, new DcmTime(SeriesTime), "1", "3", "KeyObjectDocumentSeriesModule");
722             addElementToDataset(result, dataset, new DcmLongString(ProtocolName), "1", "3", "KeyObjectDocumentSeriesModule");
723             addElementToDataset(result, dataset, new DcmLongString(SeriesDescription), "1", "3", "KeyObjectDocumentSeriesModule");
724             /* always write empty sequence since not yet fully supported */
725             ReferencedPerformedProcedureStep.clear();
726             addElementToDataset(result, dataset, new DcmSequenceOfItems(ReferencedPerformedProcedureStep), "1", "2", "KeyObjectDocumentSeriesModule");
727         } else {
728             addElementToDataset(result, dataset, new DcmCodeString(Modality), "1", "1", "SRDocumentSeriesModule");
729             addElementToDataset(result, dataset, new DcmUniqueIdentifier(SeriesInstanceUID), "1", "1", "SRDocumentSeriesModule");
730             addElementToDataset(result, dataset, new DcmIntegerString(SeriesNumber), "1", "1", "SRDocumentSeriesModule");
731             addElementToDataset(result, dataset, new DcmDate(SeriesDate), "1", "3", "SRDocumentSeriesModule");
732             addElementToDataset(result, dataset, new DcmTime(SeriesTime), "1", "3", "SRDocumentSeriesModule");
733             addElementToDataset(result, dataset, new DcmLongString(ProtocolName), "1", "3", "SRDocumentSeriesModule");
734             addElementToDataset(result, dataset, new DcmLongString(SeriesDescription), "1", "3", "SRDocumentSeriesModule");
735             /* always write empty sequence since not yet fully supported */
736             ReferencedPerformedProcedureStep.clear();
737             addElementToDataset(result, dataset, new DcmSequenceOfItems(ReferencedPerformedProcedureStep), "1", "2", "SRDocumentSeriesModule");
738         }
739 
740         // --- SR Document General Module / Key Object Document Module ---
741         if (usesKeyObjectDocumentModule(getDocumentType()))
742         {
743             addElementToDataset(result, dataset, new DcmIntegerString(InstanceNumber), "1", "1", "KeyObjectDocumentModule");
744             addElementToDataset(result, dataset, new DcmDate(ContentDate), "1", "1", "KeyObjectDocumentModule");
745             addElementToDataset(result, dataset, new DcmTime(ContentTime), "1", "1", "KeyObjectDocumentModule");
746         } else {
747             addElementToDataset(result, dataset, new DcmIntegerString(InstanceNumber), "1", "1", "SRDocumentGeneralModule");
748             addElementToDataset(result, dataset, new DcmDate(ContentDate), "1", "1", "SRDocumentGeneralModule");
749             addElementToDataset(result, dataset, new DcmTime(ContentTime), "1", "1", "SRDocumentGeneralModule");
750             addElementToDataset(result, dataset, new DcmCodeString(PreliminaryFlag), "1", "3", "SRDocumentGeneralModule");
751             addElementToDataset(result, dataset, new DcmCodeString(CompletionFlag), "1", "1", "SRDocumentGeneralModule");
752             addElementToDataset(result, dataset, new DcmLongString(CompletionFlagDescription), "1", "3", "SRDocumentGeneralModule");
753             addElementToDataset(result, dataset, new DcmCodeString(VerificationFlag), "1", "1", "SRDocumentGeneralModule");
754             if (VerificationFlagEnum == VF_Verified)
755                 addElementToDataset(result, dataset, new DcmSequenceOfItems(VerifyingObserver), "1-n", "1", "SRDocumentGeneralModule");
756             if (result.good())
757                 PredecessorDocuments.write(dataset);    /* optional */
758             /* always write empty sequence since not yet fully supported */
759             PerformedProcedureCode.clear();
760             addElementToDataset(result, dataset, new DcmSequenceOfItems(PerformedProcedureCode), "1-n", "2", "SRDocumentGeneralModule");
761             if (result.good())
762                 result = PertinentOtherEvidence.write(dataset);
763             if (result.good())
764                 result = ReferencedInstances.write(dataset);
765         }
766 
767         if (result.good())
768             IdenticalDocuments.write(dataset);          /* optional */
769         if (result.good())
770             result = CurrentRequestedProcedureEvidence.write(dataset);
771 
772         /* write SR document tree */
773         if (result.good())
774             result = DocumentTree.write(dataset, markedItems);
775     } else
776         result = SR_EC_InvalidDocument;
777     return result;
778 }
779 
780 
readXML(const OFString & filename,const size_t flags)781 OFCondition DSRDocument::readXML(const OFString &filename,
782                                  const size_t flags)
783 {
784     DSRXMLDocument doc;
785     DCMSR_DEBUG("Reading SR document from XML format");
786     /* read, parse and validate XML document */
787     OFCondition result = doc.read(filename, flags);
788     if (result.good())
789     {
790         /* re-initialize SR document */
791         clear();
792         /* start with document root node */
793         DSRXMLCursor cursor(doc.getRootNode());
794         /* check whether we really parse a "report" document */
795         result = doc.checkNode(cursor, "report");
796         if (result.good())
797         {
798             /* goto sub-element "sopclass" (first child node!) */
799             result = doc.checkNode(cursor.gotoChild(), "sopclass");
800             if (result.good())
801             {
802                 /* determine document type (SOP class) */
803                 result = doc.getElementFromAttribute(cursor, SOPClassUID, "uid");
804                 if (result.good())
805                 {
806                     OFString sopClassUID;
807                     getSOPClassUID(sopClassUID);
808                     /* create new document of specified type (also checks for support) */
809                     result = createNewDocument(sopClassUIDToDocumentType(sopClassUID));
810                     if (result.good())
811                     {
812                         /* proceed with document header */
813                         result = readXMLDocumentHeader(doc, cursor.gotoNext(), flags);
814                     } else
815                         DCMSR_ERROR("Unknown/Unsupported SOP Class UID");
816                 }
817             }
818         }
819     }
820     return result;
821 }
822 
823 
readXMLDocumentHeader(DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)824 OFCondition DSRDocument::readXMLDocumentHeader(DSRXMLDocument &doc,
825                                                DSRXMLCursor cursor,
826                                                const size_t flags)
827 {
828     OFCondition result = SR_EC_InvalidDocument;
829     if (doc.valid() && cursor.valid())
830     {
831         result = EC_Normal;
832         /* iterate over all nodes */
833         while (cursor.valid() && result.good())
834         {
835             /* check for known element tags */
836             if (doc.matchNode(cursor, "charset"))
837             {
838                 /* use "charset" to decode special characters (has to be at the beginning) */
839                 if (!doc.encodingHandlerValid())
840                 {
841                     OFString tmpString;
842                     /* check for known character set */
843                     setSpecificCharacterSet(doc.getStringFromNodeContent(cursor, tmpString));
844                     if (tmpString.empty())
845                         DCMSR_WARN("Empty value for 'charset' ... ignoring");
846                     else {
847                         const char *encString = characterSetToXMLName(SpecificCharacterSetEnum);
848                         if ((strcmp(encString, "?") == 0) || doc.setEncodingHandler(encString).bad())
849                             DCMSR_WARN("Character set '" << tmpString << "' not supported");
850                     }
851                 } else {
852                     /* only one "charset" node allowed */
853                     doc.printUnexpectedNodeWarning(cursor);
854                 }
855             }
856             else if (doc.matchNode(cursor, "timezone"))
857             {
858                 doc.getElementFromNodeContent(cursor, TimezoneOffsetFromUTC, NULL, OFTrue /*encoding*/);
859             }
860             else if (doc.matchNode(cursor, "modality"))
861             {
862                 OFString tmpString;
863                 /* compare the XML node content */
864                 if (doc.getStringFromNodeContent(cursor, tmpString) != documentTypeToModality(getDocumentType()))
865                     DCMSR_WARN("Invalid value for 'modality' ... ignoring");
866             }
867             else if (doc.matchNode(cursor, "device"))
868             {
869                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "manufacturer", OFFalse /*required*/), Manufacturer, NULL, OFTrue /*encoding*/);
870                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "model"), ManufacturerModelName, NULL, OFTrue /*encoding*/);
871                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "serial", OFFalse /*required*/), DeviceSerialNumber, NULL, OFTrue /*encoding*/);
872                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "version", OFFalse /*required*/), SoftwareVersions, NULL, OFTrue /*encoding*/);
873             }
874             else if (doc.matchNode(cursor, "manufacturer"))
875                 doc.getElementFromNodeContent(cursor, Manufacturer, "manufacturer", OFTrue /*encoding*/);
876             else if (doc.matchNode(cursor, "synchronization"))
877             {
878                 doc.getElementFromAttribute(cursor, SynchronizationFrameOfReferenceUID, "uid");
879                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "trigger"), SynchronizationTrigger);
880                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "acquisitiontime"), AcquisitionTimeSynchronized);
881             }
882             else if (doc.matchNode(cursor, "referringphysician"))
883             {
884                 /* goto sub-element "name" */
885                 const DSRXMLCursor childNode = doc.getNamedChildNode(cursor, "name");
886                 if (childNode.valid())
887                 {
888                     /* Referring Physician's Name */
889                     OFString tmpString;
890                     DSRPNameTreeNode::getValueFromXMLNodeContent(doc, childNode.getChild(), tmpString);
891                     ReferringPhysicianName.putOFStringArray(tmpString);
892                 }
893             }
894             else if (doc.matchNode(cursor, "patient"))
895                 result = readXMLPatientData(doc, cursor.getChild(), flags);
896             else if (doc.matchNode(cursor, "study"))
897                 result = readXMLStudyData(doc, cursor, flags);
898             else if (doc.matchNode(cursor, "series"))
899                 result = readXMLSeriesData(doc, cursor, flags);
900             else if (doc.matchNode(cursor, "instance"))
901                 result = readXMLInstanceData(doc, cursor, flags);
902             else if (doc.matchNode(cursor, "coding"))
903             {
904                 const DSRXMLCursor childNode = cursor.getChild();
905                 if (childNode.valid())
906                     result = CodingSchemeIdentification.readXML(doc, childNode, flags);
907             }
908             else if (doc.matchNode(cursor, "evidence"))
909             {
910                 OFString typeString;
911                 /* check "type" attribute for corresponding sequence */
912                 if (doc.getStringFromAttribute(cursor, typeString, "type") == "Current Requested Procedure")
913                     result = CurrentRequestedProcedureEvidence.readXML(doc, cursor.getChild(), flags);
914                 else if (typeString == "Pertinent Other")
915                 {
916                     if (usesSRDocumentGeneralModule(getDocumentType()))
917                         result = PertinentOtherEvidence.readXML(doc, cursor.getChild(), flags);
918                     else
919                         doc.printUnexpectedNodeWarning(cursor);
920                 } else // none of the standard defined evidence types
921                     printUnknownValueWarningMessage("Evidence type", typeString.c_str());
922             }
923             else if (doc.matchNode(cursor, "reference"))
924             {
925                 const DSRXMLCursor childNode = cursor.getChild();
926                 if (childNode.valid())
927                     result = ReferencedInstances.readXML(doc, childNode, flags);
928             }
929             else if (doc.matchNode(cursor, "document"))
930                 result = readXMLDocumentData(doc, cursor.getChild(), flags);
931             else
932                 doc.printUnexpectedNodeWarning(cursor);
933             /* print node error message (if any) */
934             doc.printGeneralNodeError(cursor, result);
935             /* proceed with next node */
936             cursor.gotoNext();
937         }
938     }
939     return result;
940 }
941 
942 
readXMLPatientData(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t)943 OFCondition DSRDocument::readXMLPatientData(const DSRXMLDocument &doc,
944                                             DSRXMLCursor cursor,
945                                             const size_t /*flags*/)
946 {
947     OFCondition result = SR_EC_InvalidDocument;
948     if (cursor.valid())
949     {
950         OFString tmpString;
951         result = EC_Normal;
952         /* iterate over all nodes */
953         while (cursor.valid())
954         {
955             /* check for known element tags (all type 2) */
956             if (doc.matchNode(cursor, "name"))
957             {
958                 /* Patient's Name */
959                 DSRPNameTreeNode::getValueFromXMLNodeContent(doc, cursor.getChild(), tmpString);
960                 PatientName.putOFStringArray(tmpString);
961             }
962             else if (doc.matchNode(cursor, "birthday"))
963             {
964                 /* Patient's Birth Date */
965                 DSRDateTreeNode::getValueFromXMLNodeContent(doc, doc.getNamedChildNode(cursor, "date"), tmpString);
966                 PatientBirthDate.putOFStringArray(tmpString);
967             }
968             else if (doc.getElementFromNodeContent(cursor, PatientID, "id").bad() &&
969                      doc.getElementFromNodeContent(cursor, IssuerOfPatientID, "issuer").bad() &&
970                      doc.getElementFromNodeContent(cursor, PatientSex, "sex").bad())
971             {
972                 doc.printUnexpectedNodeWarning(cursor);
973             }
974             /* proceed with next node */
975             cursor.gotoNext();
976         }
977     }
978     return result;
979 }
980 
981 
readXMLStudyData(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)982 OFCondition DSRDocument::readXMLStudyData(const DSRXMLDocument &doc,
983                                           DSRXMLCursor cursor,
984                                           const size_t flags)
985 {
986     OFCondition result = SR_EC_InvalidDocument;
987     if (cursor.valid())
988     {
989         OFString tmpString;
990         /* get Study Instance UID from XML attribute */
991         if (flags & XF_acceptEmptyStudySeriesInstanceUID)
992         {
993             if (doc.getElementFromAttribute(cursor, StudyInstanceUID, "uid", OFFalse /*encoding*/, OFFalse /*required*/).bad())
994                 doc.printMissingAttributeWarning(cursor, "uid");
995             result = EC_Normal;
996         } else
997             result = doc.getElementFromAttribute(cursor, StudyInstanceUID, "uid");
998         /* goto first sub-element */
999         cursor.gotoChild();
1000         /* iterate over all nodes */
1001         while (cursor.valid())
1002         {
1003             /* check for known element tags */
1004             if (doc.matchNode(cursor, "accession"))
1005             {
1006                 /* goto sub-element "number" */
1007                 doc.getElementFromNodeContent(doc.getNamedChildNode(cursor, "number"), AccessionNumber);
1008             }
1009             else if (doc.matchNode(cursor, "date"))
1010             {
1011                 DSRDateTreeNode::getValueFromXMLNodeContent(doc, cursor, tmpString);
1012                 StudyDate.putOFStringArray(tmpString);
1013             }
1014             else if (doc.matchNode(cursor, "time"))
1015             {
1016                 DSRTimeTreeNode::getValueFromXMLNodeContent(doc, cursor, tmpString);
1017                 StudyTime.putOFStringArray(tmpString);
1018             }
1019             else if (doc.getElementFromNodeContent(cursor, StudyID, "id").bad() &&
1020                      doc.getElementFromNodeContent(cursor, StudyDescription, "description", OFTrue /*encoding*/).bad())
1021             {
1022                 doc.printUnexpectedNodeWarning(cursor);
1023             }
1024             /* proceed with next node */
1025             cursor.gotoNext();
1026         }
1027         /* check required element values */
1028         checkElementValue(StudyInstanceUID, "1", "1");
1029     }
1030     return result;
1031 }
1032 
1033 
readXMLSeriesData(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)1034 OFCondition DSRDocument::readXMLSeriesData(const DSRXMLDocument &doc,
1035                                            DSRXMLCursor cursor,
1036                                            const size_t flags)
1037 {
1038     OFCondition result = SR_EC_InvalidDocument;
1039     if (cursor.valid())
1040     {
1041         OFString tmpString;
1042         /* get Series Instance UID from XML attribute */
1043         if (flags & XF_acceptEmptyStudySeriesInstanceUID)
1044         {
1045             if (doc.getElementFromAttribute(cursor, SeriesInstanceUID, "uid", OFFalse /*encoding*/, OFFalse /*required*/).bad())
1046                 doc.printMissingAttributeWarning(cursor, "uid");
1047             result = EC_Normal;
1048         } else
1049             result = doc.getElementFromAttribute(cursor, SeriesInstanceUID, "uid");
1050         /* goto first sub-element */
1051         cursor.gotoChild();
1052         /* iterate over all nodes */
1053         while (cursor.valid())
1054         {
1055             /* check for known element tags */
1056             if (doc.matchNode(cursor, "date"))
1057             {
1058                 DSRDateTreeNode::getValueFromXMLNodeContent(doc, cursor, tmpString);
1059                 SeriesDate.putOFStringArray(tmpString);
1060             }
1061             else if (doc.matchNode(cursor, "time"))
1062             {
1063                 DSRTimeTreeNode::getValueFromXMLNodeContent(doc, cursor, tmpString);
1064                 SeriesTime.putOFStringArray(tmpString);
1065             }
1066             else if (doc.getElementFromNodeContent(cursor, SeriesNumber, "number").bad() &&
1067                 doc.getElementFromNodeContent(cursor, ProtocolName, "protocol", OFTrue /*encoding*/).bad() &&
1068                 doc.getElementFromNodeContent(cursor, SeriesDescription, "description", OFTrue /*encoding*/).bad())
1069             {
1070                 doc.printUnexpectedNodeWarning(cursor);
1071             }
1072             /* proceed with next node */
1073             cursor.gotoNext();
1074         }
1075         /* check required element values */
1076         checkElementValue(SeriesInstanceUID, "1", "1");
1077         checkElementValue(SeriesNumber, "1", "1");
1078     }
1079     return result;
1080 }
1081 
1082 
readXMLInstanceData(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)1083 OFCondition DSRDocument::readXMLInstanceData(const DSRXMLDocument &doc,
1084                                              DSRXMLCursor cursor,
1085                                              const size_t flags)
1086 {
1087     OFCondition result = SR_EC_InvalidDocument;
1088     if (cursor.valid())
1089     {
1090         OFString tmpString;
1091         /* get SOP Instance UID from XML attribute */
1092         if (flags & XF_acceptEmptyStudySeriesInstanceUID)
1093         {
1094             if (doc.getElementFromAttribute(cursor, SOPInstanceUID, "uid", OFFalse /*encoding*/, OFFalse /*required*/).bad())
1095                 doc.printMissingAttributeWarning(cursor, "uid");
1096             result = EC_Normal;
1097         } else
1098             result = doc.getElementFromAttribute(cursor, SOPInstanceUID, "uid");
1099         /* goto first sub-element */
1100         cursor.gotoChild();
1101         /* iterate over all nodes */
1102         while (cursor.valid())
1103         {
1104             /* check for known element tags */
1105             if (doc.matchNode(cursor, "creation"))
1106             {
1107                 /* Instance Creator UID */
1108                 doc.getElementFromAttribute(cursor, InstanceCreatorUID, "uid", OFFalse /*encoding*/, OFFalse /*required*/);
1109                 /* Instance Creation Date */
1110                 DSRDateTreeNode::getValueFromXMLNodeContent(doc, doc.getNamedChildNode(cursor, "date"), tmpString);
1111                 InstanceCreationDate.putOFStringArray(tmpString);
1112                 /* Instance Creation Time */
1113                 DSRTimeTreeNode::getValueFromXMLNodeContent(doc, doc.getNamedChildNode(cursor, "time"), tmpString);
1114                 InstanceCreationTime.putOFStringArray(tmpString);
1115             }
1116             else if (doc.getElementFromNodeContent(cursor, InstanceNumber, "number").bad())
1117                 doc.printUnexpectedNodeWarning(cursor);
1118             /* proceed with next node */
1119             cursor.gotoNext();
1120         }
1121         /* check required element values */
1122         checkElementValue(SOPInstanceUID, "1", "1");
1123     }
1124     return result;
1125 }
1126 
1127 
readXMLDocumentData(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)1128 OFCondition DSRDocument::readXMLDocumentData(const DSRXMLDocument &doc,
1129                                              DSRXMLCursor cursor,
1130                                              const size_t flags)
1131 {
1132     OFCondition result = SR_EC_InvalidDocument;
1133     if (cursor.valid())
1134     {
1135         OFString tmpString;
1136         /* check whether general SR modules are used */
1137         const OFBool usesGeneralSRModules = usesSRDocumentGeneralModule(getDocumentType());
1138         result = EC_Normal;
1139         /* iterate over all nodes */
1140         while (cursor.valid() && result.good())
1141         {
1142             /* check for known element tags
1143                (not all SR IODs contain the SR Document General Module) */
1144             if (usesGeneralSRModules && doc.matchNode(cursor, "preliminary"))
1145             {
1146                 /* Preliminary Flag */
1147                 PreliminaryFlagEnum = enumeratedValueToPreliminaryFlag(doc.getStringFromAttribute(cursor, tmpString, "flag"));
1148                 if (PreliminaryFlagEnum == PF_invalid)
1149                     printUnknownValueWarningMessage("PreliminaryFlag", tmpString.c_str());
1150             }
1151             else if (usesGeneralSRModules && doc.matchNode(cursor, "completion"))
1152             {
1153                 /* Completion Flag */
1154                 CompletionFlagEnum = enumeratedValueToCompletionFlag(doc.getStringFromAttribute(cursor, tmpString, "flag"));
1155                 if (CompletionFlagEnum != CF_invalid)
1156                 {
1157                     /* Completion Flag Description (optional) */
1158                     const DSRXMLCursor childCursor = doc.getNamedChildNode(cursor, "description", OFFalse /*required*/);
1159                     if (childCursor.valid())
1160                         doc.getElementFromNodeContent(childCursor, CompletionFlagDescription, NULL /*name*/, OFTrue /*encoding*/);
1161                 } else
1162                     printUnknownValueWarningMessage("CompletionFlag", tmpString.c_str());
1163             }
1164             else if (usesGeneralSRModules && doc.matchNode(cursor, "verification"))
1165             {
1166                 /* Verification Flag */
1167                 VerificationFlagEnum = enumeratedValueToVerificationFlag(doc.getStringFromAttribute(cursor, tmpString, "flag"));
1168                 if (VerificationFlagEnum != VF_invalid)
1169                 {
1170                     /* Verifying Observers (required if VERIFIED) */
1171                     result = readXMLVerifyingObserverData(doc, cursor.getChild(), flags);
1172                     /* allow absence in case of UNVERIFIED */
1173                     if (VerificationFlagEnum == VF_Unverified)
1174                         result = EC_Normal;
1175                 } else
1176                     printUnknownValueWarningMessage("VerificationFlag", tmpString.c_str());
1177             }
1178             else if (usesGeneralSRModules && doc.matchNode(cursor, "predecessor"))
1179             {
1180                 /* Predecessor Documents Sequence (optional) */
1181                 result = PredecessorDocuments.readXML(doc, cursor.getChild(), flags);
1182             }
1183             else if (doc.matchNode(cursor, "identical"))
1184             {
1185                 /* Identical Documents Sequence (optional) */
1186                 result = IdenticalDocuments.readXML(doc, cursor.getChild(), flags);
1187             }
1188             else if (doc.matchNode(cursor, "content"))
1189             {
1190                 /* Content Date */
1191                 DSRDateTreeNode::getValueFromXMLNodeContent(doc, doc.getNamedChildNode(cursor, "date"), tmpString);
1192                 ContentDate.putOFStringArray(tmpString);
1193                 /* Content Time */
1194                 DSRTimeTreeNode::getValueFromXMLNodeContent(doc, doc.getNamedChildNode(cursor, "time"), tmpString);
1195                 ContentTime.putOFStringArray(tmpString);
1196                 /* proceed with document tree */
1197                 result = DocumentTree.readXML(doc, cursor.getChild(), flags);
1198             } else
1199                 doc.printUnexpectedNodeWarning(cursor);
1200             /* print node error message (if any) */
1201             doc.printGeneralNodeError(cursor, result);
1202             /* proceed with next node */
1203             cursor.gotoNext();
1204         }
1205     }
1206     return result;
1207 }
1208 
1209 
readXMLVerifyingObserverData(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)1210 OFCondition DSRDocument::readXMLVerifyingObserverData(const DSRXMLDocument &doc,
1211                                                       DSRXMLCursor cursor,
1212                                                       const size_t flags)
1213 {
1214     OFCondition result = SR_EC_InvalidDocument;
1215     if (cursor.valid())
1216     {
1217         result = EC_Normal;
1218         /* iterate over all nodes */
1219         while (cursor.valid())
1220         {
1221             /* check for known element tags */
1222             if (doc.matchNode(cursor, "observer"))
1223             {
1224                 DcmItem *ditem = new DcmItem();
1225                 if (ditem != NULL)
1226                 {
1227                     OFString dateTimeString, nameString, orgaString;
1228                     DSRCodedEntryValue codeValue;
1229                     DSRXMLCursor childCursor = cursor.getChild();
1230                     /* iterate over all child nodes */
1231                     while (childCursor.valid())
1232                     {
1233                         /* check for known element tags */
1234                         if (doc.matchNode(childCursor, "code"))
1235                         {
1236                             /* Verifying Observer Code */
1237                             codeValue.readXML(doc, childCursor, flags);
1238                         }
1239                         else if (doc.matchNode(childCursor, "name"))
1240                         {
1241                             /* Verifying Observer Name */
1242                             DSRPNameTreeNode::getValueFromXMLNodeContent(doc, childCursor.getChild(), nameString);
1243                         }
1244                         else if (doc.matchNode(childCursor, "datetime"))
1245                         {
1246                             /* Verification DateTime */
1247                             DSRDateTimeTreeNode::getValueFromXMLNodeContent(doc, childCursor, dateTimeString);
1248                         } else {
1249                             /* Verifying Observer Organization */
1250                             doc.getStringFromNodeContent(childCursor, orgaString, "organization", OFTrue /*encoding*/, OFFalse /*clearString*/);
1251                         }
1252                         /* proceed with next node */
1253                         childCursor.gotoNext();
1254                     }
1255                     /* put string values into the sequence item */
1256                     putStringValueToDataset(*ditem, DCM_VerificationDateTime, dateTimeString);
1257                     putStringValueToDataset(*ditem, DCM_VerifyingObserverName, nameString);
1258                     putStringValueToDataset(*ditem, DCM_VerifyingOrganization, orgaString);
1259                     /* write code value to sequence item (might be empty, type 2) */
1260                     codeValue.writeSequence(*ditem, DCM_VerifyingObserverIdentificationCodeSequence);
1261                     /* insert items into sequence */
1262                     VerifyingObserver.insert(ditem);
1263                 }
1264             } else
1265                 doc.printUnexpectedNodeWarning(cursor);
1266             /* proceed with next node */
1267             cursor.gotoNext();
1268         }
1269     }
1270     return result;
1271 }
1272 
1273 
writeXML(STD_NAMESPACE ostream & stream,const size_t flags)1274 OFCondition DSRDocument::writeXML(STD_NAMESPACE ostream &stream,
1275                                   const size_t flags)
1276 {
1277     OFCondition result = SR_EC_InvalidDocument;
1278     /* only write valid documents */
1279     if (isValid())
1280     {
1281         DCMSR_DEBUG("Writing SR document to XML format");
1282         /* used for multiple purposes */
1283         OFString tmpString;
1284         /* update all DICOM attributes */
1285         updateAttributes();
1286         /* check whether general SR modules are used */
1287         const OFBool usesGeneralSRModules = usesSRDocumentGeneralModule(getDocumentType());
1288 
1289         // --- XML document structure (start) ---
1290 
1291         stream << "<?xml version=\"1.0\"";
1292         /* optional character set */
1293         tmpString = characterSetToXMLName(SpecificCharacterSetEnum);
1294         if (!tmpString.empty() && (tmpString != "?"))
1295             stream << " encoding=\"" << tmpString << "\"";
1296         else if (!SpecificCharacterSet.isEmpty())
1297             DCMSR_WARN("Cannot map Specific Character Set to equivalent XML encoding");
1298         stream << "?>" << OFendl;
1299 
1300         stream << "<report";
1301         /* optional namespace declaration */
1302         if (flags & XF_useDcmsrNamespace)
1303             stream << " xmlns=\"" << DCMSR_XML_NAMESPACE_URI << "\"";
1304         /* optional XML Schema reference */
1305         if (flags & XF_addSchemaReference)
1306         {
1307             if (flags & XF_useDcmsrNamespace)
1308                 stream << OFendl << "       ";
1309             stream << " xmlns:xsi=\"" XML_SCHEMA_INSTANCE_URI "\"" << OFendl << "       "
1310                    << " xsi:noNamespaceSchemaLocation=\"" DCMSR_XML_XSD_FILE "\"" << OFendl << "       ";
1311         }
1312         stream << " type=\"" << documentTypeToReadableName(getDocumentType()) << "\">" << OFendl;
1313 
1314         // --- write some general document information ---
1315 
1316         stream << "<sopclass uid=\"" << getMarkupStringFromElement(SOPClassUID, tmpString) << "\">";
1317         /* retrieve name of SOP class (if known) */
1318         stream << dcmFindNameOfUID(tmpString.c_str(), "" /* empty value as default */);
1319         stream << "</sopclass>" << OFendl;
1320         writeStringFromElementToXML(stream, SpecificCharacterSet, "charset", (flags & XF_writeEmptyTags) > 0);
1321         writeStringFromElementToXML(stream, TimezoneOffsetFromUTC, "timezone", (flags & XF_writeEmptyTags) > 0);
1322         writeStringFromElementToXML(stream, Modality, "modality", (flags & XF_writeEmptyTags) > 0);
1323         /* check for additional device information */
1324         if (!ManufacturerModelName.isEmpty())
1325         {
1326             stream << "<device>" << OFendl;
1327             writeStringFromElementToXML(stream, Manufacturer, "manufacturer", (flags & XF_writeEmptyTags) > 0);
1328             writeStringFromElementToXML(stream, ManufacturerModelName, "model", (flags & XF_writeEmptyTags) > 0);
1329             writeStringFromElementToXML(stream, DeviceSerialNumber, "serial", (flags & XF_writeEmptyTags) > 0);
1330             writeStringFromElementToXML(stream, SoftwareVersions, "version", (flags & XF_writeEmptyTags) > 0);
1331             stream << "</device>" << OFendl;
1332         } else
1333             writeStringFromElementToXML(stream, Manufacturer, "manufacturer", (flags & XF_writeEmptyTags) > 0);
1334 
1335         if ((flags & XF_writeEmptyTags) || !SynchronizationFrameOfReferenceUID.isEmpty() ||
1336             !SynchronizationTrigger.isEmpty() || !AcquisitionTimeSynchronized.isEmpty())
1337         {
1338             stream << "<synchronization";
1339             if (!SynchronizationFrameOfReferenceUID.isEmpty())
1340                 stream << " uid=\"" << getMarkupStringFromElement(SynchronizationFrameOfReferenceUID, tmpString) << "\"";
1341             stream << ">" << OFendl;
1342             writeStringFromElementToXML(stream, SynchronizationTrigger, "trigger", (flags & XF_writeEmptyTags) > 0);
1343             writeStringFromElementToXML(stream, AcquisitionTimeSynchronized, "acquisitiontime", (flags & XF_writeEmptyTags) > 0);
1344             stream << "</synchronization>" << OFendl;
1345         }
1346 
1347         if ((flags & XF_writeEmptyTags) || !ReferringPhysicianName.isEmpty())
1348         {
1349             stream << "<referringphysician>" << OFendl;
1350             writeStringFromElementToXML(stream, ReferringPhysicianName, "name", (flags & XF_writeEmptyTags) > 0);
1351             stream << "</referringphysician>" << OFendl;
1352         }
1353 
1354         stream << "<patient>" << OFendl;
1355         writeStringFromElementToXML(stream, PatientID, "id", (flags & XF_writeEmptyTags) > 0);
1356         writeStringFromElementToXML(stream, IssuerOfPatientID, "issuer", (flags & XF_writeEmptyTags) > 0);
1357         writeStringFromElementToXML(stream, PatientName, "name", (flags & XF_writeEmptyTags) > 0);
1358         if ((flags & XF_writeEmptyTags) || !PatientBirthDate.isEmpty())
1359         {
1360             stream << "<birthday>" << OFendl;
1361             PatientBirthDate.getISOFormattedDate(tmpString);
1362             writeStringValueToXML(stream, tmpString, "date", (flags & XF_writeEmptyTags) > 0);
1363             stream << "</birthday>" << OFendl;
1364         }
1365         writeStringFromElementToXML(stream, PatientSex, "sex", (flags & XF_writeEmptyTags) > 0);
1366         stream << "</patient>" << OFendl;
1367 
1368         stream << "<study uid=\"" << getMarkupStringFromElement(StudyInstanceUID, tmpString) << "\">" << OFendl;
1369         writeStringFromElementToXML(stream, StudyID, "id", (flags & XF_writeEmptyTags) > 0);
1370         StudyDate.getISOFormattedDate(tmpString);
1371         writeStringValueToXML(stream, tmpString, "date", (flags & XF_writeEmptyTags) > 0);
1372         StudyTime.getISOFormattedTime(tmpString);
1373         writeStringValueToXML(stream, tmpString, "time", (flags & XF_writeEmptyTags) > 0);
1374         if ((flags & XF_writeEmptyTags) || !AccessionNumber.isEmpty())
1375         {
1376             stream << "<accession>" << OFendl;
1377             writeStringFromElementToXML(stream, AccessionNumber, "number", (flags & XF_writeEmptyTags) > 0);
1378             stream << "</accession>" << OFendl;
1379         }
1380         writeStringFromElementToXML(stream, StudyDescription, "description", (flags & XF_writeEmptyTags) > 0);
1381         stream << "</study>" << OFendl;
1382 
1383         stream << "<series uid=\"" << getMarkupStringFromElement(SeriesInstanceUID, tmpString) << "\">" << OFendl;
1384         writeStringFromElementToXML(stream, SeriesNumber, "number", (flags & XF_writeEmptyTags) > 0);
1385         SeriesDate.getISOFormattedDate(tmpString);
1386         writeStringValueToXML(stream, tmpString, "date", (flags & XF_writeEmptyTags) > 0);
1387         SeriesTime.getISOFormattedTime(tmpString);
1388         writeStringValueToXML(stream, tmpString, "time", (flags & XF_writeEmptyTags) > 0);
1389         writeStringFromElementToXML(stream, ProtocolName, "protocol", (flags & XF_writeEmptyTags) > 0);
1390         writeStringFromElementToXML(stream, SeriesDescription, "description", (flags & XF_writeEmptyTags) > 0);
1391         stream << "</series>" << OFendl;
1392 
1393         stream << "<instance uid=\"" << getMarkupStringFromElement(SOPInstanceUID, tmpString) << "\">" << OFendl;
1394         writeStringFromElementToXML(stream, InstanceNumber, "number", (flags & XF_writeEmptyTags) > 0);
1395         if ((flags & XF_writeEmptyTags) || !InstanceCreatorUID.isEmpty() ||
1396             !InstanceCreationDate.isEmpty() || !InstanceCreationTime.isEmpty())
1397         {
1398             stream << "<creation";
1399             if (!InstanceCreatorUID.isEmpty())
1400                 stream << " uid=\"" << getMarkupStringFromElement(InstanceCreatorUID, tmpString) << "\"";
1401             stream << ">" << OFendl;
1402             InstanceCreationDate.getISOFormattedDate(tmpString);
1403             writeStringValueToXML(stream, tmpString, "date", (flags & XF_writeEmptyTags) > 0);
1404             InstanceCreationTime.getISOFormattedTime(tmpString);
1405             writeStringValueToXML(stream, tmpString, "time", (flags & XF_writeEmptyTags) > 0);
1406             stream << "</creation>" << OFendl;
1407         }
1408         stream << "</instance>" << OFendl;
1409 
1410         if ((flags & XF_writeEmptyTags) || !CodingSchemeIdentification.isEmpty())
1411         {
1412             stream << "<coding>" << OFendl;
1413             CodingSchemeIdentification.writeXML(stream, flags);
1414             stream << "</coding>" << OFendl;
1415         }
1416         if ((flags & XF_writeEmptyTags) || !CurrentRequestedProcedureEvidence.isEmpty())
1417         {
1418             stream << "<evidence type=\"Current Requested Procedure\">" << OFendl;
1419             CurrentRequestedProcedureEvidence.writeXML(stream, flags);
1420             stream << "</evidence>" << OFendl;
1421         }
1422         if (usesGeneralSRModules)
1423         {
1424             if ((flags & XF_writeEmptyTags) || !PertinentOtherEvidence.isEmpty())
1425             {
1426                 stream << "<evidence type=\"Pertinent Other\">" << OFendl;
1427                 PertinentOtherEvidence.writeXML(stream, flags);
1428                 stream << "</evidence>" << OFendl;
1429             }
1430             if ((flags & XF_writeEmptyTags) || !ReferencedInstances.isEmpty())
1431             {
1432                 stream << "<reference>" << OFendl;
1433                 ReferencedInstances.writeXML(stream, flags);
1434                 stream << "</reference>" << OFendl;
1435             }
1436         }
1437 
1438         stream << "<document>" << OFendl;
1439         if (usesGeneralSRModules)
1440         {
1441             if (!PreliminaryFlag.isEmpty())
1442                 stream << "<preliminary flag=\"" << getStringValueFromElement(PreliminaryFlag, tmpString) << "\"/>" << OFendl;
1443             stream << "<completion flag=\"" << getStringValueFromElement(CompletionFlag, tmpString) << "\">" << OFendl;
1444             writeStringFromElementToXML(stream, CompletionFlagDescription, "description", (flags & XF_writeEmptyTags) > 0);
1445             stream << "</completion>" << OFendl;
1446 
1447             stream << "<verification flag=\"" << getStringValueFromElement(VerificationFlag, tmpString) << "\">" << OFendl;
1448             const size_t obsCount = getNumberOfVerifyingObservers();
1449             for (size_t i = 1; i <= obsCount; i++)
1450             {
1451                 stream << "<observer pos=\"" << i << "\">" << OFendl;
1452                 DSRCodedEntryValue obsCode;
1453                 OFString dateTime, obsName, organization;
1454                 if (getVerifyingObserver(i, dateTime, obsName, obsCode, organization).good())
1455                 {
1456                     /* output time in ISO 8601 format */
1457                     DcmDateTime::getISOFormattedDateTimeFromString(dateTime, tmpString, OFTrue /*seconds*/, OFFalse /*fraction*/,
1458                         OFTrue /*timeZone*/, OFFalse /*createMissingPart*/, "T" /*dateTimeSeparator*/, "" /*timeZoneSeparator*/);
1459                     writeStringValueToXML(stream, tmpString, "datetime", (flags & XF_writeEmptyTags) > 0);
1460                     if (!obsName.empty() || (flags & XF_writeEmptyTags))
1461                         stream << "<name>" << OFendl << dicomToXMLPersonName(obsName, tmpString) << OFendl << "</name>" << OFendl;
1462                     if (obsCode.isValid())
1463                     {
1464                         if (flags & DSRTypes::XF_codeComponentsAsAttribute)
1465                             stream << "<code";     // bracket ">" is closed in next writeXML() call
1466                         else
1467                             stream << "<code>" << OFendl;
1468                         obsCode.writeXML(stream, flags);
1469                         stream << "</code>" << OFendl;
1470                     }
1471                     writeStringValueToXML(stream, organization, "organization", (flags & XF_writeEmptyTags) > 0);
1472                 }
1473                 stream << "</observer>" << OFendl;
1474             }
1475             stream << "</verification>" << OFendl;
1476 
1477             if ((flags & XF_writeEmptyTags) || !PredecessorDocuments.isEmpty())
1478             {
1479                 stream << "<predecessor>" << OFendl;
1480                 PredecessorDocuments.writeXML(stream, flags);
1481                 stream << "</predecessor>" << OFendl;
1482             }
1483         }
1484         if ((flags & XF_writeEmptyTags) || !IdenticalDocuments.isEmpty())
1485         {
1486             stream << "<identical>" << OFendl;
1487             IdenticalDocuments.writeXML(stream, flags);
1488             stream << "</identical>" << OFendl;
1489         }
1490 
1491         // --- write document content/tree to stream ---
1492 
1493         stream << "<content>" << OFendl;
1494         ContentDate.getISOFormattedDate(tmpString);
1495         writeStringValueToXML(stream, tmpString, "date", (flags & XF_writeEmptyTags) > 0);
1496         ContentTime.getISOFormattedTime(tmpString);
1497         writeStringValueToXML(stream, tmpString, "time", (flags & XF_writeEmptyTags) > 0);
1498         result = DocumentTree.writeXML(stream, flags);
1499         stream << "</content>" << OFendl;
1500         stream << "</document>" << OFendl;
1501 
1502         // --- XML document structure (end) ---
1503 
1504         stream << "</report>" << OFendl;
1505     }
1506     return result;
1507 }
1508 
1509 
renderHTMLPatientData(STD_NAMESPACE ostream & stream,const size_t flags)1510 void DSRDocument::renderHTMLPatientData(STD_NAMESPACE ostream &stream,
1511                                         const size_t flags)
1512 {
1513     OFString tmpString, string2;
1514     OFString htmlString;
1515     stream << convertToHTMLString(dicomToReadablePersonName(getStringValueFromElement(PatientName, tmpString), string2), htmlString, flags);
1516     OFString patientStr;
1517     if (!PatientSex.isEmpty())
1518     {
1519         getPrintStringFromElement(PatientSex, tmpString);
1520         if (tmpString == "M")
1521             patientStr += "male";
1522         else if (tmpString == "F")
1523             patientStr += "female";
1524         else if (tmpString == "O")
1525             patientStr += "other";
1526         else
1527             patientStr += convertToHTMLString(tmpString, htmlString, flags);
1528     }
1529     if (!PatientBirthDate.isEmpty())
1530     {
1531         if (!patientStr.empty())
1532             patientStr += ", ";
1533         patientStr += '*';
1534         patientStr += dicomToReadableDate(getStringValueFromElement(PatientBirthDate, tmpString), string2);
1535     }
1536     if (!PatientID.isEmpty())
1537     {
1538         if (!patientStr.empty())
1539             patientStr += ", ";
1540         patientStr += '#';
1541         patientStr += convertToHTMLString(getStringValueFromElement(PatientID, tmpString), htmlString, flags);
1542         if (!IssuerOfPatientID.isEmpty())
1543         {
1544             patientStr += ":";
1545             patientStr += convertToHTMLString(getStringValueFromElement(IssuerOfPatientID, tmpString), htmlString, flags);
1546         }
1547     }
1548     if (!patientStr.empty())
1549         stream << " (" << patientStr << ")";
1550 }
1551 
1552 
renderHTMLReferenceList(STD_NAMESPACE ostream & stream,DSRSOPInstanceReferenceList & refList,const size_t flags)1553 void DSRDocument::renderHTMLReferenceList(STD_NAMESPACE ostream &stream,
1554                                           DSRSOPInstanceReferenceList &refList,
1555                                           const size_t flags)
1556 {
1557     /* goto first list item (if not empty) */
1558     if (refList.gotoFirstItem().good())
1559     {
1560         OFString tmpString;
1561         DSRCodedEntryValue codeValue;
1562         unsigned int i = 0;
1563         /* iterate over all list items */
1564         do {
1565             if (i > 0)
1566             {
1567                 stream << "</tr>" << OFendl;
1568                 stream << "<tr>" << OFendl;
1569                 stream << "<td></td>" << OFendl;
1570             }
1571             /* hyperlink to composite object */
1572             OFString sopClass, sopInstance;
1573             if (!refList.getSOPClassUID(sopClass).empty() && !refList.getSOPInstanceUID(sopInstance).empty())
1574             {
1575                 stream << "<td><a href=\"" << HTML_HYPERLINK_PREFIX_FOR_CGI;
1576                 stream << "?composite=" << sopClass << "+" << sopInstance << "\">";
1577                 /* check whether referenced object has a well-known SOP class */
1578                 stream << dcmFindNameOfUID(sopClass.c_str(), "unknown composite object");
1579                 stream << "</a>";
1580                 /* try to get the purpose of reference code, which is optional */
1581                 if (refList.getPurposeOfReference(codeValue).good() && !codeValue.getCodeMeaning().empty())
1582                     stream << " (" << DSRTypes::convertToHTMLString(codeValue.getCodeMeaning(), tmpString, flags) << ")";
1583                 stream << "</td>" << OFendl;
1584             } else
1585                 stream << "<td><i>invalid object reference</i></td>" << OFendl;
1586             i++;
1587         } while (refList.gotoNextItem().good());
1588     }
1589 }
1590 
1591 
renderHTMLReferenceList(STD_NAMESPACE ostream & stream,DSRReferencedInstanceList & refList,const size_t flags)1592 void DSRDocument::renderHTMLReferenceList(STD_NAMESPACE ostream &stream,
1593                                           DSRReferencedInstanceList &refList,
1594                                           const size_t flags)
1595 {
1596     /* goto first list item (if not empty) */
1597     if (refList.gotoFirstItem().good())
1598     {
1599         OFString tmpString;
1600         DSRCodedEntryValue codeValue;
1601         unsigned int i = 0;
1602         /* iterate over all list items */
1603         do {
1604             if (i > 0)
1605             {
1606                 stream << "</tr>" << OFendl;
1607                 stream << "<tr>" << OFendl;
1608                 stream << "<td></td>" << OFendl;
1609             }
1610             /* hyperlink to composite object */
1611             OFString sopClass, sopInstance;
1612             if (!refList.getSOPClassUID(sopClass).empty() && !refList.getSOPInstanceUID(sopInstance).empty())
1613             {
1614                 stream << "<td><a href=\"" << HTML_HYPERLINK_PREFIX_FOR_CGI;
1615                 stream << "?composite=" << sopClass << "+" << sopInstance << "\">";
1616                 /* retrieve name of SOP class (if known) */
1617                 stream << dcmFindNameOfUID(sopClass.c_str(), "unknown composite object");
1618                 stream << "</a>";
1619                 /* try to get the purpose of reference code (at least the code meaning) */
1620                 if (refList.getPurposeOfReference(codeValue).good() && !codeValue.getCodeMeaning().empty())
1621                     stream << " (" << DSRTypes::convertToHTMLString(codeValue.getCodeMeaning(), tmpString, flags) << ")";
1622                 stream << "</td>" << OFendl;
1623             } else
1624                 stream << "<td><i>invalid reference</i></td>" << OFendl;
1625             i++;
1626         } while (refList.gotoNextItem().good());
1627     }
1628 }
1629 
1630 
renderHTML(STD_NAMESPACE ostream & stream,const size_t flags,const char * styleSheet)1631 OFCondition DSRDocument::renderHTML(STD_NAMESPACE ostream &stream,
1632                                     const size_t flags,
1633                                     const char *styleSheet)
1634 {
1635     OFCondition result = SR_EC_InvalidDocument;
1636     /* only render valid documents */
1637     if (isValid())
1638     {
1639         size_t newFlags = flags;
1640         if (flags & HF_XHTML11Compatibility)
1641             newFlags = (flags & ~DSRTypes::HF_HTML32Compatibility);
1642         else if (flags & HF_HTML32Compatibility)
1643         {
1644             /* fixes for HTML 3.2 */
1645             newFlags = (flags & ~HF_useCodeDetailsTooltip) | HF_convertNonASCIICharacters;
1646             /* ignore CSS (if any) */
1647             styleSheet = NULL;
1648         }
1649 
1650         /* used for multiple purposes */
1651         OFString tmpString, string2;
1652         /* used for HTML tmpString conversion */
1653         OFString htmlString;
1654         /* update only some DICOM attributes */
1655         updateAttributes(OFFalse /*updateAll*/);
1656         /* check whether general SR modules are used */
1657         const OFBool usesGeneralSRModules = usesSRDocumentGeneralModule(getDocumentType());
1658 
1659         // --- HTML/XHTML document structure (start) ---
1660 
1661         if (newFlags & HF_XHTML11Compatibility)
1662         {
1663             stream << "<?xml version=\"1.0\"";
1664             /* optional character set */
1665             tmpString = characterSetToXMLName(SpecificCharacterSetEnum);
1666             if (!tmpString.empty())
1667             {
1668                 if (tmpString != "?")
1669                     stream << " encoding=\"" << tmpString << "\"";
1670                 else
1671                     DCMSR_WARN("Cannot map Specific Character Set to equivalent XML encoding");
1672             }
1673             stream << "?>" << OFendl;
1674         }
1675 
1676         /* optional document type definition */
1677         if (newFlags & HF_addDocumentTypeReference)
1678         {
1679             if (newFlags & HF_XHTML11Compatibility)
1680                 stream << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">" << OFendl;
1681             else {
1682                 if (newFlags & HF_HTML32Compatibility)
1683                     stream << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">" << OFendl;
1684                 else
1685                     stream << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" << OFendl;
1686             }
1687         }
1688         stream << "<html";
1689         if (newFlags & HF_XHTML11Compatibility)
1690             stream << " xmlns=\"http://www.w3.org/1999/xhtml\"";
1691         stream << ">" << OFendl;
1692         stream << "<head>" << OFendl;
1693         /* document title */
1694         stream << "<title>";
1695         if (newFlags & HF_renderPatientTitle)
1696             renderHTMLPatientData(stream, newFlags);
1697         else
1698             stream << documentTypeToDocumentTitle(getDocumentType(), tmpString);
1699         stream << "</title>" << OFendl;
1700         /* for HTML 4.01 and XHTML 1.1 only */
1701         if (!(newFlags & HF_HTML32Compatibility))
1702         {
1703             /* optional cascading style sheet */
1704             if (styleSheet != NULL)
1705             {
1706                 if (newFlags & HF_copyStyleSheetContent)
1707                 {
1708                     /* copy content from CSS file */
1709                     STD_NAMESPACE ifstream cssFile(styleSheet, OFopenmode_in_nocreate);
1710                     if (cssFile)
1711                     {
1712                         char c;
1713                         stream << "<style type=\"text/css\">" << OFendl;
1714                         stream << "<!--" << OFendl;
1715                         /* copy all characters */
1716                         while (cssFile.get(c))
1717                             stream << c;
1718                         stream << "//-->" << OFendl;
1719                         stream << "</style>" << OFendl;
1720                     } else
1721                         DCMSR_WARN("Could not open CSS file \"" << styleSheet << "\" ... ignoring");
1722                 } else {
1723                     /* just add a reference to the CSS file (might be an URL) */
1724                     stream << "<link rel=\"stylesheet\" type=\"text/css\" href=\"" << styleSheet << "\"";
1725                     if (newFlags & HF_XHTML11Compatibility)
1726                         stream << " /";
1727                     stream << ">" << OFendl;
1728                 }
1729             }
1730             /* optional character set */
1731             tmpString = characterSetToHTMLName(SpecificCharacterSetEnum);
1732             if (!tmpString.empty())
1733             {
1734                 if (tmpString != "?")
1735                 {
1736                     stream << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" << tmpString << "\"";
1737                     if (newFlags & HF_XHTML11Compatibility)
1738                         stream << " /";
1739                     stream << ">" << OFendl;
1740                 } else
1741                     DCMSR_WARN("Cannot map Specific Character Set to equivalent HTML charset");
1742             }
1743         }
1744         /* generator meta element referring to the DCMTK */
1745         if (!(newFlags & HF_omitGeneratorMetaElement))
1746         {
1747             stream << "<meta name=\"generator\" content=\"OFFIS DCMTK " << OFFIS_DCMTK_VERSION << "\"";
1748             if (newFlags & HF_XHTML11Compatibility)
1749                 stream << " /";
1750             stream << ">" << OFendl;
1751         }
1752         stream << "</head>" << OFendl;
1753         stream << "<body>" << OFendl;
1754 
1755         // --- render some general document information ---
1756 
1757         if (!(newFlags & HF_renderNoDocumentHeader))
1758         {
1759             /* create a table for this purpose */
1760             stream << "<table>" << OFendl;
1761             /* patient related information */
1762             if (!PatientName.isEmpty())
1763             {
1764                 stream << "<tr>" << OFendl;
1765                 stream << "<td><b>Patient:</b></td>" << OFendl;
1766                 stream << "<td>";
1767                 renderHTMLPatientData(stream, newFlags);
1768                 stream << "</td>" << OFendl;
1769                 stream << "</tr>" << OFendl;
1770             }
1771             /* referring physician */
1772             if (!ReferringPhysicianName.isEmpty())
1773             {
1774                 stream << "<tr>" << OFendl;
1775                 stream << "<td><b>Referring Physician:</b></td>" << OFendl;
1776                 stream << "<td>" << convertToHTMLString(dicomToReadablePersonName(getStringValueFromElement(ReferringPhysicianName, tmpString), string2), htmlString, newFlags);
1777                 stream << "</td>" << OFendl;
1778                 stream << "</tr>" << OFendl;
1779             }
1780             /* study-related information */
1781             if (!StudyDescription.isEmpty())
1782             {
1783                 stream << "<tr>" << OFendl;
1784                 stream << "<td><b>Study:</b></td>" << OFendl;
1785                 stream << "<td>" << convertToHTMLString(getStringValueFromElement(StudyDescription, tmpString), htmlString, newFlags);
1786                 if (!StudyID.isEmpty())
1787                     stream << " (#" << convertToHTMLString(getStringValueFromElement(StudyID, tmpString), htmlString, newFlags) << ")";
1788                 stream << "</td>" << OFendl;
1789                 stream << "</tr>" << OFendl;
1790             }
1791             /* series-related information */
1792             if (!SeriesDescription.isEmpty())
1793             {
1794                 stream << "<tr>" << OFendl;
1795                 stream << "<td><b>Series:</b></td>" << OFendl;
1796                 stream << "<td>" << convertToHTMLString(getStringValueFromElement(SeriesDescription, tmpString), htmlString, newFlags);
1797                 if (!SeriesNumber.isEmpty())
1798                     stream << " (#" << convertToHTMLString(getStringValueFromElement(SeriesNumber, tmpString), htmlString, newFlags) << ")";
1799                 stream << "</td>" << OFendl;
1800                 stream << "</tr>" << OFendl;
1801             }
1802             /* protocol name */
1803             if (!ProtocolName.isEmpty())
1804             {
1805                 stream << "<tr>" << OFendl;
1806                 stream << "<td><b>Protocol:</b></td>" << OFendl;
1807                 stream << "<td>" << convertToHTMLString(getStringValueFromElement(ProtocolName, tmpString), htmlString, newFlags) << "</td>" << OFendl;
1808                 stream << "</tr>" << OFendl;
1809             }
1810             /* manufacturer */
1811             if (!Manufacturer.isEmpty())
1812             {
1813                 stream << "<tr>" << OFendl;
1814                 stream << "<td><b>Manufacturer:</b></td>" << OFendl;
1815                 stream << "<td>" << convertToHTMLString(getStringValueFromElement(Manufacturer, tmpString), htmlString, newFlags);
1816                 OFString deviceStr;
1817                 if (!ManufacturerModelName.isEmpty())
1818                     deviceStr += convertToHTMLString(getStringValueFromElement(ManufacturerModelName, tmpString), htmlString, newFlags);
1819                 if (!DeviceSerialNumber.isEmpty())
1820                 {
1821                    if (!deviceStr.empty())
1822                        deviceStr += ", ";
1823                    deviceStr += '#';
1824                    deviceStr += convertToHTMLString(getStringValueFromElement(DeviceSerialNumber, tmpString), htmlString, newFlags);
1825                 }
1826                 if (!deviceStr.empty())
1827                     stream << " (" << deviceStr << ")";
1828                 stream << "</td>" << OFendl;
1829                 stream << "</tr>" << OFendl;
1830             }
1831             if (usesGeneralSRModules)
1832             {
1833                 /* preliminary flag */
1834                 if (!PreliminaryFlag.isEmpty())
1835                 {
1836                     stream << "<tr>" << OFendl;
1837                     stream << "<td><b>Preliminary Flag:</b></td>" << OFendl;
1838                     stream << "<td>" << getStringValueFromElement(PreliminaryFlag, tmpString) << "</td>" << OFendl;
1839                     stream << "</tr>" << OFendl;
1840                 }
1841                 /* completion flag */
1842                 stream << "<tr>" << OFendl;
1843                 stream << "<td><b>Completion Flag:</b></td>" << OFendl;
1844                 stream << "<td>" << getStringValueFromElement(CompletionFlag, tmpString) << "</td>" << OFendl;
1845                 stream << "</tr>" << OFendl;
1846                 /* completion flag description */
1847                 if (!CompletionFlagDescription.isEmpty())
1848                 {
1849                     stream << "<tr>" << OFendl;
1850                     stream << "<td></td>" << OFendl;
1851                     stream << "<td>" << convertToHTMLString(getStringValueFromElement(CompletionFlagDescription, tmpString), htmlString, newFlags);
1852                     stream << "</td>" << OFendl;
1853                     stream << "</tr>" << OFendl;
1854                 }
1855                 /* predecessor documents */
1856                 if (!PredecessorDocuments.isEmpty())
1857                 {
1858                     stream << "<tr>" << OFendl;
1859                     stream << "<td><b>Predecessor Docs:</b></td>" << OFendl;
1860                     renderHTMLReferenceList(stream, PredecessorDocuments, flags);
1861                     stream << "</tr>" << OFendl;
1862                 }
1863             }
1864             /* identical documents */
1865             if (!IdenticalDocuments.isEmpty())
1866             {
1867                 stream << "<tr>" << OFendl;
1868                 stream << "<td><b>Identical Docs:</b></td>" << OFendl;
1869                 renderHTMLReferenceList(stream, IdenticalDocuments, flags);
1870                 stream << "</tr>" << OFendl;
1871             }
1872             /* referenced instances */
1873             if (!ReferencedInstances.isEmpty())
1874             {
1875                 stream << "<tr>" << OFendl;
1876                 stream << "<td><b>Referenced Objects:</b></td>" << OFendl;
1877                 renderHTMLReferenceList(stream, ReferencedInstances, flags);
1878                 stream << "</tr>" << OFendl;
1879             }
1880             if (usesGeneralSRModules)
1881             {
1882                 /* verification flag */
1883                 stream << "<tr>" << OFendl;
1884                 stream << "<td><b>Verification Flag:</b></td>" << OFendl;
1885                 stream << "<td>" << getStringValueFromElement(VerificationFlag, tmpString) << "</td>" << OFendl;
1886                 stream << "</tr>" << OFendl;
1887                 /* verifying observer */
1888                 const size_t obsCount = getNumberOfVerifyingObservers();
1889                 for (size_t i = 1; i <= obsCount; i++)
1890                 {
1891                     OFString dateTime, obsName, organization;
1892                     DSRCodedEntryValue obsCode;
1893                     if (getVerifyingObserver(i, dateTime, obsName, obsCode, organization).good())
1894                     {
1895                         stream << "<tr>" << OFendl;
1896                         if (i == 1)
1897                             stream << "<td><b>Verifying Observers:</b></td>" << OFendl;
1898                         else
1899                             stream << "<td></td>" << OFendl;
1900                         stream << "<td>";
1901                         stream << dicomToReadableDateTime(dateTime, string2) << " - ";
1902                         /* render code details as a tooltip (HTML 4.01 or above) */
1903                         if (obsCode.isValid() && (newFlags & DSRTypes::HF_useCodeDetailsTooltip))
1904                         {
1905                             if (newFlags & HF_XHTML11Compatibility)
1906                                 stream << "<span class=\"code\" title=\"(";
1907                             else /* HTML 4.01 */
1908                                 stream << "<span class=\"under\" title=\"(";
1909                             stream << convertToHTMLString(obsCode.getCodeValue(), htmlString, newFlags) << ", ";
1910                             stream << convertToHTMLString(obsCode.getCodingSchemeDesignator(), htmlString, newFlags);
1911                             if (!obsCode.getCodingSchemeVersion().empty())
1912                                 stream << " [" << convertToHTMLString(obsCode.getCodingSchemeVersion(), htmlString, newFlags) << "]";
1913                             stream << ", &quot;" << convertToHTMLString(obsCode.getCodeMeaning(), htmlString, newFlags) << "&quot;)\">";
1914                         }
1915                         stream << convertToHTMLString(dicomToReadablePersonName(obsName, string2), htmlString, newFlags);
1916                         if (obsCode.isValid() && (newFlags & DSRTypes::HF_useCodeDetailsTooltip))
1917                             stream << "</span>";
1918                         else {
1919                             /* render code in a conventional manner */
1920                             if (obsCode.isValid() && ((newFlags & HF_renderAllCodes) == HF_renderAllCodes))
1921                             {
1922                                 stream << " (" << convertToHTMLString(obsCode.getCodeValue(), htmlString, newFlags);
1923                                 stream << ", " << convertToHTMLString(obsCode.getCodingSchemeDesignator(), htmlString, newFlags);
1924                                 if (!obsCode.getCodingSchemeVersion().empty())
1925                                     stream << " [" << convertToHTMLString(obsCode.getCodingSchemeVersion(), htmlString, newFlags) << "]";
1926                                 stream << ", &quot;" << convertToHTMLString(obsCode.getCodeMeaning(), htmlString, newFlags) << "&quot;)";
1927                             }
1928                         }
1929                         stream << ", " << convertToHTMLString(organization, htmlString, newFlags);
1930                         stream << "</td>" << OFendl;
1931                         stream << "</tr>" << OFendl;
1932                     }
1933                 }
1934             }
1935             if (!ContentDate.isEmpty() && !ContentTime.isEmpty())
1936             {
1937                 /* content date and time */
1938                 stream << "<tr>" << OFendl;
1939                 stream << "<td><b>Content Date/Time:</b></td>" << OFendl;
1940                 stream << "<td>" << dicomToReadableDate(getStringValueFromElement(ContentDate, tmpString), string2) << " ";
1941                 stream << dicomToReadableTime(getStringValueFromElement(ContentTime, tmpString), string2) << "</td>" << OFendl;
1942                 stream << "</tr>" << OFendl;
1943             }
1944             /* end of table */
1945             stream << "</table>" << OFendl;
1946 
1947             if (newFlags & HF_XHTML11Compatibility)
1948                 stream << "<hr />" << OFendl;
1949             else
1950                 stream << "<hr>" << OFendl;
1951         }
1952 
1953         // --- render document tree to stream ---
1954 
1955         /* create memory output stream for the annex */
1956         OFOStringStream annexStream;
1957         /* render document tree two the streams */
1958         result = DocumentTree.renderHTML(stream, annexStream, newFlags);
1959         /* append annex (with heading) to main document */
1960         if (result.good())
1961             result = appendStream(stream, annexStream, "<h1>Annex</h1>");
1962 
1963         // --- footnote ---
1964 
1965         if (newFlags & HF_renderDcmtkFootnote)
1966         {
1967             if (newFlags & HF_XHTML11Compatibility)
1968             {
1969                 stream << "<hr />" << OFendl;
1970                 stream << "<div class=\"footnote\">" << OFendl;
1971             } else {
1972                 stream << "<hr>" << OFendl;
1973                 if (newFlags & HF_HTML32Compatibility)
1974                     stream << "<div>" << OFendl;
1975                 else /* HTML 4.01 */
1976                     stream << "<div class=\"footnote\">" << OFendl;
1977                 stream << "<small>" << OFendl;
1978             }
1979             stream << "This page was generated from a DICOM Structured Reporting document by ";
1980             stream << "<a href=\"" << DCMTK_INTERNET_URL << "\">OFFIS DCMTK</a> " << OFFIS_DCMTK_VERSION << "." << OFendl;
1981             if (!(newFlags & HF_XHTML11Compatibility))
1982                 stream << "</small>" << OFendl;
1983             stream << "</div>" << OFendl;
1984         }
1985 
1986         // --- HTML document structure (end) ---
1987 
1988         stream << "</body>" << OFendl;
1989         stream << "</html>" << OFendl;
1990     }
1991     return result;
1992 }
1993 
1994 
getDocumentType() const1995 DSRTypes::E_DocumentType DSRDocument::getDocumentType() const
1996 {
1997     return DocumentTree.getDocumentType();
1998 }
1999 
2000 
setTree(const DSRDocumentTree & tree)2001 OFCondition DSRDocument::setTree(const DSRDocumentTree &tree)
2002 {
2003     OFCondition result = SR_EC_InvalidDocumentTree;
2004     /* make sure that the new tree is valid */
2005     if (tree.isValid())
2006     {
2007         /* replace current tree with given one */
2008         DocumentTree = tree;
2009         /* update IOD-specific DICOM attributes */
2010         updateAttributes(OFFalse /*updateAll*/);
2011         result = EC_Normal;
2012     }
2013     return result;
2014 }
2015 
2016 
setTreeFromRootTemplate(DSRRootTemplate & rootTemplate,const OFBool expandTree)2017 OFCondition DSRDocument::setTreeFromRootTemplate(DSRRootTemplate &rootTemplate,
2018                                                  const OFBool expandTree)
2019 {
2020     OFCondition result = EC_Normal;
2021     /* check whether to expand the included templates (if any) */
2022     if (expandTree)
2023     {
2024         DSRDocumentSubTree *tree = NULL;
2025         /* expand tree managed by the template and replace the currently stored tree */
2026         result = rootTemplate.getTree().createExpandedSubTree(tree);
2027         if (result.good())
2028             result = DocumentTree.changeDocumentType(rootTemplate.getDocumentType(), OFTrue /*deleteTree*/);
2029         if (result.good())
2030             result = DocumentTree.insertSubTree(tree, AM_belowCurrent, RT_unknown, OFFalse /*deleteIfFail*/);
2031         /* update IOD-specific DICOM attributes */
2032         updateAttributes(OFFalse /*updateAll*/);
2033         /* in case of error, free memory */
2034         if (result.bad())
2035             delete tree;
2036     } else {
2037         /* call the functions doing the real work */
2038         result = setTree(rootTemplate.getTree());
2039     }
2040     return result;
2041 }
2042 
2043 
getSpecificCharacterSet() const2044 const char *DSRDocument::getSpecificCharacterSet() const
2045 {
2046     /* never return NULL */
2047     return OFSTRING_GUARD(getStringValueFromElement(SpecificCharacterSet));
2048 }
2049 
2050 
getSpecificCharacterSetType() const2051 DSRTypes::E_CharacterSet DSRDocument::getSpecificCharacterSetType() const
2052 {
2053     return SpecificCharacterSetEnum;
2054 }
2055 
2056 
setSpecificCharacterSetType(const E_CharacterSet characterSet)2057 OFCondition DSRDocument::setSpecificCharacterSetType(const E_CharacterSet characterSet)
2058 {
2059     SpecificCharacterSetEnum = characterSet;
2060     return SpecificCharacterSet.putString(characterSetToDefinedTerm(SpecificCharacterSetEnum));
2061 }
2062 
2063 
getPreliminaryFlag() const2064 DSRTypes::E_PreliminaryFlag DSRDocument::getPreliminaryFlag() const
2065 {
2066     return PreliminaryFlagEnum;
2067 }
2068 
2069 
setPreliminaryFlag(const E_PreliminaryFlag flag)2070 OFCondition DSRDocument::setPreliminaryFlag(const E_PreliminaryFlag flag)
2071 {
2072     OFCondition result = EC_IllegalCall;
2073     /* not all SR IODs contain the SR Document General Module */
2074     if (usesSRDocumentGeneralModule(getDocumentType()))
2075     {
2076         PreliminaryFlagEnum = flag;
2077         result = EC_Normal;
2078     }
2079     return result;
2080 }
2081 
2082 
getCompletionFlag() const2083 DSRTypes::E_CompletionFlag DSRDocument::getCompletionFlag() const
2084 {
2085     return CompletionFlagEnum;
2086 }
2087 
2088 
getVerificationFlag() const2089 DSRTypes::E_VerificationFlag DSRDocument::getVerificationFlag() const
2090 {
2091     return VerificationFlagEnum;
2092 }
2093 
2094 
hasVerifyingObservers() const2095 OFBool DSRDocument::hasVerifyingObservers() const
2096 {
2097     return (VerifyingObserver.card() > 0);
2098 }
2099 
2100 
getNumberOfVerifyingObservers() const2101 size_t DSRDocument::getNumberOfVerifyingObservers() const
2102 {
2103     return OFstatic_cast(size_t, VerifyingObserver.card());
2104 }
2105 
2106 
getVerifyingObserver(const size_t idx,OFString & dateTime,OFString & observerName,OFString & organization)2107 OFCondition DSRDocument::getVerifyingObserver(const size_t idx,
2108                                               OFString &dateTime,
2109                                               OFString &observerName,
2110                                               OFString &organization)
2111 {
2112     DSRCodedEntryValue dummyCode;
2113     return getVerifyingObserver(idx, dateTime, observerName, dummyCode, organization);
2114 }
2115 
2116 
getVerifyingObserver(const size_t idx,OFString & dateTime,OFString & observerName,DSRCodedEntryValue & observerCode,OFString & organization)2117 OFCondition DSRDocument::getVerifyingObserver(const size_t idx,
2118                                               OFString &dateTime,
2119                                               OFString &observerName,
2120                                               DSRCodedEntryValue &observerCode,
2121                                               OFString &organization)
2122 {
2123     OFCondition result = EC_IllegalParameter;
2124     /* clear all reference variables */
2125     dateTime.clear();
2126     observerName.clear();
2127     observerCode.clear();
2128     organization.clear();
2129     /* get specified entry */
2130     if ((idx > 0) && (idx <= getNumberOfVerifyingObservers()))
2131     {
2132         /* access by index is currently not very efficient */
2133         DcmItem *ditem = VerifyingObserver.getItem(OFstatic_cast(unsigned long, idx - 1));
2134         if (ditem != NULL)
2135         {
2136             result = getStringValueFromDataset(*ditem, DCM_VerificationDateTime, dateTime);
2137             if (result.good())
2138                 result = getStringValueFromDataset(*ditem, DCM_VerifyingObserverName, observerName);
2139             if (result.good())
2140             {
2141                 /* code is optional (type 2) */
2142                 observerCode.readSequence(*ditem, DCM_VerifyingObserverIdentificationCodeSequence, "2" /*type*/);
2143                 result = getStringValueFromDataset(*ditem, DCM_VerifyingOrganization, organization);
2144             }
2145             if (result.good())
2146             {
2147                 if (dateTime.empty() || observerName.empty() || organization.empty())
2148                     result = SR_EC_InvalidValue;
2149             }
2150         }
2151     }
2152     return result;
2153 }
2154 
2155 
getPredecessorDocuments()2156 DSRSOPInstanceReferenceList &DSRDocument::getPredecessorDocuments()
2157 {
2158     /* pass current value of specific character set */
2159     PredecessorDocuments.setSpecificCharacterSet(getSpecificCharacterSet());
2160     return PredecessorDocuments;
2161 }
2162 
2163 
getIdenticalDocuments()2164 DSRSOPInstanceReferenceList &DSRDocument::getIdenticalDocuments()
2165 {
2166     /* pass current value of specific character set */
2167     IdenticalDocuments.setSpecificCharacterSet(getSpecificCharacterSet());
2168     return IdenticalDocuments;
2169 }
2170 
2171 
getCurrentRequestedProcedureEvidence()2172 DSRSOPInstanceReferenceList &DSRDocument::getCurrentRequestedProcedureEvidence()
2173 {
2174     /* pass current value of specific character set */
2175     CurrentRequestedProcedureEvidence.setSpecificCharacterSet(getSpecificCharacterSet());
2176     return CurrentRequestedProcedureEvidence;
2177 }
2178 
2179 
getPertinentOtherEvidence()2180 DSRSOPInstanceReferenceList &DSRDocument::getPertinentOtherEvidence()
2181 {
2182     /* pass current value of specific character set */
2183     PredecessorDocuments.setSpecificCharacterSet(getSpecificCharacterSet());
2184     return PertinentOtherEvidence;
2185 }
2186 
2187 
getReferencedInstances()2188 DSRReferencedInstanceList &DSRDocument::getReferencedInstances()
2189 {
2190     return ReferencedInstances;
2191 }
2192 
2193 
getCodingSchemeIdentification()2194 DSRCodingSchemeIdentificationList &DSRDocument::getCodingSchemeIdentification()
2195 {
2196     /* pass current value of specific character set */
2197     CodingSchemeIdentification.setSpecificCharacterSet(getSpecificCharacterSet());
2198     return CodingSchemeIdentification;
2199 }
2200 
2201 
2202 // --- get attributes (C++ string) ---
2203 
getSpecificCharacterSet(OFString & value,const signed long pos) const2204 OFCondition DSRDocument::getSpecificCharacterSet(OFString &value,
2205                                                  const signed long pos) const
2206 {
2207     return getStringValueFromElement(SpecificCharacterSet, value, pos);
2208 }
2209 
2210 
getCompletionFlagDescription(OFString & value,const signed long pos) const2211 OFCondition DSRDocument::getCompletionFlagDescription(OFString &value,
2212                                                      const signed long pos) const
2213 {
2214     return getStringValueFromElement(CompletionFlagDescription, value, pos);
2215 }
2216 
2217 
getModality(OFString & value,const signed long pos) const2218 OFCondition DSRDocument::getModality(OFString &value,
2219                                      const signed long pos) const
2220 {
2221     return getStringValueFromElement(Modality, value, pos);
2222 }
2223 
2224 
getSOPClassUID(OFString & value,const signed long pos) const2225 OFCondition DSRDocument::getSOPClassUID(OFString &value,
2226                                         const signed long pos) const
2227 {
2228     return getStringValueFromElement(SOPClassUID, value, pos);
2229 }
2230 
2231 
getStudyInstanceUID(OFString & value,const signed long pos) const2232 OFCondition DSRDocument::getStudyInstanceUID(OFString &value,
2233                                              const signed long pos) const
2234 {
2235     return getStringValueFromElement(StudyInstanceUID, value, pos);
2236 }
2237 
2238 
getSeriesInstanceUID(OFString & value,const signed long pos) const2239 OFCondition DSRDocument::getSeriesInstanceUID(OFString &value,
2240                                               const signed long pos) const
2241 {
2242     return getStringValueFromElement(SeriesInstanceUID, value, pos);
2243 }
2244 
2245 
getSOPInstanceUID(OFString & value,const signed long pos) const2246 OFCondition DSRDocument::getSOPInstanceUID(OFString &value,
2247                                            const signed long pos) const
2248 {
2249     return getStringValueFromElement(SOPInstanceUID, value, pos);
2250 }
2251 
2252 
getInstanceCreatorUID(OFString & value,const signed long pos) const2253 OFCondition DSRDocument::getInstanceCreatorUID(OFString &value,
2254                                                const signed long pos) const
2255 {
2256     return getStringValueFromElement(InstanceCreatorUID, value, pos);
2257 }
2258 
2259 
getTimezoneOffsetFromUTC(OFString & value,const signed long pos) const2260 OFCondition DSRDocument::getTimezoneOffsetFromUTC(OFString &value,
2261                                                   const signed long pos) const
2262 {
2263     return getStringValueFromElement(TimezoneOffsetFromUTC, value, pos);
2264 }
2265 
2266 
getPatientName(OFString & value,const signed long pos) const2267 OFCondition DSRDocument::getPatientName(OFString &value,
2268                                         const signed long pos) const
2269 {
2270     return getStringValueFromElement(PatientName, value, pos);
2271 }
2272 
2273 
getPatientBirthDate(OFString & value,const signed long pos) const2274 OFCondition DSRDocument::getPatientBirthDate(OFString &value,
2275                                              const signed long pos) const
2276 {
2277     return getStringValueFromElement(PatientBirthDate, value, pos);
2278 }
2279 
2280 
getPatientSex(OFString & value,const signed long pos) const2281 OFCondition DSRDocument::getPatientSex(OFString &value,
2282                                        const signed long pos) const
2283 {
2284     return getStringValueFromElement(PatientSex, value, pos);
2285 }
2286 
2287 
getReferringPhysicianName(OFString & value,const signed long pos) const2288 OFCondition DSRDocument::getReferringPhysicianName(OFString &value,
2289                                                    const signed long pos) const
2290 {
2291     return getStringValueFromElement(ReferringPhysicianName, value, pos);
2292 }
2293 
2294 
getStudyDescription(OFString & value,const signed long pos) const2295 OFCondition DSRDocument::getStudyDescription(OFString &value,
2296                                              const signed long pos) const
2297 {
2298     return getStringValueFromElement(StudyDescription, value, pos);
2299 }
2300 
2301 
getSeriesDescription(OFString & value,const signed long pos) const2302 OFCondition DSRDocument::getSeriesDescription(OFString &value,
2303                                               const signed long pos) const
2304 {
2305     return getStringValueFromElement(SeriesDescription, value, pos);
2306 }
2307 
2308 
getProtocolName(OFString & value,const signed long pos) const2309 OFCondition DSRDocument::getProtocolName(OFString &value,
2310                                          const signed long pos) const
2311 {
2312     return getStringValueFromElement(ProtocolName, value, pos);
2313 }
2314 
2315 
getManufacturer(OFString & value,const signed long pos) const2316 OFCondition DSRDocument::getManufacturer(OFString &value,
2317                                          const signed long pos) const
2318 {
2319     return getStringValueFromElement(Manufacturer, value, pos);
2320 }
2321 
2322 
getManufacturerModelName(OFString & value,const signed long pos) const2323 OFCondition DSRDocument::getManufacturerModelName(OFString &value,
2324                                                   const signed long pos) const
2325 {
2326     return getStringValueFromElement(ManufacturerModelName, value, pos);
2327 }
2328 
2329 
getDeviceSerialNumber(OFString & value,const signed long pos) const2330 OFCondition DSRDocument::getDeviceSerialNumber(OFString &value,
2331                                                const signed long pos) const
2332 {
2333     return getStringValueFromElement(DeviceSerialNumber, value, pos);
2334 }
2335 
2336 
getSoftwareVersions(OFString & value,const signed long pos) const2337 OFCondition DSRDocument::getSoftwareVersions(OFString &value,
2338                                              const signed long pos) const
2339 {
2340     return getStringValueFromElement(SoftwareVersions, value, pos);
2341 }
2342 
2343 
getSynchronizationFrameOfReferenceUID(OFString & value,const signed long pos) const2344 OFCondition DSRDocument::getSynchronizationFrameOfReferenceUID(OFString &value,
2345                                                                const signed long pos) const
2346 {
2347     return getStringValueFromElement(SynchronizationFrameOfReferenceUID, value, pos);
2348 }
2349 
2350 
getSynchronizationTrigger(OFString & value,const signed long pos) const2351 OFCondition DSRDocument::getSynchronizationTrigger(OFString &value,
2352                                                    const signed long pos) const
2353 {
2354     return getStringValueFromElement(SynchronizationTrigger, value, pos);
2355 }
2356 
2357 
getAcquisitionTimeSynchronized(OFString & value,const signed long pos) const2358 OFCondition DSRDocument::getAcquisitionTimeSynchronized(OFString &value,
2359                                                         const signed long pos) const
2360 {
2361     return getStringValueFromElement(AcquisitionTimeSynchronized, value, pos);
2362 }
2363 
2364 
getStudyDate(OFString & value,const signed long pos) const2365 OFCondition DSRDocument::getStudyDate(OFString &value,
2366                                       const signed long pos) const
2367 {
2368     return getStringValueFromElement(StudyDate, value, pos);
2369 }
2370 
2371 
getStudyTime(OFString & value,const signed long pos) const2372 OFCondition DSRDocument::getStudyTime(OFString &value,
2373                                       const signed long pos) const
2374 {
2375     return getStringValueFromElement(StudyTime, value, pos);
2376 }
2377 
2378 
getSeriesDate(OFString & value,const signed long pos) const2379 OFCondition DSRDocument::getSeriesDate(OFString &value,
2380                                        const signed long pos) const
2381 {
2382     return getStringValueFromElement(SeriesDate, value, pos);
2383 }
2384 
2385 
getSeriesTime(OFString & value,const signed long pos) const2386 OFCondition DSRDocument::getSeriesTime(OFString &value,
2387                                        const signed long pos) const
2388 {
2389     return getStringValueFromElement(SeriesTime, value, pos);
2390 }
2391 
2392 
getInstanceCreationDate(OFString & value,const signed long pos) const2393 OFCondition DSRDocument::getInstanceCreationDate(OFString &value,
2394                                                  const signed long pos) const
2395 {
2396     return getStringValueFromElement(InstanceCreationDate, value, pos);
2397 }
2398 
2399 
getInstanceCreationTime(OFString & value,const signed long pos) const2400 OFCondition DSRDocument::getInstanceCreationTime(OFString &value,
2401                                                  const signed long pos) const
2402 {
2403     return getStringValueFromElement(InstanceCreationTime, value, pos);
2404 }
2405 
2406 
getContentDate(OFString & value,const signed long pos) const2407 OFCondition DSRDocument::getContentDate(OFString &value,
2408                                         const signed long pos) const
2409 {
2410     return getStringValueFromElement(ContentDate, value, pos);
2411 }
2412 
2413 
getContentTime(OFString & value,const signed long pos) const2414 OFCondition DSRDocument::getContentTime(OFString &value,
2415                                         const signed long pos) const
2416 {
2417     return getStringValueFromElement(ContentTime, value, pos);
2418 }
2419 
2420 
getStudyID(OFString & value,const signed long pos) const2421 OFCondition DSRDocument::getStudyID(OFString &value,
2422                                     const signed long pos) const
2423 {
2424     return getStringValueFromElement(StudyID, value, pos);
2425 }
2426 
2427 
getPatientID(OFString & value,const signed long pos) const2428 OFCondition DSRDocument::getPatientID(OFString &value,
2429                                       const signed long pos) const
2430 {
2431     return getStringValueFromElement(PatientID, value, pos);
2432 }
2433 
2434 
getIssuerOfPatientID(OFString & value,const signed long pos) const2435 OFCondition DSRDocument::getIssuerOfPatientID(OFString &value,
2436                                               const signed long pos) const
2437 {
2438     return getStringValueFromElement(IssuerOfPatientID, value, pos);
2439 }
2440 
2441 
getSeriesNumber(OFString & value,const signed long pos) const2442 OFCondition DSRDocument::getSeriesNumber(OFString &value,
2443                                          const signed long pos) const
2444 {
2445     return getStringValueFromElement(SeriesNumber, value, pos);
2446 }
2447 
2448 
getInstanceNumber(OFString & value,const signed long pos) const2449 OFCondition DSRDocument::getInstanceNumber(OFString &value,
2450                                            const signed long pos) const
2451 {
2452     return getStringValueFromElement(InstanceNumber, value, pos);
2453 }
2454 
2455 
getAccessionNumber(OFString & value,const signed long pos) const2456 OFCondition DSRDocument::getAccessionNumber(OFString &value,
2457                                             const signed long pos) const
2458 {
2459     return getStringValueFromElement(AccessionNumber, value, pos);
2460 }
2461 
2462 
2463 // --- set attributes ---
2464 
setSpecificCharacterSet(const OFString & value,const OFBool check)2465 OFCondition DSRDocument::setSpecificCharacterSet(const OFString &value,
2466                                                  const OFBool check)
2467 {
2468     OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "1-n") : EC_Normal;
2469     if (result.good())
2470     {
2471         /* try to map the given string to an enum value */
2472         SpecificCharacterSetEnum = definedTermToCharacterSet(value);
2473         if (SpecificCharacterSetEnum == CS_unknown)
2474             DCMSR_WARN("Setting unknown/unsupported value for Specific Character Set: " << value);
2475         /* in any case, store the passed string value */
2476         result = SpecificCharacterSet.putOFStringArray(value);
2477     }
2478     return result;
2479 }
2480 
2481 
setCompletionFlagDescription(const OFString & value,const OFBool check)2482 OFCondition DSRDocument::setCompletionFlagDescription(const OFString &value,
2483                                                       const OFBool check)
2484 {
2485     OFCondition result = EC_IllegalCall;
2486     /* not all SR IODs contain the SR Document General Module */
2487     if (usesSRDocumentGeneralModule(getDocumentType()))
2488     {
2489         if (check)
2490             result = DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet());
2491         if (result.good())
2492             result = CompletionFlagDescription.putOFStringArray(value);
2493     }
2494     return result;
2495 }
2496 
2497 
setTimezoneOffsetFromUTC(const OFString & value,const OFBool check)2498 OFCondition DSRDocument::setTimezoneOffsetFromUTC(const OFString &value,
2499                                                   const OFBool check)
2500 {
2501     OFCondition result = (check) ? DcmShortString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2502     if (result.good())
2503         result = TimezoneOffsetFromUTC.putOFStringArray(value);
2504     return result;
2505 }
2506 
2507 
setPatientName(const OFString & value,const OFBool check)2508 OFCondition DSRDocument::setPatientName(const OFString &value,
2509                                         const OFBool check)
2510 {
2511     OFCondition result = (check) ? DcmPersonName::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2512     if (result.good())
2513         result = PatientName.putOFStringArray(value);
2514     return result;
2515 }
2516 
2517 
setPatientBirthDate(const OFString & value,const OFBool check)2518 OFCondition DSRDocument::setPatientBirthDate(const OFString &value,
2519                                              const OFBool check)
2520 {
2521     OFCondition result = (check) ? DcmDate::checkStringValue(value, "1") : EC_Normal;
2522     if (result.good())
2523         result = PatientBirthDate.putOFStringArray(value);
2524     return result;
2525 }
2526 
2527 
setPatientSex(const OFString & value,const OFBool check)2528 OFCondition DSRDocument::setPatientSex(const OFString &value,
2529                                        const OFBool check)
2530 {
2531     OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal;
2532     if (result.good())
2533         result = PatientSex.putOFStringArray(value);
2534     return result;
2535 }
2536 
2537 
setReferringPhysicianName(const OFString & value,const OFBool check)2538 OFCondition DSRDocument::setReferringPhysicianName(const OFString &value,
2539                                                    const OFBool check)
2540 {
2541     OFCondition result = (check) ? DcmPersonName::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2542     if (result.good())
2543         result = ReferringPhysicianName.putOFStringArray(value);
2544     return result;
2545 }
2546 
2547 
setStudyDescription(const OFString & value,const OFBool check)2548 OFCondition DSRDocument::setStudyDescription(const OFString &value,
2549                                              const OFBool check)
2550 {
2551     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2552     if (result.good())
2553         result = StudyDescription.putOFStringArray(value);
2554     return result;
2555 }
2556 
2557 
setSeriesDescription(const OFString & value,const OFBool check)2558 OFCondition DSRDocument::setSeriesDescription(const OFString &value,
2559                                               const OFBool check)
2560 {
2561     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2562     if (result.good())
2563         result = SeriesDescription.putOFStringArray(value);
2564     return result;
2565 }
2566 
2567 
setProtocolName(const OFString & value,const OFBool check)2568 OFCondition DSRDocument::setProtocolName(const OFString &value,
2569                                          const OFBool check)
2570 {
2571     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2572     if (result.good())
2573         result = ProtocolName.putOFStringArray(value);
2574     return result;
2575 }
2576 
2577 
setManufacturer(const OFString & value,const OFBool check)2578 OFCondition DSRDocument::setManufacturer(const OFString &value,
2579                                          const OFBool check)
2580 {
2581     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2582     if (result.good())
2583         result = Manufacturer.putOFStringArray(value);
2584     return result;
2585 }
2586 
2587 
setManufacturerModelName(const OFString & value,const OFBool check)2588 OFCondition DSRDocument::setManufacturerModelName(const OFString &value,
2589                                                   const OFBool check)
2590 {
2591     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2592     if (result.good())
2593         result = ManufacturerModelName.putOFStringArray(value);
2594     return result;
2595 }
2596 
2597 
setDeviceSerialNumber(const OFString & value,const OFBool check)2598 OFCondition DSRDocument::setDeviceSerialNumber(const OFString &value,
2599                                                const OFBool check)
2600 {
2601     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2602     if (result.good())
2603         result = DeviceSerialNumber.putOFStringArray(value);
2604     return result;
2605 }
2606 
2607 
setSoftwareVersions(const OFString & value,const OFBool check)2608 OFCondition DSRDocument::setSoftwareVersions(const OFString &value,
2609                                              const OFBool check)
2610 {
2611     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1-n", getSpecificCharacterSet()) : EC_Normal;
2612     if (result.good())
2613         result = SoftwareVersions.putOFStringArray(value);
2614     return result;
2615 }
2616 
2617 
setSynchronizationFrameOfReferenceUID(const OFString & value,const OFBool check)2618 OFCondition DSRDocument::setSynchronizationFrameOfReferenceUID(const OFString &value,
2619                                                                const OFBool check)
2620 {
2621     OFCondition result = (check) ? DcmUniqueIdentifier::checkStringValue(value, "1") : EC_Normal;
2622     if (result.good())
2623         result = SynchronizationFrameOfReferenceUID.putOFStringArray(value);
2624     return result;
2625 }
2626 
2627 
setSynchronizationTrigger(const OFString & value,const OFBool check)2628 OFCondition DSRDocument::setSynchronizationTrigger(const OFString &value,
2629                                                    const OFBool check)
2630 {
2631     OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal;
2632     if (result.good())
2633         result = SynchronizationTrigger.putOFStringArray(value);
2634     return result;
2635 }
2636 
2637 
setAcquisitionTimeSynchronized(const OFString & value,const OFBool check)2638 OFCondition DSRDocument::setAcquisitionTimeSynchronized(const OFString &value,
2639                                                         const OFBool check)
2640 {
2641     OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal;
2642     if (result.good())
2643         result = AcquisitionTimeSynchronized.putOFStringArray(value);
2644     return result;
2645 }
2646 
2647 
setContentDate(const OFString & value,const OFBool check)2648 OFCondition DSRDocument::setContentDate(const OFString &value,
2649                                         const OFBool check)
2650 {
2651     OFCondition result = (check) ? DcmDate::checkStringValue(value, "1") : EC_Normal;
2652     if (result.good())
2653         result = ContentDate.putOFStringArray(value);
2654     return result;
2655 }
2656 
2657 
setContentTime(const OFString & value,const OFBool check)2658 OFCondition DSRDocument::setContentTime(const OFString &value,
2659                                         const OFBool check)
2660 {
2661     OFCondition result = (check) ? DcmTime::checkStringValue(value, "1") : EC_Normal;
2662     if (result.good())
2663         result = ContentTime.putOFStringArray(value);
2664     return result;
2665 }
2666 
2667 
setStudyDate(const OFString & value,const OFBool check)2668 OFCondition DSRDocument::setStudyDate(const OFString &value,
2669                                       const OFBool check)
2670 {
2671     OFCondition result = (check) ? DcmDate::checkStringValue(value, "1") : EC_Normal;
2672     if (result.good())
2673         result = StudyDate.putOFStringArray(value);
2674     return result;
2675 }
2676 
2677 
setStudyTime(const OFString & value,const OFBool check)2678 OFCondition DSRDocument::setStudyTime(const OFString &value,
2679                                       const OFBool check)
2680 {
2681     OFCondition result = (check) ? DcmTime::checkStringValue(value, "1") : EC_Normal;
2682     if (result.good())
2683         result = StudyTime.putOFStringArray(value);
2684     return result;
2685 }
2686 
2687 
setSeriesDate(const OFString & value,const OFBool check)2688 OFCondition DSRDocument::setSeriesDate(const OFString &value,
2689                                        const OFBool check)
2690 {
2691     OFCondition result = (check) ? DcmDate::checkStringValue(value, "1") : EC_Normal;
2692     if (result.good())
2693         result = SeriesDate.putOFStringArray(value);
2694     return result;
2695 }
2696 
2697 
setSeriesTime(const OFString & value,const OFBool check)2698 OFCondition DSRDocument::setSeriesTime(const OFString &value,
2699                                        const OFBool check)
2700 {
2701     OFCondition result = (check) ? DcmTime::checkStringValue(value, "1") : EC_Normal;
2702     if (result.good())
2703         result = SeriesTime.putOFStringArray(value);
2704     return result;
2705 }
2706 
2707 
setStudyID(const OFString & value,const OFBool check)2708 OFCondition DSRDocument::setStudyID(const OFString &value,
2709                                     const OFBool check)
2710 {
2711     OFCondition result = (check) ? DcmShortString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2712     if (result.good())
2713         result = StudyID.putOFStringArray(value);
2714     return result;
2715 }
2716 
2717 
setPatientID(const OFString & value,const OFBool check)2718 OFCondition DSRDocument::setPatientID(const OFString &value,
2719                                       const OFBool check)
2720 {
2721     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2722     if (result.good())
2723         result = PatientID.putOFStringArray(value);
2724     return result;
2725 }
2726 
2727 
setIssuerOfPatientID(const OFString & value,const OFBool check)2728 OFCondition DSRDocument::setIssuerOfPatientID(const OFString &value,
2729                                               const OFBool check)
2730 {
2731     OFCondition result = (check) ? DcmLongString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2732     if (result.good())
2733         result = IssuerOfPatientID.putOFStringArray(value);
2734     return result;
2735 }
2736 
2737 
setSeriesNumber(const OFString & value,const OFBool check)2738 OFCondition DSRDocument::setSeriesNumber(const OFString &value,
2739                                          const OFBool check)
2740 {
2741     OFCondition result = (check) ? DcmIntegerString::checkStringValue(value, "1") : EC_Normal;
2742     if (result.good())
2743         result = SeriesNumber.putOFStringArray(value);
2744     return result;
2745 }
2746 
2747 
setInstanceNumber(const OFString & value,const OFBool check)2748 OFCondition DSRDocument::setInstanceNumber(const OFString &value,
2749                                            const OFBool check)
2750 {
2751     OFCondition result = (check) ? DcmIntegerString::checkStringValue(value, "1") : EC_Normal;
2752     if (result.good())
2753         result = InstanceNumber.putOFStringArray(value);
2754     return result;
2755 }
2756 
2757 
setAccessionNumber(const OFString & value,const OFBool check)2758 OFCondition DSRDocument::setAccessionNumber(const OFString &value,
2759                                             const OFBool check)
2760 {
2761     OFCondition result = (check) ? DcmShortString::checkStringValue(value, "1", getSpecificCharacterSet()) : EC_Normal;
2762     if (result.good())
2763         result = AccessionNumber.putOFStringArray(value);
2764     return result;
2765 }
2766 
2767 
2768 // --- document management functions
2769 
createNewStudy()2770 void DSRDocument::createNewStudy()
2771 {
2772     /* clear all study-related attributes */
2773     StudyInstanceUID.clear();
2774     StudyDate.clear();
2775     StudyTime.clear();
2776     ReferringPhysicianName.clear();
2777     StudyID.clear();
2778     AccessionNumber.clear();
2779     StudyDescription.clear();
2780     /* also creates new study (since UID is empty) and SOP instance */
2781     createNewSeries();
2782 }
2783 
2784 
createNewSeries()2785 void DSRDocument::createNewSeries()
2786 {
2787     /* clear all series-related attributes */
2788     SeriesInstanceUID.clear();
2789     SeriesNumber.clear();
2790     SeriesDate.clear();
2791     SeriesTime.clear();
2792     SeriesDescription.clear();
2793     /* also creates new series (since UID is empty) */
2794     createNewSOPInstance();
2795 }
2796 
2797 
createNewSeriesInStudy(const OFString & studyUID,const OFBool check)2798 OFCondition DSRDocument::createNewSeriesInStudy(const OFString &studyUID,
2799                                                 const OFBool check)
2800 {
2801     OFCondition result = EC_IllegalParameter;
2802     if (!studyUID.empty())
2803     {
2804         /* check parameter for conformance with VR and VM (if needed) */
2805         result = (check) ? DcmUniqueIdentifier::checkStringValue(studyUID, "1") : EC_Normal;
2806         if (result.good())
2807         {
2808             StudyInstanceUID.putOFStringArray(studyUID);
2809             /* also creates new SOP instance */
2810             createNewSeries();
2811         }
2812     }
2813     return result;
2814 }
2815 
2816 
createNewSOPInstance()2817 void DSRDocument::createNewSOPInstance()
2818 {
2819     SOPInstanceUID.clear();
2820     /* reset FinalizedFlag */
2821     FinalizedFlag = OFFalse;
2822     /* update all DICOM attributes (incl. empty UIDs) */
2823     updateAttributes();
2824 }
2825 
2826 
createNewDocument()2827 OFCondition DSRDocument::createNewDocument()
2828 {
2829     /* create new document with the same type as the current one */
2830     return createNewDocument(getDocumentType());
2831 }
2832 
2833 
createNewDocument(const E_DocumentType documentType)2834 OFCondition DSRDocument::createNewDocument(const E_DocumentType documentType)
2835 {
2836     /* document type is stored only once (namely in the document tree) */
2837     OFCondition result = DocumentTree.changeDocumentType(documentType, OFTrue /*deleteTree*/);
2838     if (result.good())
2839     {
2840         /* clear object (all member variables) */
2841         clear();
2842         /* set initial values for a new SOP instance */
2843         createNewSOPInstance();
2844     }
2845     return result;
2846 }
2847 
2848 
changeDocumentType(const E_DocumentType documentType)2849 OFCondition DSRDocument::changeDocumentType(const E_DocumentType documentType)
2850 {
2851     /* document type is stored only once (namely in the document tree) */
2852     OFCondition result = DocumentTree.changeDocumentType(documentType, OFFalse /*deleteTree*/);
2853     if (result.good())
2854     {
2855         /* update IOD-specific DICOM attributes */
2856         updateAttributes(OFFalse /*updateAll*/);
2857     }
2858     return result;
2859 }
2860 
2861 
createRevisedVersion(const OFBool clearList)2862 OFCondition DSRDocument::createRevisedVersion(const OFBool clearList)
2863 {
2864     OFCondition result = EC_IllegalCall;
2865     /* not all SR IODs contain the SR Document General Module */
2866     if (usesSRDocumentGeneralModule(getDocumentType()))
2867     {
2868         /* check whether document is already completed */
2869         if (CompletionFlagEnum == CF_Complete)
2870         {
2871             if (clearList)
2872                 PredecessorDocuments.clear();
2873             /* add current document */
2874             OFString studyUID, seriesUID, classUID, instanceUID;
2875             result = PredecessorDocuments.addItem(getStringValueFromElement(StudyInstanceUID, studyUID),
2876                                                   getStringValueFromElement(SeriesInstanceUID, seriesUID),
2877                                                   getStringValueFromElement(SOPClassUID, classUID),
2878                                                   getStringValueFromElement(SOPInstanceUID, instanceUID));
2879             if (result.good())
2880             {
2881                 IdenticalDocuments.clear();
2882                 /* reset completion flag, delete description */
2883                 CompletionFlagEnum = CF_invalid;
2884                 CompletionFlagDescription.clear();
2885                 /* clear content date/time, will be set automatically in updateAttributes() */
2886                 ContentDate.clear();
2887                 ContentTime.clear();
2888                 /* clear list of verifying observers and set flag to UNVERIFIED */
2889                 removeVerification();
2890                 /* remove digital signatures from document tree */
2891                 DocumentTree.removeSignatures();
2892                 /* create new instance UID, update creation date/time and reset finalized flag */
2893                 createNewSOPInstance();
2894             }
2895         }
2896     }
2897     return result;
2898 }
2899 
2900 
completeDocument()2901 OFCondition DSRDocument::completeDocument()
2902 {
2903     /* complete document with empty/absent completion description */
2904     return completeDocument("");
2905 }
2906 
2907 
completeDocument(const OFString & description,const OFBool check)2908 OFCondition DSRDocument::completeDocument(const OFString &description,
2909                                           const OFBool check)
2910 {
2911     OFCondition result = EC_IllegalCall;
2912     /* not all SR IODs contain the SR Document General Module */
2913     if (usesSRDocumentGeneralModule(getDocumentType()))
2914     {
2915         /* if document is not already completed */
2916         if (CompletionFlagEnum != CF_Complete)
2917         {
2918             /* check parameter for conformance with VR and VM (if needed) */
2919             result = (check) ? DcmLongString::checkStringValue(description, "1", getSpecificCharacterSet()) : EC_Normal;
2920             if (result.good())
2921             {
2922                 /* completed for now and ever */
2923                 CompletionFlagEnum = CF_Complete;
2924                 /* completion flag description */
2925                 setCompletionFlagDescription(description);
2926             }
2927         }
2928     }
2929     return result;
2930 }
2931 
2932 
verifyDocument(const OFString & observerName,const OFString & organization,const OFString & dateTime,const OFBool check)2933 OFCondition DSRDocument::verifyDocument(const OFString &observerName,
2934                                         const OFString &organization,
2935                                         const OFString &dateTime,
2936                                         const OFBool check)
2937 {
2938     /* empty CodedEntryValue */
2939     return verifyDocument(observerName, DSRCodedEntryValue() /*dummy*/, organization, dateTime, check);
2940 }
2941 
2942 
verifyDocument(const OFString & observerName,const DSRCodedEntryValue & observerCode,const OFString & organization,const OFString & dateTime,const OFBool check)2943 OFCondition DSRDocument::verifyDocument(const OFString &observerName,
2944                                         const DSRCodedEntryValue &observerCode,
2945                                         const OFString &organization,
2946                                         const OFString &dateTime,
2947                                         const OFBool check)
2948 {
2949     OFCondition result = EC_IllegalCall;
2950     /* not all SR IODs contain the SR Document General Module */
2951     if (usesSRDocumentGeneralModule(getDocumentType()))
2952     {
2953         /* verify completed documents only */
2954         if (CompletionFlagEnum == CF_Complete)
2955         {
2956             /* empty strings are not allowed (type 1 attributes) */
2957             if (!observerName.empty() && !organization.empty())
2958             {
2959                 /* check parameters for conformance with VR and VM (if needed) */
2960                 if (check)
2961                 {
2962                     if (observerCode.isEmpty() || observerCode.isValid())
2963                         result = EC_Normal;
2964                     if (result.good())
2965                         result = DcmPersonName::checkStringValue(observerName, "1", getSpecificCharacterSet());
2966                     if (result.good())
2967                         result = DcmLongString::checkStringValue(organization, "1", getSpecificCharacterSet());
2968                     if (result.good())
2969                         result = DcmDateTime::checkStringValue(dateTime, "1");
2970                 } else {
2971                     /* no checks, so everything is fine */
2972                     result = EC_Normal;
2973                 }
2974                 if (result.good())
2975                 {
2976                     DcmItem *ditem = new DcmItem();
2977                     if (ditem != NULL)
2978                     {
2979                         /* write VerifyingObserverName */
2980                         putStringValueToDataset(*ditem, DCM_VerifyingObserverName, observerName);
2981                         /* write VerifyingObserverIdentificationCodeSequence (might be empty, type 2) */
2982                         observerCode.writeSequence(*ditem, DCM_VerifyingObserverIdentificationCodeSequence);
2983                         /* write VerifyingOrganization */
2984                         putStringValueToDataset(*ditem, DCM_VerifyingOrganization, organization);
2985                         /* write VerificationDateTime */
2986                         if (dateTime.empty())
2987                         {
2988                             OFString tmpString;
2989                             currentDateTime(tmpString);
2990                             putStringValueToDataset(*ditem, DCM_VerificationDateTime, tmpString);
2991                         } else
2992                             putStringValueToDataset(*ditem, DCM_VerificationDateTime, dateTime);
2993                         /* insert items into sequence */
2994                         VerifyingObserver.insert(ditem);
2995                         /* set VerificationFlag to VERIFIED */
2996                         VerificationFlagEnum = VF_Verified;
2997                         /* reset FinalizedFlag */
2998                         FinalizedFlag = OFFalse;
2999                     } else
3000                         result = EC_MemoryExhausted;
3001                 }
3002             } else
3003                 result = EC_IllegalParameter;
3004         }
3005     }
3006     return result;
3007 }
3008 
3009 
removeVerification()3010 void DSRDocument::removeVerification()
3011 {
3012     /* clear list of verifying observers and set flag to UNVERIFIED */
3013     VerifyingObserver.clear();
3014     VerificationFlagEnum = VF_Unverified;
3015     /* reset FinalizedFlag */
3016     FinalizedFlag = OFFalse;
3017 }
3018 
3019 
finalizeDocument()3020 OFCondition DSRDocument::finalizeDocument()
3021 {
3022     OFCondition result = EC_IllegalCall;
3023     /* document can only be finalized if it is already completed */
3024     if (CompletionFlagEnum == CF_Complete)
3025     {
3026         /* set FinalizedFlag */
3027         FinalizedFlag = OFTrue;
3028         result = EC_Normal;
3029     }
3030     return result;
3031 }
3032 
3033 
updateAttributes(const OFBool updateAll,const OFBool verboseMode)3034 void DSRDocument::updateAttributes(const OFBool updateAll,
3035                                    const OFBool verboseMode)
3036 {
3037     if (verboseMode)
3038         DCMSR_DEBUG("Updating " << (updateAll ? "all " : "") << "DICOM header attributes");
3039     const E_DocumentType documentType = getDocumentType();
3040     /* retrieve SOP class UID from internal document type */
3041     SOPClassUID.putString(documentTypeToSOPClassUID(documentType));
3042     /* put modality string depending on document type */
3043     Modality.putString(documentTypeToModality(documentType));
3044     if (updateAll)
3045     {
3046         OFString tmpString;
3047         /* determine local timezone (if required) */
3048         if (requiresTimezoneModule(documentType) && TimezoneOffsetFromUTC.isEmpty())
3049         {
3050             DCMSR_DEBUG("  Determining local timezone for Timezone Offset From UTC");
3051             TimezoneOffsetFromUTC.putOFStringArray(localTimezone(tmpString));
3052         }
3053 
3054         /* create new instance number if required (type 1) */
3055         if (InstanceNumber.isEmpty())
3056             InstanceNumber.putString("1");
3057         /* create new series number if required (type 1) */
3058         if (SeriesNumber.isEmpty())
3059             SeriesNumber.putString("1");
3060 
3061         char uid[100];
3062         /* create new SOP instance UID if required */
3063         if (SOPInstanceUID.isEmpty())
3064         {
3065             if (verboseMode)
3066                 DCMSR_DEBUG("  Generating new value for SOP Instance UID");
3067             SOPInstanceUID.putString(dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
3068             /* set instance creation date to current date (YYYYMMDD) */
3069             InstanceCreationDate.putOFStringArray(currentDate(tmpString));
3070             /* set instance creation time to current time (HHMMSS) */
3071             InstanceCreationTime.putOFStringArray(currentTime(tmpString));
3072             /* set instance creator UID to identify instances that have been created by this toolkit */
3073             InstanceCreatorUID.putString(OFFIS_INSTANCE_CREATOR_UID);
3074         }
3075         /* create new study instance UID if required */
3076         if (StudyInstanceUID.isEmpty())
3077         {
3078             if (verboseMode)
3079                 DCMSR_DEBUG("  Generating new value for Study Instance UID");
3080             StudyInstanceUID.putString(dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT));
3081         }
3082         /* create new series instance UID if required */
3083         if (SeriesInstanceUID.isEmpty())
3084         {
3085             if (verboseMode)
3086                 DCMSR_DEBUG("  Generating new value for Series Instance UID");
3087             SeriesInstanceUID.putString(dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT));
3088         }
3089 
3090         /* check and set content date if required */
3091         if (ContentDate.isEmpty())
3092             ContentDate.putString(getStringValueFromElement(InstanceCreationDate));
3093         /* check and set content time if required */
3094         if (ContentTime.isEmpty())
3095             ContentTime.putString(getStringValueFromElement(InstanceCreationTime));
3096     }
3097     /* not all SR IODs contain the SR Document General Module */
3098     if (usesSRDocumentGeneralModule(documentType))
3099     {
3100         /* set preliminary flag */
3101         PreliminaryFlag.putString(preliminaryFlagToEnumeratedValue(PreliminaryFlagEnum));
3102         /* check and adjust completion flag if required */
3103         if (CompletionFlagEnum == CF_invalid)
3104             CompletionFlagEnum = CF_Partial;
3105         CompletionFlag.putString(completionFlagToEnumeratedValue(CompletionFlagEnum));
3106         /* check and adjust verification flag if required */
3107         if (VerificationFlagEnum == VF_invalid)
3108             VerificationFlagEnum = VF_Unverified;
3109         VerificationFlag.putString(verificationFlagToEnumeratedValue(VerificationFlagEnum));
3110     } else {
3111         /* if not, reset the various flags and clear related information */
3112         PreliminaryFlagEnum = PF_invalid;
3113         CompletionFlagEnum = CF_invalid;
3114         VerificationFlagEnum = VF_invalid;
3115         PreliminaryFlag.clear();
3116         CompletionFlag.clear();
3117         CompletionFlagDescription.clear();
3118         VerificationFlag.clear();
3119         VerifyingObserver.clear();
3120     }
3121 }
3122