Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcdatset.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1994-2026, 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:  Gerd Ehlers, Andreas Barth
17
 *
18
 *  Purpose: Implementation of class DcmDataset
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24
25
#include "dcmtk/ofstd/ofstream.h"
26
#include "dcmtk/ofstd/ofstack.h"
27
#include "dcmtk/ofstd/ofstd.h"
28
29
#include "dcmtk/dcmdata/dcjson.h"
30
#include "dcmtk/dcmdata/dcdatset.h"
31
#include "dcmtk/dcmdata/dcxfer.h"
32
#include "dcmtk/dcmdata/dcvrus.h"
33
#include "dcmtk/dcmdata/dcpixel.h"
34
#include "dcmtk/dcmdata/dcdeftag.h"
35
#include "dcmtk/dcmdata/dcistrma.h"    /* for class DcmInputStream */
36
#include "dcmtk/dcmdata/dcistrmf.h"    /* for class DcmInputFileStream */
37
#include "dcmtk/dcmdata/dcistrms.h"    /* for class DcmStdinStream */
38
#include "dcmtk/dcmdata/dcostrma.h"    /* for class DcmOutputStream */
39
#include "dcmtk/dcmdata/dcostrmf.h"    /* for class DcmOutputFileStream */
40
#include "dcmtk/dcmdata/dcostrms.h"    /* for class DcmStdoutStream */
41
#include "dcmtk/dcmdata/dcwcache.h"    /* for class DcmWriteCache */
42
43
44
// ********************************
45
46
47
DcmDataset::DcmDataset()
48
0
  : DcmItem(DCM_ItemTag, DCM_UndefinedLength),
49
0
    OriginalXfer(EXS_Unknown),
50
    // the default transfer syntax is explicit VR with local endianness
51
0
    CurrentXfer((gLocalByteOrder == EBO_BigEndian) ? EXS_BigEndianExplicit : EXS_LittleEndianExplicit)
52
0
{
53
0
}
54
55
56
DcmDataset& DcmDataset::operator=(const DcmDataset& obj)
57
0
{
58
0
  if (this != &obj)
59
0
  {
60
    // copy parent's member variables
61
0
    DcmItem::operator=(obj);
62
    // copy DcmDataset's member variables
63
0
    OriginalXfer = obj.OriginalXfer;
64
0
    CurrentXfer = obj.CurrentXfer;
65
0
  }
66
0
  return *this;
67
0
}
68
69
70
DcmDataset::DcmDataset(const DcmDataset &old)
71
0
  : DcmItem(old),
72
0
    OriginalXfer(old.OriginalXfer),
73
0
    CurrentXfer(old.CurrentXfer)
