I'm using log4cplus in my project to do logging.
I created logger.conf and I will load it in the beginning of my application.
This is my logger.conf:
log4cplus.appender.Developer=log4cplus::RollingFileAppender
log4cplus.appender.Developer.DatePattern = ".yyyy-MM-dd"
log4cplus.appender.Developer.Schedule = HOURLY
log4cplus.appender.Developer.File=log/developer.log
log4cplus.appender.Developer.MaxFileSize=3MB
log4cplus.appender.Developer.MaxBackupIndex=10
log4cplus.appender.Developer.layout=log4cplus::PatternLayout
log4cplus.appender.Developer.layout.ContextPrinting=enabled
log4cplus.appender.Developer.layout.ConversionPattern=%D{%Y-%m-%d %H:%M:%S,%Q} [%t] %p - %m%n
log4cplus.appender.Developer.Threshold=TRACE
log4cplus.logger.DEVELOPER=TRACE, Developer
This is how I load my logger.conf:
QString log_path = qApp->applicationDirPath() + "/log";
QDir().mkpath(log_path);
PropertyConfigurator logger(L"configs/logger.conf", Logger::getDefaultHierarchy());
logger.configure();
And whenever I want to log, I use the following line:
Logger::getInstance(L"DEVELOPER").log(INFO_LOG_LEVEL, L"..............");
I'd like to know two things:
How can I tell Log4Cplus to compress the backup logs?
In some post I saw this reply:
I need to create my own appender, inheriting from RollingFileAppender and then add a compression steps.
If it's possible, can anyone tell me how to do it, please? I don't know how to implement this.
How can I add a pattern to the name of these backup logs?
At the moment, Log4Cplus makes my back up like this:
developer.log.1
developer.log.2
developer.log.3
...
I'd like to add date and time to it.
You will need to implement you own Appender like this:
class NewFileAppender : public ::log4cplus::RollingFileAppender
{
void
newFileAppender::rollover()
{
helpers::LogLog & loglog = getLogLog();
// Close the current file
out.close();
out.clear(); // reset flags since the C++ standard specified that all the
// flags should remain unchanged on a close
// If maxBackups <= 0, then there is no file renaming to be done.
if (maxBackupIndex > 0)
{
rolloverCompressedFiles(filename, maxBackupIndex);
// Rename fileName to fileName.DATE
tstring target = filename + DATE;
int ret;
ret = file_rename (filename, target);
//TODO: compress using zlib
}
}
}
Related
I'm trying to display the length of audio files in a Playlist component for an application.
I've not used Juce or C++ before, and I can't understand how to do that from Juce documentation.
I want to make a function that takes an audio file's URL and returns the length in seconds of that audio without playing that file or doing anything else with that file.
I've tried a lot of things, and all of them didn't work, and this is the last thing I've tried:
void PlaylistComponent::trackStats(URL audioURL)
{
AudioFormatManager formatManager;
std::unique_ptr<AudioFormatReaderSource> readerSource;
AudioTransportSource transportSource;
auto* reader = formatManager.createReaderFor(audioURL.createInputStream(false));
if (reader != nullptr)
{
std::unique_ptr<AudioFormatReaderSource> newSource(new AudioFormatReaderSource(reader, true));
transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
readerSource.reset(newSource.release());
DBG("PlaylistComponent::trackStats(URL audioURL): " << transportSource.getLengthInSeconds());
}
else
{
DBG("Something went wrong loading the file");
}
}
And this is the PlaylistComponent header file:
class PlaylistComponent : public juce::Component,
public juce::TableListBoxModel,
public Button::Listener,
public FileDragAndDropTarget
{
...
}
juce::AudioFormatReaderSource has a method called getTotalLength() which returns the total amount of samples.
Divide that by the sample rate of the file and you have the total length in seconds. Something like this:
if (auto* reader = audioFormatReaderSource->getAudioFormatReader())
double lengthInSeconds = static_cast<double> (audioFormatReaderSource->getTotalLength()) / reader->sampleRate;
You can do this very early on in the audio file opening procedure. You only need an AudioFormatReader instance (no need to create an AudioFormatReaderSource):
// create juce::File from a path juce::String (from a drag & drop event etc).
File file{filePath};
// make sure it is a file and not a directory, etc.
if (!file.existsAsFile()) return;
// create the AudioFormatReader that contains the data
AudioFormatReader *reader = formatManagerInstance.createReaderFor(file);
// make sure a valid reader can be created (not an unsupported file)
if (reader == nullptr) return;
// log the length in seconds
std::cout << reader->lengthInSamples / reader->sampleRate << "\n";
Note: for this to work you will need to
have access to your AudioFormatManager instance and
have already registered the format(s) of the file type (usually through a .registerBasicFormats() call on your AudioFormatManager instance.
IMPORTANT: if successful the createReaderFor() method uses new to create a new AudioFormatReaderInstance, so make sure to use delete on it when you are finished using it to avoid memory leaks
I'm trying to use log4cxx to log my application using RollingFileAppender on a Windows C++ console application. I would like to create a new log file every time the size reaches 1MB. Furthermore, when the desired size is reached, the file should be zipped automatically. The maximum number of files created must be 10; after which older files should be overwritten.
I'm using:
apache-log4cxx-0.10.0
apr-util-1.6.1
apr-1.7.0
This is my code:
log4cxx::rolling::RollingFileAppender* fileAppender1 = new log4cxx::rolling::RollingFileAppender();
fileAppender1->setLayout(log4cxx::LayoutPtr(new log4cxx::PatternLayout(L"[%d{ISO8601}{GMT}] %-4r [%t] %c | %-5p | %m%n")));
fileAppender1->setAppend(true);
log4cxx::helpers::Pool p;
fileAppender1->activateOptions(p);
log4cxx::rolling::FixedWindowRollingPolicy* rollingPolicy = new log4cxx::rolling::FixedWindowRollingPolicy();
rollingPolicy->setMinIndex(1);
rollingPolicy->setMaxIndex(10);
rollingPolicy->setFileNamePattern(L"j_log_%i.log");
log4cxx::rolling::SizeBasedTriggeringPolicy* triggerPolicy = new log4cxx::rolling::SizeBasedTriggeringPolicy();
triggerPolicy->setMaxFileSize(1024*1024);
fileAppender1->setRollingPolicy(rollingPolicy);
fileAppender1->setTriggeringPolicy(triggerPolicy);
LoggerPtr logger(Logger::getLogger("LogConsole1"));
logger->addAppender(fileAppender1);
logger->setLevel(log4cxx::Level::getTrace());
for (int i = 0; i < 10000; i++)
{
LOG4CXX_INFO(logger, "Created FileAppender appender");
LOG4CXX_INFO(logger, "LOGGER1");
}
The result obtained is a file named ".1" (without any extension) with such content (it seems ok):
[2019-09-13 07:44:58,619] 21063 [0x00003e14] LogConsole1 | INFO | Created FileAppender appender
[2019-09-13 07:44:58,622] 21066 [0x00003e14] LogConsole1 | INFO | LOGGER1
The problems are:
The file does not have the proper name
The file does not roll over (only one file is created also if its size exceeds 1MB)
On the application console I see many exceptions like: "log4cxx: Exception during rollover"
What am I doing wrong?
I do not completely understand your file pattern but the docs do not use the "L" char in their Pattern.
In my projects is use
rollingPolicy->setFileNamePattern("file.%i.log");
sometimes with a string variable which works good.
I can not find the configuration in your code snipped.
As far as i know, you have to setup the appender by using the BasicConfiguration object.
log4cxx::BasicConfigurator::configure(log4cxx::AppenderPtr(yourAppenderPointer));
this will append your appender to the root logger and works for my case.
Here is my full code snippet of my initialize.
void someclass::initLogger(std::string fileName) {
std::string::size_type found = fileName.find(".log");
std::string strippedFileName;
if (found != std::string::npos)
{
strippedFileName = fileName.substr(0, found);
}
else
{
strippedFileName = fileName;
fileName = fileName + ".log";
}
//initializes for rolling file appenders
rollingFileAppender = new log4cxx::rolling::RollingFileAppender();
rollingPolicy = new log4cxx::rolling::FixedWindowRollingPolicy();
rollingPolicy->setMinIndex(1);
rollingPolicy->setMaxIndex(3);
log4cxx::LogString fileNamePattern = strippedFileName + ".%i.log";
rollingPolicy->setFileNamePattern(fileNamePattern);
trigger = new log4cxx::rolling::SizeBasedTriggeringPolicy();
trigger->setMaxFileSize(1024);
rollingFileAppender->setRollingPolicy(rollingPolicy);
rollingFileAppender->setTriggeringPolicy(trigger);
rollingFileAppender->setLayout(log4cxx::LayoutPtr(new log4cxx::PatternLayout(LOGFILE_LAYOUT_PATTERN)));
rollingFileAppender->setFile(fileName);
rollingFileAppender->setAppend(true);
//initializes for a console appender
consoleAppender = new log4cxx::ConsoleAppender(log4cxx::LayoutPtr(new log4cxx::PatternLayout(LOGFILE_LAYOUT_PATTERN)));
log4cxx::helpers::Pool p;
rollingFileAppender->activateOptions(p);
log4cxx::BasicConfigurator::configure(log4cxx::AppenderPtr(consoleAppender));
log4cxx::BasicConfigurator::configure(log4cxx::AppenderPtr(rollingFileAppender));
}
This code prints to a specified file via a rolling file appender and also prints to the terminal using the consoleAppender
This prints file one file with fileName.log and up to three more with fileName.i.log
Ok, firstly, I'm new to this. So yell at me as much as you like, but try to be useful at the same time :)
So I'm attempting to build a plugin using C++ to find a log file, and upload to a FTP every few minutes. The idea is to allow administrators to see the logs without needing direct access to the server. The ftp was the easy bit of this, running #include <CkFtp2.h> to do most of this with ease. I then used fs::path to find the latest file edited. Which looked like this:
//finding the latest file
int FindFile() {
fs::path latest;
std::time_t latest_tm {};
for (auto&& entry : boost::make_iterator_range(fs::directory_iterator("."), {})) {
fs::path p = entry.path();
if (is_regular_file(p) && p.extension() == ".txt")
{
std::time_t timestamp = fs::last_write_time(p);
if (timestamp > latest_tm) {
latest = p;
latest_tm = timestamp;
}
}
}
}
I now want to define string localFilename = latest however I get error: no viable conversion from 'fs::path' to 'string. Could someone help me?
Check out my github here to see what I'm working on and how I want this to implement with the rest of the code: https://github.com/TGTGamer/sourcebansLogMonitoring/blob/master/SourcebansToFTP.sp
p.s. If i'm being stupid, tell me the answer then slap me round the face....
In regard to Indy 10 of IdHTTP, many things have been running perfectly, but there are a few things that don't work so well here. That is why, once again, I need your help.
Download button has been running perfectly. I'm using the following code :
void __fastcall TForm1::DownloadClick(TObject *Sender)
{
MyFile = SaveDialog->FileName;
TFileStream* Fist = new TFileStream(MyFile, fmCreate | fmShareDenyNone);
Download->Enabled = false;
Urlz = Edit1->Text;
Url->Caption = Urlz;
try
{
IdHTTP->Get(Edit1->Text, Fist);
IdHTTP->Connected();
IdHTTP->Response->ResponseCode = 200;
IdHTTP->ReadTimeout = 70000;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReuseSocket;
Fist->Position = 0;
}
__finally
{
delete Fist;
Form1->Updated();
}
}
However, a "Cancel Resume" button is still can't resume interrupted downloads. Meant, it is always sending back the entire file every time I call Get() though I've used IdHTTP->Request->Ranges property.
I use the following code:
void __fastcall TForm1::CancelResumeClick(TObject *Sender)
{
MyFile = SaveDialog->FileName;;
TFileStream* TFist = new TFileStream(MyFile, fmCreate | fmShareDenyNone);
if (IdHTTP->Connected() == true)
{
IdHTTP->Disconnect();
CancelResume->Caption = "RESUME";
IdHTTP->Response->AcceptRanges = "Bytes";
}
else
{
try {
CancelResume->Caption = "CANCEL";
// IdHTTP->Request->Ranges == "0-100";
// IdHTTP->Request->Range = Format("bytes=%d-",ARRAYOFCONST((TFist->Position)));
IdHTTP->Request->Ranges->Add()->StartPos = TFist->Position;
IdHTTP->Get(Edit1->Text, TFist);
IdHTTP->Request->Referer = Edit1->Text;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReadTimeout = 70000;
}
__finally {
delete TFist;
}
}
Meanwhile, by using the FormatBytes function, found here, has been able to shows only the size of download files. But still unable to determine the speed of download or transfer speed.
I'm using the following code:
void __fastcall TForm1::IdHTTPWork(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount)
{
__int64 Romeo = 0;
Romeo = IdHTTP->Response->ContentStream->Position;
// Romeo = AWorkCount;
Download->Caption = FormatBytes(Romeo) + " (" + IntToStr(Romeo) + " Bytes)";
ForSpeed->Caption = FormatBytes(Romeo);
ProgressBar->Position = AWorkCount;
ProgressBar->Update();
Form1->Updated();
}
Please advise and give an example. Any help would sure be appreciated!
In your DownloadClick() method:
Calling Connected() is useless, since you don't do anything with the result. Nor is there any guarantee that the connection will remain connected, as the server could send a Connection: close response header. I don't see anything in your code that is asking for HTTP keep-alives. Let TIdHTTP manage the connection for you.
You are forcing the Response->ResponseCode to 200. Don't do that. Respect the response code that the server actually sent. The fact that no exception was raised means the response was successful whether it is 200 or 206.
You are reading the ReuseSocket property value and ignoring it.
There is no need to reset the Fist->Position property to 0 before closing the file.
Now, with that said, your CancelResumeClick() method has many issues.
You are using the fmCreate flag when opening the file. If the file already exists, you will overwrite it from scratch, thus TFist->Position will ALWAYS be 0. Use fmOpenReadWrite instead so an existing file will open as-is. And then you have to seek to the end of the file to provide the correct Position to the Ranges header.
You are relying on the socket's Connected() state to make decisions. DO NOT do that. The connection may be gone after the previous response, or may have timed out and been closed before the new request is made. The file can still be resumed either way. HTTP is stateless. It does not matter if the socket remains open between requests, or is closed in between. Every request is self-contained. Use information provided in the previous response to govern the next request. Not the socket state.
You are modifying the value of the Response->AcceptRanges property, instead of using the value provided by the previous response. The server tells you if the file supports resuming, so you have to remember that value, or query it before then attempting to resumed download.
When you actually call Get(), the server may or may not respect the requested Range, depending on whether the requested file supports byte ranges or not. If the server responds with a response code of 206, the requested range is accepted, and the server sends ONLY the requested bytes, so you need to APPEND them to your existing file. However, if the server response with a response code of 200, the server is sending the entire file from scratch, so you need to REPLACE your existing file with the new bytes. You are not taking that into account.
In your IdHTTPWork() method, in order to calculate the download/transfer speed, you have to keep track of how many bytes are actually being transferred in between each event firing. When the event is fired, save the current AWorkCount and tick count, and then the next time the event is fired, you can compare the new AWorkCount and current ticks to know how much time has elapsed and how many bytes were transferred. From those value, you can calculate the speed, and even the estimated time remaining.
As for your progress bar, you can't use AWorkCount alone to calculate a new position. That only works if you set the progress bar's Max to AWorkCountMax in the OnWorkBegin event, and that value is not always know before a download begins. You need to take into account the size of the file being downloaded, whether it is being downloaded fresh or being resumed, how many bytes are being requested during a resume, etc. So there is lot more work involved in displaying a progress bar for a HTTP download.
Now, to answer your two questions:
How to retrieve and save the download file to a disk by using its original name?
It is provided by the server in the filename parameter of the Content-Disposition header, and/or in the name parameter of the Content-Type header. If neither value is provided by the server, you can use the filename that is in the URL you are requesting. TIdHTTP has a URL property that provides the parsed version of the last requested URL.
However, since you are creating the file locally before sending your download request, you will have to create a local file using a temp filename, and then rename the local file after the download is complete. Otherwise, use TIdHTTP.Head() to determine the real filename (you can also use it to determine if resuming is supported) before creating the local file with that filename, then use TIdHTTP.Get() to download to that local file. Otherwise, download the file to memory using TMemoryStream instead of TFileStream, and then save with the desired filename when complete.
when I click http://get.videolan.org/vlc/2.2.1/win32/vlc-2.2.1-win32.exe then the server will process requests to its actual url. http://mirror.vodien.com/videolan/vlc/2.2.1/win32/vlc-2.2.1-win32.exe. The problem is that IdHTTP will not automatically grab through it.
That is because VideoLan is not using an HTTP redirect to send clients to the real URL (TIdHTTP supports HTTP redirects). VideoLan is using an HTML redirect instead (TIdHTTP does not support HTML redirects). When a webbrowser downloads the first URL, a 5 second countdown timer is displayed before the real download then begins. As such, you will have to manually detect that the server is sending you an HTML page instead of the real file (look at the TIdHTTP.Response.ContentType property for that), parse the HTML to determine the real URL, and then download it. This also means that you cannot download the first URL directly into your target local file, otherwise you will corrupt it, especially during a resume. You have to cache the server's response first, either to a temp file or to memory, so you can analyze it before deciding how to act on it. It also means you have to remember the real URL for resuming, you cannot resume the download using the original countdown URL.
Try something more like the following instead. It does not take into account for everything mentioned above (particularly speed/progress tracking, HTML redirects, etc), but should get you a little closer:
void __fastcall TForm1::DownloadClick(TObject *Sender)
{
Urlz = Edit1->Text;
Url->Caption = Urlz;
IdHTTP->Head(Urlz);
String FileName = IdHTTP->Response->RawHeaders->Params["Content-Disposition"]["filename"];
if (FileName.IsEmpty())
{
FileName = IdHTTP->Response->RawHeaders->Params["Content-Type"]["name"];
if (FileName.IsEmpty())
FileName = IdHTTP->URL->Document;
}
SaveDialog->FileName = FileName;
if (!SaveDialog->Execute()) return;
MyFile = SaveDialog->FileName;
TFileStream* Fist = new TFileStream(MyFile, fmCreate | fmShareDenyWrite);
try
{
try
{
Download->Enabled = false;
Resume->Enabled = false;
IdHTTP->Request->Clear();
//...
IdHTTP->ReadTimeout = 70000;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->Get(Urlz, Fist);
}
__finally
{
delete Fist;
Download->Enabled = true;
Updated();
}
}
catch (const EIdHTTPProtocolException &)
{
DeleteFile(MyFile);
throw;
}
}
void __fastcall TForm1::ResumeClick(TObject *Sender)
{
TFileStream* Fist = new TFileStream(MyFile, fmOpenReadWrite | fmShareDenyWrite);
try
{
Download->Enabled = false;
Resume->Enabled = false;
IdHTTP->Request->Clear();
//...
Fist->Seek(0, soEnd);
IdHTTP->Request->Ranges->Add()->StartPos = Fist->Position;
IdHTTP->Request->Referer = Edit1->Text;
IdHTTP->ConnectTimeout = 70000;
IdHTTP->ReadTimeout = 70000;
IdHTTP->Get(Urlz, Fist);
}
__finally
{
delete Fist;
Download->Enabled = true;
Updated();
}
}
void __fastcall TForm1::IdHTTPHeadersAvailable(TObject*Sender, TIdHeaderList *AHeaders, bool &VContinue)
{
Resume->Enabled = ( ((IdHTTP->Response->ResponseCode == 200) || (IdHTTP->Response->ResponseCode == 206)) && TextIsSame(AHeaders->Values["Accept-Ranges"], "bytes") );
if ((IdHTTP->Response->ContentStream) && (IdHTTP->Request->Ranges->Count > 0) && (IdHTTP->Response->ResponseCode == 200))
IdHTTP->Response->ContentStream->Size = 0;
}
#Romeo:
Also, you can try a following function to determine the real download filename.
I've translated this to C++ based on the RRUZ'function. So far so good, I'm using it on my simple IdHTTP download program, too.
But, this translation result is of course still need value improvement input from Remy Lebeau, RRUZ, or any other master here.
String __fastcall GetRemoteFileName(const String URI)
{
String result;
try
{
TIdHTTP* HTTP = new TIdHTTP(NULL);
try
{
HTTP->Head(URI);
result = HTTP->Response->RawHeaders->Params["Content-Disposition"]["filename"];
if (result.IsEmpty())
{
result = HTTP->Response->RawHeaders->Params["Content-Type"]["name"];
if (result.IsEmpty())
result = HTTP->URL->Document;
}
}
__finally
{
delete HTTP;
}
}
catch(const Exception &ex)
{
ShowMessage(const_cast<Exception&>(ex).ToString());
}
return result;
}
I want to use the DCMTK 3.6.1 library in an existing project that can create DICOM image. I want to use this library because I want to make the compression of the DICOM images. In a new solution (Visual Studio 2013/C++) Following the example in the DCMTK official documentation, I have this code, that works properly.
using namespace std;
int main()
{
DJEncoderRegistration::registerCodecs();
DcmFileFormat fileformat;
/**** MONO FILE ******/
if (fileformat.loadFile("Files/test.dcm").good())
{
DcmDataset *dataset = fileformat.getDataset();
DcmItem *metaInfo = fileformat.getMetaInfo();
DJ_RPLossless params; // codec parameters, we use the defaults
// this causes the lossless JPEG version of the dataset
//to be created EXS_JPEGProcess14SV1
dataset->chooseRepresentation(EXS_JPEGProcess14SV1, ¶ms);
// check if everything went well
if (dataset->canWriteXfer(EXS_JPEGProcess14SV1))
{
// force the meta-header UIDs to be re-generated when storing the file
// since the UIDs in the data set may have changed
delete metaInfo->remove(DCM_MediaStorageSOPClassUID);
delete metaInfo->remove(DCM_MediaStorageSOPInstanceUID);
metaInfo->putAndInsertString(DCM_ImplementationVersionName, "New Implementation Version Name");
//delete metaInfo->remove(DCM_ImplementationVersionName);
//dataset->remove(DCM_ImplementationVersionName);
// store in lossless JPEG format
fileformat.saveFile("Files/carrellata_esami_compresso.dcm", EXS_JPEGProcess14SV1);
}
}
DJEncoderRegistration::cleanup();
return 0;
}
Now I want to use the same code in an existing C++ application where
if (infoDicom.arrayImgDicom.GetSize() != 0) //Things of existing previous code
{
//I have added here the registration
DJEncoderRegistration::registerCodecs(); // register JPEG codecs
DcmFileFormat fileformat;
DcmDataset *dataset = fileformat.getDataset();
DJ_RPLossless params;
dataset->putAndInsertUint16(DCM_Rows, infoDicom.rows);
dataset->putAndInsertUint16(DCM_Columns, infoDicom.columns,);
dataset->putAndInsertUint16(DCM_BitsStored, infoDicom.m_bitstor);
dataset->putAndInsertUint16(DCM_HighBit, infoDicom.highbit);
dataset->putAndInsertUint16(DCM_PixelRepresentation, infoDicom.pixelrapresentation);
dataset->putAndInsertUint16(DCM_RescaleIntercept, infoDicom.rescaleintercept);
dataset->putAndInsertString(DCM_PhotometricInterpretation,"MONOCHROME2");
dataset->putAndInsertString(DCM_PixelSpacing, "0.086\\0.086");
dataset->putAndInsertString(DCM_ImagerPixelSpacing, "0.096\\0.096");
BYTE* pData = new BYTE[sizeBuffer];
LPBYTE pSorg;
for (int nf=0; nf<iNumberFrames; nf++)
{
//this contains all the PixelData and I put it into the dataset
pSorg = (BYTE*)infoDicom.arrayImgDicom.GetAt(nf);
dataset->putAndInsertUint8Array(DCM_PixelData, pSorg, sizeBuffer);
dataset->chooseRepresentation(EXS_JPEGProcess14SV1, ¶ms);
//and I put it in my data set
//but this IF return false so che canWriteXfer fails...
if (dataset->canWriteXfer(EXS_JPEGProcess14SV1))
{
dataset->remove(DCM_MediaStorageSOPClassUID);
dataset->remove(DCM_MediaStorageSOPInstanceUID);
}
//the saveFile fails too, and the error is "Pixel
//rappresentation non found" but I have set the Pixel rep with
//dataset->putAndInsertUint16(DCM_PixelRepresentation, infoDicom.pixelrapresentation);
OFCondition status = fileformat.saveFile("test1.dcm", EXS_JPEGProcess14SV1);
DJEncoderRegistration::cleanup();
if (status.bad())
{
int error = 0; //only for test
}
thefile.Write(pSorg, sizeBuffer); //previous code
}
Actually I made test with image that have on one frame, so the for cycle is done only one time. I don't understand why if I choose dataset->chooseRepresentation(EXS_LittleEndianImplicit, ¶ms); or dataset->chooseRepresentation(EXS_LittleEndianEXplicit, ¶ms); works perfectly but not when I choose dataset->chooseRepresentation(EXS_JPEGProcess14SV1, ¶ms);
If I use the same image in the first application, I can compress the image without problems...
EDIT: I think the main problem to solve is the status = dataset->chooseRepresentation(EXS_JPEGProcess14SV1, &rp_lossless) that return "Tag not found". How can I know wich tag is missed?
EDIT2: As suggest in the DCMTK forum I have added the tag about the Bits Allocated and now works for few images, but non for all. For some images I have again "Tag not found": how can I know wich one of tags is missing? As a rule it's better insert all the tags?
I solve the problem adding the tags DCM_BitsAllocated and DCM_PlanarConfiguration. This are the tags that are missed. I hope that is useful for someone.
At least you should call the function chooseRepresentation, after you have applied the data.
**dataset->putAndInsertUint8Array(DCM_PixelData, pSorg, sizeBuffer);**
dataset->chooseRepresentation(EXS_JPEGProcess14SV1, ¶ms);