This little utility program is very useful for producing proofsheets from a list of images generated by a digital camera.
Download the zipped executable program and source file: prfsheet.zip (20 kb). The program prfsheet.exe requires the presence of the Victor Library vic32.dll in the same directory. If you don't already have vic32.dll download a zipped eval copy of the Victor Library dll (119 kb). (Programmers: for all the evaluation modules necessary to create a Victor Library application visit http://www.catenary.com/download/vic5e.html)
In this example we create a Win32 console application that allows a 2 x 2-inch area for each picture and reads a list of JPEG image file names from an ASCII text file. The proofsheet is created by running the console application with the name of the text file as the command line argument, for example:
prfsheet myimages.txt
The image file names are specified in an ASCII text file, such as "myimages.txt" shown here:
| myimages.txt |
|---|
c:\pics\p0004351.jpg c:\pics\p0004352.jpg c:\pics\p0004353.jpg c:\pics\p0004354.jpg |
The steps to print a proofsheet are
This example allows up to 100 pages to be printed. Each image is printed in a 2 x 2-inch area with 1/2-inch spacing between horizontal and vertical rows of images.
// Prfsheet.c -- console app to print a photo proofsheet // // Usage: prfsheet myimages.txt // // Reads the text file containing the file names of the image files // Prints 12 images per page, each image is allowed 2 x 2 inch area #include <windows.h> #include <stdio.h> #include <vicdefs.h> #include <tchar.h> typedef TCHAR TSTR256[258]; // Holds one image filename // Struct to hold data about printing typedef struct { HDC hPrn; // Handle to the printer DC int mode; // Print mode RECT prtrc; // Area to print in mils int frame; // Border width in pixels int devCaps; // Device capabilities (status word) int ready; // Printer driver has sufficient capability int xRes, yRes; // Printer resolution in dots per inch int xSize, ySize; // Printable width, height in mils } PrtInfo; static HDC GetPrinterDC(void); static void GetPrinterInfo(PrtInfo *prtvals); static int loadTheImageFile(LPTSTR fname, imgdes *image, int *iwidth, int *ilength); static int readInImgFilenames(LPTSTR imgFilename, TSTR256 **filelist); #define NOMOFILES -112 // No more image filenames in filename array int _cdecl _tmain(int argc, TCHAR *argv[]) { int fileNameMax, fileCtr, rcode=NO_ERROR; TSTR256 *filelist = NULL; PrtInfo prtvals; HWND hWnd = GetActiveWindow(); TCHAR docName[256]; int xx, yy, page, picsOnPage=0; LPTSTR listFilename; // Filename of image filename list // Make sure a filename list is present if(argc < 2) { printf(_T("Usage: prfsheet <filename list>\n")); printf(_T("Press ENTER to continue\n")); getchar(); return(1); } // Second arg is the name of the image filename list listFilename = argv[1]; // Read in data file fileNameMax = readInImgFilenames(listFilename, &filelist); if(fileNameMax == 0) // Exit if there are no image filenames in the list return(1); // Set up printer: Get hPrn and printer characteristics GetPrinterInfo(&prtvals); if(prtvals.hPrn == NULL) return(1); // Since pics will be printed with their longest dimension equal to two inches, // make sure printable width is sufficient to print three per row. if((2000 * 3 + 500 * 3) > prtvals.xSize) { printf(_T("Printable width insufficient for three pics per row: %d\n"), prtvals.xSize); printf(_T("Press ENTER to continue\n")); getchar(); return(1); } // Initialize for multi-image printing _tcscpy(docName, _T("Victor Proof Sheets")); fileCtr = 0; // Count filenames in array that we process for(page=0,picsOnPage=0; page<100 && rcode!=NOMOFILES; page++) { rcode = printimagestartdoc(prtvals.hPrn, docName); if(rcode != NO_ERROR) { printf(_T("printimagestartdoc error\n")); printf(_T("Press ENTER to exit\n")); getchar(); break; } printf(_T("Printing page %d\n"), page+1); for(yy=500; yy<prtvals.ySize-2000 && rcode!=NOMOFILES; yy+=2500) { for(xx=500; xx<prtvals.xSize-2000 && rcode!=NOMOFILES; xx+=2500) { if(fileCtr >= fileNameMax) rcode = NOMOFILES; // No more filenames in image filename list else { // Process next image filename LPTSTR fname = (LPTSTR)filelist[fileCtr]; imgdes image; int iwidth, ilength, prtWide, prtLong; // Load the image file to print rcode = loadTheImageFile(fname, &image, &iwidth, &ilength); if(rcode == NO_ERROR) { // Calc printing dimensions: Make longest dimension = 2 inches and keep aspect ratio the same as in image prtWide = (iwidth > ilength) ? 2000 : (iwidth * 2000 / ilength); prtLong = (iwidth <= ilength) ? 2000 : (ilength * 2000 / iwidth); // Set area to print on page SetRect(&prtvals.prtrc, xx, yy, xx + prtWide, yy + prtLong); printf(_T("Printing file %d: %s...\n"), fileCtr+1, fname); // Print the pic on the page rcode = printimagenoeject(hWnd, prtvals.hPrn, prtvals.mode, &image, &prtvals.prtrc, prtvals.frame, NULL); if(rcode != NO_ERROR) { printf(_T("printimagenoeject() error: %d for %s\n"), rcode, fname); printf(_T("Press ENTER to continue\n")); getchar(); } else picsOnPage++; // Keep track of number of pics on the page } freeimage(&image); fileCtr++; } } // xx } // yy // Done printing current page, eject it printimageenddoc(prtvals.hPrn, TRUE); picsOnPage = 0; // Reset pics on the page we've printed } // page if(picsOnPage > 0) // If there are pics on the page, we need to call prtimgendoc() to eject it printimageenddoc(prtvals.hPrn, TRUE); if(prtvals.hPrn) DeleteDC(prtvals.hPrn); // Delete printer DC if(filelist != NULL) free(filelist); printf(_T("Done!\n")); return(0); } // Load the image. Prints error message if there is a problem. // Returns NO_ERROR so we just continue if we can't find an image. static int loadTheImageFile(LPTSTR fname, imgdes *image, int *iwidth, int *ilength) { JpegData jdat; // Reserve space for struct int rc, rcode = NO_ERROR; // Make sure file exists and get its dimensions *iwidth = 0; *ilength = 0; rc = jpeginfo(fname, &jdat); if(rc != NO_ERROR) { printf(_T("jpeginfo() error: %d for %s\n"), rcode, fname); printf(_T("Press ENTER to continue\n")); getchar(); } else { // Allocate image buffer to the file dimensions *iwidth = jdat.width; *ilength = jdat.length; rc = allocimage(image, jdat.width, jdat.length, jdat.vbitcount); if(rc != NO_ERROR) { printf(_T("allocimage() error: %d for %s\n"), rcode, fname); printf(_T("Press ENTER to continue\n")); getchar(); } else { // Load the image rc = loadjpg(fname, image); if(rc != NO_ERROR) { printf(_T("loadjpg() error: %d for %s\n"), rcode, fname); printf(_T("Press ENTER to continue\n")); getchar(); } } } return(rcode); } // Get default printer and create a DC. Returns a handle to the DC or 0 if unsuccessful. static HDC GetPrinterDC(void) { TCHAR szPrinter[80]; LPTSTR szDevice, szDriver, szOutput; GetProfileString(_T("windows"), _T("device"), _T(""), szPrinter, sizeof(szPrinter)); if((szDevice = _tcstok(szPrinter, _T(","))) != 0 && (szDriver = _tcstok(0, _T(","))) != 0 && (szOutput = _tcstok(0, _T(","))) != 0) return(CreateDC(szDriver, szDevice, szOutput, 0)); return(0); } // Get info we need from printer static void GetPrinterInfo(PrtInfo *prtvals) { memset(prtvals, 0, sizeof(PrtInfo)); // Zero all members of struct prtvals->devCaps = 0; // Assume no raster capabilities prtvals->hPrn = GetPrinterDC(); // Get printer DC if(prtvals->hPrn) { prtvals->devCaps = GetDeviceCaps(prtvals->hPrn, RASTERCAPS); // Set flag to enable/disable print menu item prtvals->ready = (prtvals->devCaps & RC_BITBLT); // Get info we need prtvals->xRes = GetDeviceCaps(prtvals->hPrn, LOGPIXELSX); prtvals->yRes = GetDeviceCaps(prtvals->hPrn, LOGPIXELSY); prtvals->xSize = (int)(GetDeviceCaps(prtvals->hPrn, HORZRES) * 1000L / prtvals->xRes); prtvals->ySize = (int)(GetDeviceCaps(prtvals->hPrn, VERTRES) * 1000L / prtvals->yRes); prtvals->mode = 0; // Print mode: Default // Area to print (prtvals->prtrc) will be filled in later prtvals->frame = 0; // Border width in pixels } else { printf(_T("Could not get printer DC\n")); printf(_T("Press ENTER to continue\n")); getchar(); } } // Read in image filenames and store them in a buffer 258 TCHARs wide. // Returns number of image files in name array. static int readInImgFilenames( LPTSTR imgFilename, TSTR256 **filelist) { HANDLE shandle = INVALID_HANDLE_VALUE; DWORD fileSizeLow, fileSizeHigh; int rcode = NO_ERROR; int nelem = 0; BYTE *databuff = NULL; // Open existing file for reading that contains image filenames shandle = CreateFile(imgFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(shandle == INVALID_HANDLE_VALUE) rcode = BAD_OPN; else { // Get size of file fileSizeLow = GetFileSize(shandle, &fileSizeHigh); // Allocate space for file databuff = (BYTE *)calloc(fileSizeLow, sizeof(BYTE)); if(databuff == NULL) rcode = BAD_MEM; else { DWORD j, dwBytsRd; BYTE *pp; unsigned ch; // Read in the file ReadFile(shandle, (LPSTR)databuff, (unsigned)fileSizeLow, &dwBytsRd, NULL); // First time thru just count filename entries (CR marks the end of a line, ';' is comment) pp = databuff; nelem = 0; // Image filename counter j = 0; while(j < fileSizeLow) { if( __iscsym(*pp) ) // Check first char in filename is letter, digit, or underscore nelem++; // Not in a comment string, count entry // Find LF do { ch = *pp++; j++; } while(ch != '\n'); } // while // Allocate space for image filename array *filelist = (TSTR256 *)calloc(nelem, sizeof(TSTR256)); if(*filelist == NULL) rcode = BAD_MEM; // Second time thru copy filenames into filelist[] else { BYTE *dp; TSTR256 *fn; pp = databuff; fn = *filelist; // Point fn and dp at first filename buffer dp = (TCHAR *)fn; j = 0; while(j < fileSizeLow) { BOOL isFilename = (__iscsym(*pp) == TRUE); // Check that first char in string is char, digit or underscore // Find LF do { ch = *pp++; if(isFilename == TRUE && isprint(ch)) *dp++ = ch; j++; } while(ch != '\n'); if(isFilename == TRUE) { fn++; // Point fn and dp at next filename buffer dp = (TCHAR *)fn; } } // while } // else } } if(databuff) free(databuff); if(shandle != INVALID_HANDLE_VALUE) CloseHandle(shandle); // Handle any errors if(rcode != NO_ERROR) { if(rcode == BAD_OPN) printf(_T("Could not open %s\n"), imgFilename); else if(rcode == BAD_MEM) printf(_T("Insufficient memory\n")); printf(_T("Press ENTER to continue\n")); getchar(); } return(nelem); }
Victor Image Processing Library homepage | Victor Product Summary | more source code