74
0
{
75
0
}
76
77
78
OFCondition DcmDataset::copyFrom(const DcmObject& rhs)
79
0
{
80
0
  if (this != &rhs)
81
0
  {
82
0
    if (rhs.ident() != ident()) return EC_IllegalCall;
83
0
    *this = OFstatic_cast(const DcmDataset &, rhs);
84
0
  }
85
0
  return EC_Normal;
86
0
}
87
88
89
DcmDataset::~DcmDataset()
90
0
{
91
0
}
92
93
94
// ********************************
95
96
97
OFCondition DcmDataset::clear()
98
0
{
99
0
    OFCondition result = DcmItem::clear();
100
    // TODO: should we also reset OriginalXfer and CurrentXfer?
101
0
    setLengthField(DCM_UndefinedLength);
102
0
    return result;
103
0
}
104
105
DcmEVR DcmDataset::ident() const
106
0
{
107
0
    return EVR_dataset;
108
0
}
109
110
111
E_TransferSyntax DcmDataset::getOriginalXfer() const
112
0
{
113
0
    return OriginalXfer;
114
0
}
115
116
117
E_TransferSyntax DcmDataset::getCurrentXfer() const
118
0
{
119
0
    return CurrentXfer;
120
0
}
121
122
123
void DcmDataset::updateOriginalXfer()
124
0
{
125
0
    DcmStack resultStack;
126
    /* Check for pixel data element on main dataset level only. */
127
    /* Icon images and other nested pixel data elements are not checked. */
128
0
    if (search(DCM_PixelData, resultStack, ESM_fromHere, OFFalse).good())
129
0
    {
130
0
        if (resultStack.top()->ident() == EVR_PixelData)
131
0
        {
132
            /* determine the transfer syntax of the original and current representation */
133
0
            E_TransferSyntax repType = EXS_Unknown;
134
0
            const DcmRepresentationParameter *repParam = NULL;
135
0
            DcmPixelData *pixelData = OFstatic_cast(DcmPixelData *, resultStack.top());
136
0
            pixelData->getOriginalRepresentationKey(OriginalXfer, repParam);
137
0
            pixelData->getCurrentRepresentationKey(repType, repParam);
138
            /* check whether we also need to change the current transfer syntax */
139
0
            if (repType == EXS_LittleEndianExplicit /* default */)
140
0
            {
141
                /* only change the value if not already uncompressed */
142
0
                if ((CurrentXfer != EXS_LittleEndianImplicit) &&
143
0
                    (CurrentXfer != EXS_LittleEndianExplicit) &&
144
0
                    (CurrentXfer != EXS_BigEndianExplicit))
145
0
                {
146
0
                    CurrentXfer = repType;
147
0
                }
148
0
            }
149
0
            else if (repType != EXS_Unknown)
150
0
            {
151
0
                CurrentXfer = repType;
152
0
            }
153
0
        } else {
154
            /* something is fishy with the pixel data element (wrong class) */
155
0
            DCMDATA_WARN("DcmDataset: Wrong class for pixel data element, cannot update original transfer syntax");
156
0
        }
157
0
    }
158
    /* if no pixel data was found, update only in case of unknown representation */
159
0
    else
160
0
    {
161
0
        if (OriginalXfer == EXS_Unknown)
162
0
        {
163
            /* this is also the default in DcmPixelData::getOriginalRepresentationKey() */
164
0
            OriginalXfer = EXS_LittleEndianExplicit;
165
0
        }
166
0
        if (CurrentXfer == EXS_Unknown)
167
0
        {
168
            /* this is also the default in DcmPixelData::getCurrentRepresentationKey() */
169
0
            CurrentXfer = EXS_LittleEndianExplicit;
170
0
        }
171
0
    }
172
0
}
173
174
175
void DcmDataset::removeInvalidGroups(const OFBool cmdSet)
176
0
{
177
0
    DcmStack stack;
178
0
    DcmObject *object = NULL;
179
    /* check for data or command set */
180
0
    if (cmdSet)
181
0
    {
182
        /* iterate over all elements */
183
0
        while (nextObject(stack, OFTrue).good())
184
0
        {
185
0
            object = stack.top();
186
            /* in command sets, only group 0x0000 is allowed */
187
0
            if (object->getGTag() != 0x0000)
188
0
            {
189
0
                DCMDATA_DEBUG("DcmDataset::removeInvalidGroups() removing element "
190
0
                    << object->getTag() << " from command set");
191
0
                stack.pop();
192
                /* remove element from command set and free memory */
193
0
                delete OFstatic_cast(DcmItem *, stack.top())->remove(object);
194
0
            }
195
0
        }
196
0
    } else {
197
        /* iterate over all elements */
198
0
        while (nextObject(stack, OFTrue).good())
199
0
        {
200
0
            object = stack.top();
201
            /* in data sets, group 0x0000 to 0x0003, 0x0005, 0x0007 and 0xFFFF are not allowed */
202
0
            if ((object->getGTag() == 0x0000) || (object->getGTag() == 0x0002) ||
203
0
                !object->getTag().hasValidGroup())
204
0
            {
205
0
                DCMDATA_DEBUG("DcmDataset::removeInvalidGroups() removing element "
206
0
                    << object->getTag() << " from data set");
207
0
                stack.pop();
208
                /* remove element from data set and free memory */
209
0
                delete OFstatic_cast(DcmItem *, stack.top())->remove(object);
210
0
            }
211
            /* in sequence items, also group 0x0006 is not allowed */
212
0
            else if ((stack.card() > 2) && (object->getGTag() == 0x0006))
213
0
            {
214
0
                DCMDATA_DEBUG("DcmDataset::removeInvalidGroups() removing element "
215
0
                    << object->getTag() << " from sequence item");
216
0
                stack.pop();
217
                /* remove element from data set and free memory */
218
0
                delete OFstatic_cast(DcmItem *, stack.top())->remove(object);
219
0
            }
220
0
        }
221
0
    }
222
0
}
223
224
225
// ********************************
226
227
228
Uint32 DcmDataset::calcElementLength(const E_TransferSyntax xfer,
229
                                     const E_EncodingType enctype)
230
0
{
231
0
    return DcmItem::getLength(xfer, enctype);
232
0
}
233
234
235
// ********************************
236
237
238
OFBool DcmDataset::canWriteXfer(const E_TransferSyntax newXfer,
239
                                const E_TransferSyntax oldXfer)
240
0
{
241
0
    if (newXfer == EXS_Unknown)
242
0
        return OFFalse;
243
244
    /* Check stream compression for this transfer syntax */
245
0
    DcmXfer xf(newXfer);
246
0
    if (xf.getStreamCompression() == ESC_unsupported)
247
0
        return OFFalse;
248
249
0
    return DcmItem::canWriteXfer(newXfer, (OriginalXfer == EXS_Unknown) ? oldXfer : OriginalXfer);
250
0
}
251
252
253
// ********************************
254
255
256
void DcmDataset::print(STD_NAMESPACE ostream &out,
257
                       const size_t flags,
258
                       const int level,
259
                       const char *pixelFileName,
260
                       size_t *pixelCounter)
