Sam Sam - 3 months ago 14
C++ Question

using L_SaveFileOffset to save a Multi-page TIFF file

I need to save a Multi-page TIFF file using

L_SaveFileOffset
because I need to make sure no other process including Windows itself can access the file in the middle of saving pages and as far as I know
L_SaveFileOffset
is the only API for saving in LeadTools that allows saving the image using a file handle. The problem is, no matter what I do, only the last page gets saved. Please Help.

HANDLE hFile = ::CreateFile(L"ColorMaps.tif", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if(NULL != hFile)
{
const wchar_t pathTemplate[] = {L"ColorMap%d.bmp"};
wchar_t tPath[sizeof(pathTemplate) / sizeof(pathTemplate[0])];
FILEINFO PageInfo;
SAVEFILEOPTION so;
LOADFILEOPTION tlo;
int i;
HDC hDc;
BITMAPHANDLE tBmp;
__int64 tSize;

memset(&tlo, 0, sizeof(LOADFILEOPTION));
tlo.uStructSize = sizeof(LOADFILEOPTION);
L_GetDefaultLoadFileOption(&tlo, sizeof(LOADFILEOPTION));
tlo.Flags |= ELO_ROTATED;
hDc = ::GetDC(NULL);
tlo.XResolution = ::GetDeviceCaps(hDc, LOGPIXELSX);
tlo.YResolution = ::GetDeviceCaps(hDc, LOGPIXELSY);
::ReleaseDC(NULL, hDc);
memset(&so, 0, sizeof(SAVEFILEOPTION));
so.uStructSize = sizeof(SAVEFILEOPTION);
so.Flags = ESO_INSERTPAGE;

memset(&tBmp, 0, sizeof(BITMAPHANDLE));
tBmp.uStructSize = sizeof(BITMAPHANDLE);
for(i = 1; i < 7; i++)
{
::StringCbPrintf(tPath, sizeof(tPath), pathTemplate, i);
L_FileInfo(tPath, &PageInfo, sizeof(FILEINFO), 0, &tlo);
L_LoadBitmap(tPath, &tBmp, sizeof(BITMAPHANDLE), 0, ORDER_RGBORGRAY, &tlo, &PageInfo);
if (TOP_LEFT != tBmp.ViewPerspective)
L_ChangeBitmapViewPerspective(NULL, &tBmp, sizeof(BITMAPHANDLE), TOP_LEFT);
L_SaveFileOffset((L_HFILE)hFile, 0, &tSize, &tBmp, FILE_TIF_PACKBITS, PageInfo.BitsPerPixel, 0, SAVEFILE_MULTIPAGE, NULL, NULL, &so);
so.PageNumber = i + 1;
}
::CloseHandle(hFile);
}


The above is just an example and there might be 1000's of pages being saved into the TIFF file.

The problem shows itself if a user opens Windows Explorer and navigates to the directory were the file is being saved, you can see that windows is trying to repaint the icon for the file in between each page save and if I use
L_SaveBitmap
or
L_SaveFile
, sometimes they return -14 because windows is reading the file and LeadTools cannot get a lock on it.

P.S.
L_SaveFileOffset
returns 1 (SUCCESS) for all the pages and I am using LeadTools Documents imaging version 17.5.

Thank you

Answer

Sam,
One way to have total control over the file handle is to use redirected IO. The code needed to lock the TIFF file for the whole 6 pages will be something like this:

// 2 global variables
L_HFILE hFileMyTiff = NULL;
bool reallyClose = false;
L_HFILE EXT_CALLBACK MyOpen(L_TCHAR* pFile, L_INT nMode, L_INT nShare, L_VOID* pUserData)
{
   if(!hFileMyTiff) //only open the file if it's not already open
      hFileMyTiff = (L_HFILE)::CreateFile(pFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
   return hFileMyTiff;
}
L_UINT EXT_CALLBACK MyRead(L_HFILE FD, L_UCHAR * pBuf, L_UINT uCount, L_VOID *pUserData)
{
   DWORD dwRead = 0;
   ::ReadFile(FD, pBuf, uCount, &dwRead, NULL);
   return dwRead;
}
L_UINT EXT_CALLBACK MyWrite(L_HFILE FD, L_UCHAR * pBuf, L_UINT uCount, L_VOID* pUserData)
{
   DWORD dwWritten = 0;
   ::WriteFile(FD, pBuf, uCount, &dwWritten, NULL);
   return dwWritten;
}
L_SSIZE_T EXT_CALLBACK MySeek(L_HFILE FD, L_SSIZE_T nPos, L_INT nOrigin, L_VOID* pUserData)
{
   return ::SetFilePointer(FD, nPos, NULL, nOrigin);
}
L_INT EXT_CALLBACK MyClose (L_HFILE FD, L_VOID* pUserData)
{
   if(reallyClose)
   {
      ::CloseHandle(FD);
      hFileMyTiff = NULL;
   }
   else
      ::SetFilePointer(FD, 0, NULL, FILE_BEGIN);
   return TRUE;
}
void tst()
{
   const wchar_t pathTemplate[] = {L"ColorMap%d.bmp"};
   wchar_t tPath[sizeof(pathTemplate) / sizeof(pathTemplate[0])];
   FILEINFO PageInfo;
   LOADFILEOPTION tlo;
   int i;
   HDC hDc;
   BITMAPHANDLE tBmp;
   __int64 tSize;

   memset(&tlo, 0, sizeof(LOADFILEOPTION));
   tlo.uStructSize = sizeof(LOADFILEOPTION);
   L_GetDefaultLoadFileOption(&tlo, sizeof(LOADFILEOPTION));
   tlo.Flags |= ELO_ROTATED;
   hDc = ::GetDC(NULL);
   tlo.XResolution = ::GetDeviceCaps(hDc, LOGPIXELSX);
   tlo.YResolution = ::GetDeviceCaps(hDc, LOGPIXELSY);
   ::ReleaseDC(NULL, hDc);

   memset(&tBmp, 0, sizeof(BITMAPHANDLE));
   tBmp.uStructSize = sizeof(BITMAPHANDLE);
   for(i = 1; i < 7; i++)
   {
      ::StringCbPrintf(tPath, sizeof(tPath), pathTemplate, i);
      L_FileInfo(tPath, &PageInfo, sizeof(FILEINFO), 0, &tlo);
      L_LoadBitmap(tPath, &tBmp, sizeof(BITMAPHANDLE), 0, ORDER_RGBORGRAY, &tlo, &PageInfo);
      if (TOP_LEFT != tBmp.ViewPerspective)
         L_ChangeBitmapViewPerspective(NULL, &tBmp, sizeof(BITMAPHANDLE), TOP_LEFT);

      if(i==6) //only allow closing after the last page is saved
         reallyClose = true;
      else
         reallyClose = false;
      L_RedirectIO(MyOpen, MyRead, MyWrite, MySeek, MyClose, NULL); //use our own file I/O functions
      L_SaveFile(L"ColorMaps.tif", &tBmp, FILE_TIF_PACKBITS, PageInfo.BitsPerPixel, 0, SAVEFILE_MULTIPAGE, NULL, NULL, NULL);
      L_RedirectIO(NULL, NULL, NULL, NULL, NULL, NULL); //reset to default I/O so as not affect loading
   }
}

Note the use of 2 global variables for simplicity. A cleaner approach might be to define your own data structure and pass its address in the pUserData parameter of L_RedirectIO.
We've tested this solution with v17.5 and verified it's working properly. If it is not suitable for your particular case, please contact LEADTOOLS support service and we'll discuss other alternatives.

Update: The latest build of LEADTOOLS v19 now supports appending pages to TIFF files using the L_SaveFileOffset() function.

Comments