1 /*
2  *
3  *  Copyright (C) 2003-2019, 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:  dcmdata
15  *
16  *  Author:  Michael Onken
17  *
18  *  Purpose: Class for modifying DICOM files
19  *
20  */
21 
22 #ifndef MDFDSMAN_H
23 #define MDFDSMAN_H
24 
25 #include "dcmtk/config/osconfig.h"   // make sure OS specific configuration is included first
26 
27 #include "dcmtk/ofstd/ofcond.h"
28 #include "dcmtk/dcmdata/dctagkey.h"
29 #include "dcmtk/dcmdata/dcxfer.h"
30 #include "dcmtk/ofstd/ofcmdln.h"
31 
32 
33 // forward declarations
34 class DcmDataset;
35 class DcmFileFormat;
36 class DcmElement;
37 
38 
39 /** This class encapsulates data structures and operations for modifying
40  *  DICOM files. Therefore it allows the process of load->modify->save to
41  *  provide this service.
42  */
43 class MdfDatasetManager
44 {
45 public:
46 
47     /** Constructor, initializes member-variables
48      */
49     MdfDatasetManager();
50 
51     /** Destructor
52      */
53     ~MdfDatasetManager();
54 
55     /** Loads a file into dataset manager
56      *  @param file_name file to be loaded
57         @param readMode read file with or without metaheader. Default=autodetect
58         @param xfer try to read with this transfer syntax. Default=autodetect
59         @param createIfNecessary If true, the file is created if it does not exist
60      *  @return returns EC_Normal if everything is OK, else an error
61      */
62     OFCondition loadFile(const char *file_name,
63                          const E_FileReadMode readMode = ERM_autoDetect,
64                          const E_TransferSyntax xfer = EXS_Unknown,
65                          const OFBool createIfNecessary = OFFalse);
66 
67     /** Modifies/Inserts a path (with a specific value if desired).
68      *  @param tag_path path to item/element
69      *  @param value denotes new value of tag
70      *  @param only_modify if true, only existing tags are processed. If false,
71      *                     any not existing tag is inserted
72      *  @param update_metaheader updates metaheader UIDs, if related UIDs in
73      *                           dataset are changed (default=true)
74      *  @param ignore_missing_tags if true, tags that could not be found
75      *                             while modifying (only_modify must be true)
76      *                             are handled as non-errors
77      *  @param no_reservation_checks if true, any missing private reservation
78      *                               tags are ignored when inserting private
79      *                               tags. Only makes sense w/o only_modify
80      *  @return returns EC_Normal if everything is OK, else an error
81      */
82     OFCondition modifyOrInsertPath(OFString tag_path,
83                                    const OFString &value,
84                                    const OFBool only_modify,
85                                    const OFBool update_metaheader = OFTrue,
86                                    const OFBool ignore_missing_tags = OFFalse,
87                                    const OFBool no_reservation_checks = OFFalse);
88 
89     /** Modifies/Inserts a path with a specific value read from file
90      *  @param tag_path path to item/element
91      *  @param filename name of the file from which the value should be read
92      *  @param only_modify if true, only existing tags are processed. If false,
93      *                     any not existing tag is inserted
94      *  @param update_metaheader updates metaheader UIDs, if related UIDs in
95      *                           dataset are changed (default=true)
96      *  @param ignore_missing_tags if true, tags that could not be found
97      *                             while modifying (only_modify must be true)
98      *                             are handled as non-errors
99      *  @param no_reservation_checks if true, any missing private reservation
100      *                               tags are ignored when inserting private
101      *                               tags. Only makes sense w/o only_modify
102      *  @return returns EC_Normal if everything is OK, else an error
103      */
104     OFCondition modifyOrInsertFromFile(OFString tag_path,
105                                        const OFString &filename,
106                                        const OFBool only_modify,
107                                        const OFBool update_metaheader = OFTrue,
108                                        const OFBool ignore_missing_tags = OFFalse,
109                                        const OFBool no_reservation_checks = OFFalse);
110 
111     /** Modifies all matching tags in dataset to a new value
112      *  @param tag_path denotes, which tag to modify
113      *  @param value denotes new value of tag
114      *  @param update_metaheader if true, metaheader UIDs are updated,
115      *         if related dataset UIDs are changed, (default=true)
116      *  @param count returns the number of tags that were affected
117      *  @param ignore_missing_tags if true, tags that could not be found
118      *                             while modifying are handled as non-errors
119      *  @return returns EC_Normal if everything is OK, else an error
120      */
121     OFCondition modifyAllTags(OFString tag_path,
122                               const OFString &value,
123                               const OFBool update_metaheader,
124                               int &count,
125                               const OFBool ignore_missing_tags = OFFalse);
126 
127     /** Deletes tag in dataset
128      *  @param tag_path holds complete path to tag
129      *  @param all_tags if true, tag is deleted at all levels of dataset,
130      *                  else only 1st level is accessed
131      *  @param ignore_missing_tags if true, tags that could not be found
132      *                             while modifying are handled as non-errors
133      *  @return returns EC_Normal if everything is OK, else an error
134      */
135     OFCondition deleteTag(OFString tag_path,
136                           const OFBool all_tags,
137                           const OFBool ignore_missing_tags);
138 
139     /** Deletes all private data from file,\ i.e. all tags having
140      *  odd group numbers.
141      *  @return EC_Normal, if deletion was successful; error code otherwise
142      */
143     OFCondition deletePrivateData();
144 
145     /** Inserts new Study, Series or SOP Instance UID. If SOP Instance
146      *  UID is generated, the related tag in the metaheader is deleted, too
147      *  so that the new UID is also applied there when saving to disk later.
148      *  @param uidKey [in] The Instance UID to insert (study, series or sop
149      *                instance UID key permitted).
150      *  @return EC_Normal, if insertion was successful, error otherwise
151      */
152     OFCondition generateAndInsertUID(const DcmTagKey &uidKey);
153 
154      /** Saves current dataset back to a file. Caution: After saving
155      *  MdfDatasetManager keeps working on old filename.
156      *  @param file_name filename to save to
157      *  @param opt_xfer transfer syntax to save to (EXS_Unknown: don't change)
158      *  @param opt_enctype write with explicit or implicit length encoding
159      *  @param opt_glenc option to set group length calculation mode
160      *  @param opt_padenc sets padding option
161      *  @param opt_filepad pad file to a multiple of this options value
162      *  @param opt_itempad pad item to a multiple of this options value
163      *  @param opt_dataset if true, write only dataset, else write fileformat
164      *  @return returns EC_Normal if everything is OK, else an error
165      */
166     OFCondition saveFile(const char *file_name,
167                          E_TransferSyntax opt_xfer = EXS_Unknown,
168                          E_EncodingType opt_enctype = EET_UndefinedLength,
169                          E_GrpLenEncoding opt_glenc = EGL_recalcGL,
170                          E_PaddingEncoding opt_padenc = EPD_noChange,
171                          OFCmdUnsignedInt opt_filepad = 0,
172                          OFCmdUnsignedInt opt_itempad = 0,
173                          OFBool opt_dataset = OFFalse);
174 
175     /** Saves current dataset back to file using original filename and original
176      *  parameters like transfer syntax, padding etc.
177      *  @return returns EC_Normal if everything is OK, else an error
178      */
179     OFCondition saveFile();
180 
181     /** Returns the dataset that this MdfDatasetManager handles.
182      *  You should use the returned object with care to avoid side effects with
183      *  other class methods that modify this object, too.
184      *  @return returns the dataset, this MdfDatasetManager manages and NULL, if
185      *          no dataset is loaded
186      */
187     DcmDataset* getDataset();
188 
189 
190     /** Returns the DcmFileFormat that this MdfDatasetManager handles.
191      *  You should use the returned object with care to avoid side-effects with
192      *  other class methods that modify this object, too.
193      *  @return returns the DcmFileFormat, this MdfDatasetManager manages and
194      *          NULL, if no file is loaded
195      */
196     DcmFileFormat* getFileFormat();
197 
198     /** Returns filename of the file that is currently loaded.
199      *  @return returns filename and "" if no file is loaded.
200      */
201     OFString getFilename() const;
202 
203     /** Sets whether attributes with VR of UN should be modified or
204      *  left alone.
205      *  @param modifyUNValues [in] If set, UN values will be modified (default)
206      */
207     void setModifyUNValues(OFBool modifyUNValues);
208 
209 protected:
210 
211     /** modifies element to a specific value
212      *  @param elem element that should be changed
213      *  @param value the value, the element should be changed to
214      *  @return OFCondition, which returns an error code if an error occurs
215      */
216     OFCondition startModify(DcmElement *elem,
217                             const OFString &value);
218 
219     /** If key is the tag for SOPInstanceUID or SOPClassUID, then this function
220      *  removes the related MediaStorage UIDs from the metaheader. The
221      *  metaheader is then updated "automagically" when the file is saved back to
222      *  disk.
223      *  @param key tag to examine
224      */
225     void deleteRelatedMetaheaderTag(const DcmTagKey &key);
226 
227     /** Returns true, if given tag key can be found in dictionary
228      *  @param search_key tag to lookup
229      *  @return OFTrue if tag could be found, else OFFalse
230      */
231     OFBool isTagInDictionary(const DcmTagKey &search_key);
232 
233 private:
234 
235     /// name of file that is currently loaded
236     OFString current_file;
237 
238     /// will hold file to modify
239     DcmFileFormat *dfile;
240 
241     /// will hold the dataset that should be modified
242     DcmDataset *dset;
243 
244     /// if enabled, no value modifications on attributes having VR of UN
245     /// are not executed
246     OFBool ignore_un_modifies;
247 
248     /** private undefined assignment operator
249      */
250     MdfDatasetManager &operator=(const MdfDatasetManager &);
251 
252     /** private undefined copy constructor
253      */
254     MdfDatasetManager(const MdfDatasetManager &);
255 };
256 
257 #endif // MDFDSMAN_H
258