261
0
{
262
0
    out << OFendl;
263
0
    if (flags & DCMTypes::PF_useANSIEscapeCodes)
264
0
        out << DCMDATA_ANSI_ESCAPE_CODE_COMMENT;
265
0
    printNestingLevel(out, flags, level);
266
0
    out << "# Dicom-Data-Set" << OFendl;
267
0
    if (flags & DCMTypes::PF_useANSIEscapeCodes)
268
0
        out << DCMDATA_ANSI_ESCAPE_CODE_COMMENT;
269
0
    printNestingLevel(out, flags, level);
270
0
    out << "# Used TransferSyntax: " << DcmXfer(CurrentXfer).getXferName();
271
0
    if (flags & DCMTypes::PF_useANSIEscapeCodes)
272
0
        out << DCMDATA_ANSI_ESCAPE_CODE_RESET;
273
0
    out << OFendl;
274
0
    if (!elementList->empty())
275
0
    {
276
0
        DcmObject *dO;
277
0
        elementList->seek(ELP_first);
278
0
        do {
279
0
            dO = elementList->get();
280
0
            dO->print(out, flags, level + 1, pixelFileName, pixelCounter);
281
0
        } while (elementList->seek(ELP_next));
282
0
    }
283
0
}
284
285
286
// ********************************
287
288
289
OFCondition DcmDataset::writeXML(STD_NAMESPACE ostream &out,
290
                                 const size_t flags)
291
0
{
292
0
    OFCondition l_error = EC_Normal;
293
    /* the Native DICOM Model as defined for Application Hosting needs special handling */
294
0
    if (flags & DCMTypes::XF_useNativeModel)
295
0
    {
296
        /* write XML start tag */
297
0
        out << "<NativeDicomModel xml:space=\"preserve\"";
298
0
        if (flags & DCMTypes::XF_useXMLNamespace)
299
0
            out << " xmlns=\"" << NATIVE_DICOM_MODEL_XML_NAMESPACE_URI << "\"";
300
0
        out << ">" << OFendl;
301
0
    } else {
302
        /* DCMTK-specific output format (default) */
303
0
        OFString xmlString;
304
0
        DcmXfer xfer(CurrentXfer);
305
        /* write XML start tag */
306
0
        out << "<data-set xfer=\"" << xfer.getXferID() << "\"";
307
0
        out << " name=\"" << OFStandard::convertToMarkupString(xfer.getXferName(), xmlString) << "\"";
308
0
        if (flags & DCMTypes::XF_useXMLNamespace)
309
0
            out << " xmlns=\"" << DCMTK_XML_NAMESPACE_URI << "\"";
310
0
        out << ">" << OFendl;
311
0
    }
312
    /* write dataset content */
313
0
    if (!elementList->empty())
314
0
    {
315
        /* write content of all children */
316
0
        DcmObject *dO;
317
0
        elementList->seek(ELP_first);
318
0
        do {
319
0
            dO = elementList->get();
320
0
            l_error = dO->writeXML(out, flags & ~DCMTypes::XF_useXMLNamespace);
321
0
        } while (l_error.good() && elementList->seek(ELP_next));
322
0
    }
323
0
    if (l_error.good())
324
0
    {
325
        /* write XML end tag (depending on output format) */
326
0
        if (flags & DCMTypes::XF_useNativeModel)
327
0
        {
328
0
            out << "</NativeDicomModel>" << OFendl;
329
0
        } else {
330
0
            out << "</data-set>" << OFendl;
331
0
        }
332
0
    }
333
0
    return l_error;
334
0
}
335
336
337
// ********************************
338
339
340
OFCondition DcmDataset::writeJson(STD_NAMESPACE ostream &out,
341
                                  DcmJsonFormat &format)
342
0
{
343
0
    return writeJsonExt(out, format, OFFalse, OFFalse); // omit braces
344
0
}
345
346
347
// ********************************
348
349
350
OFCondition DcmDataset::read(DcmInputStream &inStream,
351
                             const E_TransferSyntax xfer,
352
                             const E_GrpLenEncoding glenc,
353
                             const Uint32 maxReadLength)
354
0
{
355
0
  return DcmDataset::readUntilTag(inStream, xfer, glenc, maxReadLength, DCM_UndefinedTagKey);
356
0
}
357
358
OFCondition DcmDataset::readUntilTag(DcmInputStream &inStream,
359
                                     const E_TransferSyntax xfer,
360
                                     const E_GrpLenEncoding glenc,
361
                                     const Uint32 maxReadLength,
362
                                     const DcmTagKey &stopParsingAtElement)
363
0
{
364
    /* check if the stream variable reported an error */
365
0
    errorFlag = inStream.status();
366
    /* if the stream did not report an error but the stream */
367
    /* is empty, set the error flag correspondingly */
368
0
    if (errorFlag.good() && inStream.eos())
369
0
        errorFlag = EC_EndOfStream;
370
    /* else if the stream did not report an error but the transfer */
371
    /* state does not equal ERW_ready, go ahead and do something */
372
0
    else if (errorFlag.good() && getTransferState() != ERW_ready)
373
0
    {
374
        /* if the transfer state is ERW_init, go ahead and check the transfer syntax which was passed */
375
0
        if (getTransferState() == ERW_init)
376
0
        {
377
0
            if (dcmAutoDetectDatasetXfer.get())
378
0
            {
379
0
                DCMDATA_DEBUG("DcmDataset::read() automatic detection of transfer syntax is enabled");
380
                /* To support incorrectly encoded datasets detect the transfer syntax from the stream.  */
381
                /* This is possible for given unknown and plain big or little endian transfer syntaxes. */
382
0
                switch (xfer)
383
0
                {
384
0
                    case EXS_Unknown:
385
0
                    case EXS_LittleEndianImplicit:
386
0
                    case EXS_LittleEndianExplicit:
387
0
                    case EXS_BigEndianExplicit:
388
0
                    case EXS_BigEndianImplicit:
389
0
                        DCMDATA_DEBUG("DcmDataset::read() trying to detect transfer syntax of uncompressed data set");
390
0
                        OriginalXfer = checkTransferSyntax(inStream);
391
0
                        if ((xfer != EXS_Unknown) && (OriginalXfer != xfer))
392
0
                            DCMDATA_WARN("DcmDataset: Wrong transfer syntax specified, detecting from data set");
393
0
                        break;
394
0
                    default:
395
0
                        DCMDATA_DEBUG("DcmDataset::read() data set seems to be compressed, so transfer syntax is not detected");
396
0
                        OriginalXfer = xfer;
397
0
                        break;
398
0
                }
399
0
            }
400
0
            else /* default behavior */
401
0
            {
402
                /* If the transfer syntax which was passed equals EXS_Unknown we want to */
403
                /* determine the transfer syntax from the information in the stream itself. */
404
                /* If the transfer syntax is given, we want to use it. */
405
0
                if (xfer == EXS_Unknown)
406
0
                {
407
0
                    DCMDATA_DEBUG("DcmDataset::read() trying to detect transfer syntax of data set (because it is unknown)");
408
0
                    OriginalXfer = checkTransferSyntax(inStream);
409
0
                } else
410
0
                    OriginalXfer = xfer;
411
0
            }
412
            /* dump information on debug level */
413
0
            DCMDATA_DEBUG("DcmDataset::read() TransferSyntax=\""
414
0
                << DcmXfer(OriginalXfer).getXferName() << "\"");
415
0
            CurrentXfer = OriginalXfer;
416
            /* check stream compression for this transfer syntax */
417
0
            DcmXfer xf(OriginalXfer);
418
0
            E_StreamCompression sc = xf.getStreamCompression();
419
0
            switch (sc)
420
0
            {
421
0
                case ESC_none:
422
                  // nothing to do
423
0
                  break;
424
0
                case ESC_unsupported:
425
                  // stream compressed transfer syntax that we cannot create; bail out.
426
0
                  if (errorFlag.good())
427
0
                      errorFlag = EC_UnsupportedEncoding;
428
0
                  break;
429
0
                default:
430
                  // supported stream compressed transfer syntax, install filter
431
0
                  errorFlag = inStream.installCompressionFilter(sc);
432
0
                  break;
433
0
            }
434
0
        }
435
        /* pass processing the task to class DcmItem */
436
0
        if (errorFlag.good())
437
0
            errorFlag = DcmItem::readUntilTag(inStream, OriginalXfer, glenc, maxReadLength, stopParsingAtElement);
438
439
0
    }
440
441
    /* if the error flag shows ok or that the end of the stream was encountered, */
442
    /* we have read information for this particular data set or command; in this */
443
    /* case, we need to do something for the current dataset object */
444
0
    if (errorFlag.good() || errorFlag == EC_EndOfStream)
445
0
    {
446
        /* perform some final checks on dataset level */
447
0
        errorFlag = doPostReadChecks();
448
449
0
        if (errorFlag.good())
450
0
        {
451
            /* set the error flag to ok */
452
0
            errorFlag = EC_Normal;
453
454
            /* take care of group length (according to what is specified */
455
            /* in glenc) and padding elements (don't change anything) */
456
0
            computeGroupLengthAndPadding(glenc, EPD_noChange, OriginalXfer);
457
458
            /* and set the transfer state to ERW_ready to indicate that the data set is complete */
459
0
            setTransferState(ERW_ready);
460
0
        }
461
0
    }
462
463
    /* dump information if required */
464
0
    DCMDATA_TRACE("DcmDataset::read() returns error = " << errorFlag.text());
465
466
    /* return result flag */
467
0
    return errorFlag;
468
0
}
469
470
471
// ********************************
472
473
474
OFCondition DcmDataset::write(DcmOutputStream &outStream,
475
                              const E_TransferSyntax oxfer,
476
                              const E_EncodingType enctype /* = EET_UndefinedLength */,
477
                              DcmWriteCache *wcache)
478
0
{
479
0
    return write(outStream, oxfer, enctype, wcache, EGL_recalcGL);
480
0
}
481
482
483
OFCondition DcmDataset::write(DcmOutputStream &outStream,
484
                              const E_TransferSyntax oxfer,
485
                              const E_EncodingType enctype,
486
                              DcmWriteCache *wcache,
487
                              const E_GrpLenEncoding glenc,
488
                              const E_PaddingEncoding padenc,
489
                              const Uint32 padlen,
490
                              const Uint32 subPadlen,
491
                              Uint32 instanceLength)
492
0
{
493
  /* if the transfer state of this is not initialized, this is an illegal call */
494
0
  if (getTransferState() == ERW_notInitialized)
495
0
    errorFlag = EC_IllegalCall;
496
0
  else
497
0
  {
498
    /* check if the stream reported an error so far; if not, we can go ahead and write some data to it */
499
0
    errorFlag = outStream.status();
500
501
0
    if (errorFlag.good() && getTransferState() != ERW_ready)
502
0
    {
503
      /* Determine the transfer syntax which shall be used. Either we use the one which was passed, */
504
      /* or (if it's an unknown transfer syntax) we use the one which is contained in OriginalXfer. */
505
0
      E_TransferSyntax newXfer = oxfer;
506
0
      if (newXfer == EXS_Unknown)
507
0
        newXfer = OriginalXfer;
508
509
      /* if this function was called for the first time for the dataset object, the transferState is still */
510
      /* set to ERW_init. In this case, we need to take care of group length and padding elements according */
511
      /* to the strategies which are specified in glenc and padenc. Additionally, we need to set the element */
512
      /* list pointer of this data set to the fist element and we need to set the transfer state to ERW_inWork */
513
      /* so that this scenario will only be executed once for this data set object. */
514
0
      if (getTransferState() == ERW_init)
515
0
      {
516
517
        /* Check stream compression for this transfer syntax */
518
0
        DcmXfer xf(newXfer);
519
0
        E_StreamCompression sc = xf.getStreamCompression();
520
0
        switch (sc)
521
0
        {
522
0
          case ESC_none:
523
            // nothing to do
524
0
            break;
525
0
          case ESC_unsupported:
526
            // stream compressed transfer syntax that we cannot create; bail out.
527
0
            if (errorFlag.good())
528
0
              errorFlag = EC_UnsupportedEncoding;
529
0
            break;
530
0
          default:
531
            // supported stream compressed transfer syntax, install filter
532
0
            errorFlag = outStream.installCompressionFilter(sc);
533
0
            break;
534
0
        }
535
536
        /* take care of group length and padding elements, according to what is specified in glenc and padenc */
537
0
        computeGroupLengthAndPadding(glenc, padenc, newXfer, enctype, padlen, subPadlen, instanceLength);
538
0
        elementList->seek(ELP_first);
539
0
        setTransferState(ERW_inWork);
540
0
      }
541
542
      /* if the transfer state is set to ERW_inWork, we need to write the information which */
543
      /* is included in this data set's element list into the buffer which was passed. */
544
0
      if (getTransferState() == ERW_inWork)
545
0
      {
546
        // Remember that elementList->get() can be NULL if buffer was full after
547
        // writing the last item but before writing the sequence delimitation.
548
0
        if (!elementList->empty() && (elementList->get() != NULL))
549
0
        {
550
          /* as long as everything is ok, go through all elements of this data */
551
          /* set and write the corresponding information to the buffer */
552
0
          DcmObject *dO;
553
0
          do
554
0
          {
555
0
            dO = elementList->get();
556
0
            errorFlag = dO->write(outStream, newXfer, enctype, wcache);
557
0
          } while (errorFlag.good() && elementList->seek(ELP_next));
558
0
        }
559
560
        /* if all the information in this has been written to the */
561
        /* buffer set this data set's transfer state to ERW_ready */
562
0
        if (errorFlag.good())
563
0
        {
564
0
          setTransferState(ERW_ready);
565
0
          CurrentXfer = newXfer;
566
0
        }
567
0
      }
568
0
    }
569
0
  }
570
571
  /* return the corresponding result value */
572
0
  return errorFlag;
573
0
}
574
575
576
// ********************************
577
578
579
OFCondition DcmDataset::writeSignatureFormat(DcmOutputStream &outStream,
580
                                             const E_TransferSyntax oxfer,
581
                                             const E_EncodingType enctype,
582
                                             DcmWriteCache *wcache)
583
0
{
584
0
  if (getTransferState() == ERW_notInitialized)
585
0
    errorFlag = EC_IllegalCall;
586
0
  else
587
0
  {
588
0
    E_TransferSyntax newXfer = oxfer;
589
0
    if (newXfer == EXS_Unknown)
590
0
      newXfer = OriginalXfer;
591
592
0
    errorFlag = outStream.status();
593
0
    if (errorFlag.good() && getTransferState() != ERW_ready)
594
0
    {
595
0
      if (getTransferState() == ERW_init)
596
0
      {
597
0
        computeGroupLengthAndPadding(EGL_recalcGL, EPD_noChange, newXfer, enctype, 0, 0, 0);
598
0
        elementList->seek(ELP_first);
599
0
        setTransferState(ERW_inWork);
600
0
      }
601
0
      if (getTransferState() == ERW_inWork)
602
0
      {
603
        // elementList->get() can be NULL if buffer was full after
604
        // writing the last item but before writing the sequence delimitation.
605
0
        if (!elementList->empty() && (elementList->get() != NULL))
606
0
        {
607
0
          DcmObject *dO;
608
0
          do {
609
0
            dO = elementList->get();
610
0
            errorFlag = dO->writeSignatureFormat(outStream, newXfer, enctype, wcache);
611
0
          } while (errorFlag.good() && elementList->seek(ELP_next));
612
0
        }
613
0
        if (errorFlag.good())
614
0
        {
615
0
          setTransferState(ERW_ready);
616
0
          CurrentXfer = newXfer;
617
0
        }
618
0
      }
619
0
    }
620
0
  }
621
0
  return errorFlag;
622
0
}
623
624
625
// ********************************
626
627
628
OFCondition DcmDataset::loadFile(const OFFilename &fileName,
629
                                 const E_TransferSyntax readXfer,
630
                                 const E_GrpLenEncoding groupLength,
631
                                 const Uint32 maxReadLength)
632
0
{
633
0
  return DcmDataset::loadFileUntilTag(fileName, readXfer, groupLength, maxReadLength, DCM_UndefinedTagKey);
634
0
}
635
636
OFCondition DcmDataset::loadFileUntilTag(const OFFilename &fileName,
637
                                 const E_TransferSyntax readXfer,
638
                                 const E_GrpLenEncoding groupLength,
639
                                 const Uint32 maxReadLength,
640
                                 const DcmTagKey &stopParsingAtElement)
641
0
{
642
0
    OFCondition l_error = EC_InvalidFilename;
643
    /* check parameters first */
644
0
    if (!fileName.isEmpty())
645
0
    {
646
0
        if (fileName.isStandardStream())
647
0
        {
648
            /* use stdin stream */
649
0
            DcmStdinStream inStream;
650
            /* apply configured nesting depth limit */
651
0
            if (getMaxNestingDepth() > 0)
652
0
                inStream.setMaxNestingDepth(getMaxNestingDepth());
653
654
            /* clear this object */
655
0
            l_error = clear();
656
0
            if (l_error.good())
657
0
            {
658
                /* initialize transfer */
659
0
                transferInit();
660
661
0
                do
662
0
                {
663
                  /* fill the buffer from stdin */
664
0
                  inStream.fillBuffer();
665
                  /* and read the buffer content into the DICOM dataset */
666
0
                  l_error = readUntilTag(inStream, readXfer, groupLength, maxReadLength, stopParsingAtElement);
667
0
                } while (l_error == EC_StreamNotifyClient); /* repeat until we're at the end of the stream, or an error occurs */
668
669
                /* end transfer */
670
0
                transferEnd();
671
0
            }
672
673
0
        } else {
674
            /* open file for input */
675
0
            DcmInputFileStream fileStream(fileName);
676
            /* apply configured nesting depth limit */
677
0
            if (getMaxNestingDepth() > 0)
678
0
                fileStream.setMaxNestingDepth(getMaxNestingDepth());
679
680
            /* check stream status */
681
0
            l_error = fileStream.status();
682
683
0
            if (l_error.good())
684
0
            {
685
                /* clear this object */
686
0
                l_error = clear();
687
0
                if (l_error.good())
688
0
                {
689
                    /* read data from file */
690
0
                    transferInit();
691
0
                    l_error = readUntilTag(fileStream, readXfer, groupLength, maxReadLength, stopParsingAtElement);
692
0
                    transferEnd();
693
0
                }
694
0
            }
695
696
0
        }
697
0
    }
698
0
    return l_error;
699
0
}
700
701
702
OFCondition DcmDataset::saveFile(const OFFilename &fileName,
703
                                 const E_TransferSyntax writeXfer,
704
                                 const E_EncodingType encodingType,
705
                                 const E_GrpLenEncoding groupLength,
706
                                 const E_PaddingEncoding padEncoding,
707
                                 const Uint32 padLength,
708
                                 const Uint32 subPadLength)
709
0
{
710
0
    OFCondition l_error = EC_InvalidFilename;
711
    /* check parameters first */
712
0
    if (!fileName.isEmpty())
713
0
    {
714
0
        DcmWriteCache wcache;
715
0
        DcmOutputStream *outStream = NULL;
716
0
        DcmOutputFileStream *fileStream = NULL;
717
718
0
        if (fileName.isStandardStream())
719
0
        {
720
            /* use stdout stream */
721
0
            outStream = new DcmStdoutStream(fileName);
722
0
        } else {
723
            /* open file for output */
724
0
            fileStream = new DcmOutputFileStream(fileName);
725
0
            outStream = fileStream;
726
0
        }
727
728
        /* check stream status */
729
0
        l_error = outStream->status();
730
0
        if (l_error.good())
731
0
        {
732
            /* write data to file */
733
0
            transferInit();
734
0
            l_error = write(*outStream, writeXfer, encodingType, &wcache, groupLength, padEncoding, padLength, subPadLength);
735
0
            transferEnd();
736
0
        }
737
0
        if (l_error.good() && fileStream) l_error = fileStream->fclose();
738
0
        delete outStream;
739
0
    }
740
0
    return l_error;
741
0
}
742
743
744
// ********************************
745
746
747
OFCondition DcmDataset::chooseRepresentation(const E_TransferSyntax repType,
748
                                             const DcmRepresentationParameter *repParam)
749
0
{
750
0
    OFCondition l_error = EC_Normal;
751
0
    OFBool pixelDataEncountered = OFFalse;
752
0
    OFBool pixelURLEncountered = OFFalse;
753
0
    OFStack<DcmStack> pixelStack;
754
0
    DcmXfer torep(repType);
755
0
    DcmXfer fromrep(CurrentXfer);
756
757
0
    DcmStack resultStack;
758
0
    resultStack.push(this);
759
760
    // check if we are attempting to compress or decompress, but the image contains
761
    // floating point or double floating point pixel data, which our codecs don't
762
    // support
763
0
    if ((tagExists(DCM_FloatPixelData, OFTrue) || tagExists(DCM_DoubleFloatPixelData, OFTrue)) &&
764
0
         (fromrep.isPixelDataCompressed() || torep.isPixelDataCompressed()))
765
0
    {
766
0
        DCMDATA_ERROR("DcmDataset: Unable to compress/decompress floating point pixel data, cannot change representation");
767
0
        return EC_CannotChangeRepresentation;
768
0
    }
769
770
    // Check if we are attempting to convert a data set containing a pixel data
771
    // provider URL in the top-level data set (i.e., from "Image Pixel Module").
772
    // In that case, we only continue if the target transfer syntax also requires
773
    // a pixel data provider URL.
774
0
    if (tagExists(DCM_PixelDataProviderURL, OFFalse /*searchIntoSub*/))
775
0
    {
776
0
        if (fromrep.usesReferencedPixelData())
777
0
        {
778
0
            if (torep.usesReferencedPixelData())
779
0
            {
780
                // remember that we found a pixel data provider URL element
781
0
                pixelURLEncountered = OFTrue;
782
0
            } else {
783
0
                DCMDATA_ERROR("DcmDataset: Unable to " << (torep.isPixelDataCompressed() ? "compress" : "decompress")
784
0
                    << " image containing a pixel data provider URL in the top-level data set, cannot change representation");
785
0
                return EC_CannotChangeRepresentation;
786
0
            }
787
0
        } else {
788
            // ignore the pixel data provider URL since the source transfer syntax
789
            // does not allow it (not one of the JPIP referenced transfer syntaxes)
790
0
            DCMDATA_WARN("DcmDataset: Ignoring pixel data provider URL in a data set with transfer syntax \""
791
0
                << fromrep.getXferName() << "\"");
792
0
        }
793
0
    }
794
795
    // Now, search for all pixel data elements in this data set. If successful,
796
    // the 'resultStack' contains at least two pointers: one to the pixel data
797
    // element and one to the item or data set this element is contained in.
798
0
    while (search(DCM_PixelData, resultStack, ESM_afterStackTop, OFTrue).good() && l_error.good())
799
0
    {
800
0
        pixelDataEncountered = OFTrue;
801
0
        if (resultStack.top()->ident() == EVR_PixelData)
802
0
        {
803
            // if there are both pixel data and pixel data provider URL elements
804
            // in the top-level data set, return with an error
805
0
            if (pixelURLEncountered && (resultStack.elem(1)->ident() == EVR_dataset))
806
0
            {
807
0
                DCMDATA_ERROR("DcmDataset: Found both pixel data and pixel data provider URL in the top-level data set,"
808
0
                    << " cannot change representation");
809
0
                l_error = EC_CannotChangeRepresentation;
810
0
            } else {
811
                // check whether changing the encoding of the pixel data element would work
812
0
                DcmPixelData *pixelData = OFstatic_cast(DcmPixelData *, resultStack.top());
813
0
                if (!pixelData->canChooseRepresentation(repType, repParam))
814
0
                    l_error = EC_CannotChangeRepresentation;
815
0
                pixelStack.push(resultStack);
816
0
            }
817
0
        } else {
818
            // something is fishy with the pixel data element (wrong class)
819
0
            DCMDATA_ERROR("DcmDataset: Wrong class for pixel data element, cannot change representation");
820
0
            l_error = EC_CannotChangeRepresentation;
821
0
        }
822
0
    }
823
824
    // if there are no pixel data elements in the data set, issue a warning
825
0
    if (!pixelDataEncountered)
826
0
    {
827
0
        if (torep.isPixelDataCompressed() && fromrep.isPixelDataUncompressed())
828
0
        {
829
0
            DCMDATA_WARN("DcmDataset: No pixel data present, nothing to compress");
830
0
        }
831
0
        if (torep.isPixelDataUncompressed() && fromrep.isPixelDataCompressed())
832
0
        {
833
0
            DCMDATA_WARN("DcmDataset: No pixel data present, nothing to decompress");
834
0
        }
835
0
    }
836
837
    // then call the method doing the real work for all pixel data elements found
838
0
    while (l_error.good() && (pixelStack.size() > 0))
839
0
    {
840
0
        l_error = OFstatic_cast(DcmPixelData *, pixelStack.top().top())->
841
0
            chooseRepresentation(repType, repParam, pixelStack.top());
842
843
#ifdef PIXELSTACK_MEMORY_LEAK_WORKAROUND
844
        // On certain platforms there seems to be a memory leak at this point
845
        // since for some reason pixelStack.pop() does not completely destruct
846
        // the DcmStack object taken from the stack.
847
        // The following work-around should solve this issue.
848
        pixelStack.top().clear();
849
#endif
850
851
0
        pixelStack.pop();
852
0
    }
853
854
    // store current transfer syntax (if conversion was successful)
855
0
    if (l_error.good())
856
0
        CurrentXfer = repType;
857
0
    return l_error;
858
0
}
859
860
861
OFBool DcmDataset::hasRepresentation(const E_TransferSyntax repType,
862
                                     const DcmRepresentationParameter *repParam)
863
0
{
864
0
    OFBool result = OFTrue;
865
0
    DcmStack resultStack;
866
867
0
    while(search(DCM_PixelData, resultStack, ESM_afterStackTop, OFTrue).good() && result)
868
0
    {
869
0
        if (resultStack.top()->ident() == EVR_PixelData)
870
0
        {
871
0
            DcmPixelData *pixelData = OFstatic_cast(DcmPixelData *, resultStack.top());
872
0
            result = pixelData->hasRepresentation(repType, repParam);
873
0
        }
874
0
        else
875
0
            result = OFFalse;
876
0
    }
877
0
    return result;
878
0
}
879
880
881
void DcmDataset::removeAllButCurrentRepresentations()
882
0
{
883
0
    DcmStack resultStack;
884
885
0
    while(search(DCM_PixelData, resultStack, ESM_afterStackTop, OFTrue).good())
886
0
    {
887
0
        if (resultStack.top()->ident() == EVR_PixelData)
888
0
        {
889
0
            DcmPixelData *pixelData = OFstatic_cast(DcmPixelData *, resultStack.top());
890
0
            pixelData->removeAllButCurrentRepresentations();
891
0
        }
892
0
    }
893
0
}
894
895
896
void DcmDataset::removeAllButOriginalRepresentations()
897
0
{
898
0
    DcmStack resultStack;
899
900
0
    while(search(DCM_PixelData, resultStack, ESM_afterStackTop, OFTrue).good())
901
0
    {
902
0
        if (resultStack.top()->ident() == EVR_PixelData)
903
0
        {
904
0
            DcmPixelData *pixelData = OFstatic_cast(DcmPixelData *, resultStack.top());
905
0
            pixelData->removeAllButOriginalRepresentations();
906
0
        }
907
0
    }
908
0
}
909
910
911
// ********************************
912
913
914
OFCondition DcmDataset::doPostReadChecks()
915
0
{
916
0
  DcmElement* pixData = NULL;
917
0
  DcmXfer xf(OriginalXfer);
918
0
  OFCondition result = EC_Normal;
919
0
  if (findAndGetElement(DCM_PixelData, pixData).good())
920
0
  {
921
0
      Uint32 valueLength = pixData->getLengthField();
922
0
      if (xf.usesEncapsulatedFormat())
923
0
      {
924
0
          if (valueLength != DCM_UndefinedLength)
925
0
          {
926
0
              if (dcmUseExplLengthPixDataForEncTS.get() == OFFalse /* default case */)
927
0
              {
928
                  /* length of top-level dataset's Pixel Data is explicitly */
929
                  /* defined but we have a transfer syntax requiring */
930
                  /* encapsulated pixel data (always encoded with undefined */
931
                  /* length). Print and return an error. */
932
0
                  DCMDATA_ERROR("Found explicit length Pixel Data in top-level "
933
0
                      << "data set with transfer syntax " << xf.getXferName()
934
0
                      << ": Only undefined length permitted");
935
0
                  result = EC_PixelDataExplLengthIllegal;
936
0
              }
937
0
              else
938
0
              {
939
                  /* Only print warning if requested by related OFGlobal, */
940
                  /* and behave like as we have the same case as for an */
941
                  /* icon image, which is always uncompressed (see above). */
942
0
                  DCMDATA_WARN("Found explicit length Pixel Data in top-level "
943
0
                      << "data set with transfer syntax " << xf.getXferName()
944
0
                      << ": Only undefined length permitted (ignored on explicit request)");
945
0
              }
946
0
          }
947
0
      }
948
0
  }
949
950
0
  return result;
951
0
}
952
953
// ********************************
954
955
void DcmDataset::initializeXfer(const E_TransferSyntax xfer)
956
0
{
957
0
  OriginalXfer = xfer;
958
0
  CurrentXfer = xfer;
959
0
}