/// <summary> /// Our scanning callback function. We appeal directly to the supporting /// TWAIN object. This way we don't have to maintain some kind of a loop /// inside of the application, which is the source of most problems that /// developers run into. /// /// While it looks scary at first, there's really not a lot going on in /// here. We do some sanity checks, we watch for certain kinds of events, /// we support the four methods of transferring images, and we dump out /// some meta-data about the transferred image. However, because it does /// look scary I dropped in some region pragmas to break things up... /// </summary> /// <param name="a_blClosing">We're shutting down</param> /// <returns>TWAIN status</returns> private TWAIN.STS ScanCallback(bool a_blClosing) { bool blXferDone; TWAINCSToolkit.MSG msgPendingxfers = TWAINCSToolkit.MSG.ENDXFER; TWAIN.STS sts; string szFilename = ""; MemoryStream memorystream; TWAIN.TW_IMAGEINFO twimageinfo = default(TWAIN.TW_IMAGEINFO); // Validate... if (m_twain == null) { Log.Error("m_twain is null..."); if (ReportImage != null) ReportImage("ScanCallback: 001", "", "", "", CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.FAILURE); } // We're leaving... if (a_blClosing) { if (ReportImage != null) ReportImage("ScanCallback: 002", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.IDENTITY.ToString(), TWAIN.MSG.CLOSEDS.ToString(), CvtSts(TWAIN.STS.SUCCESS), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Somebody pushed the Cancel or the OK button... if (m_twain.IsMsgCloseDsReq()) { m_twain.Rollback(TWAIN.STATE.S4); ReportImage("ScanCallback: 003", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.NULL.ToString(), TWAIN.MSG.CLOSEDSREQ.ToString(), CvtSts(TWAIN.STS.SUCCESS), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } else if (m_twain.IsMsgCloseDsOk()) { m_twain.Rollback(TWAIN.STATE.S4); ReportImage("ScanCallback: 004", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.NULL.ToString(), TWAIN.MSG.CLOSEDSOK.ToString(), CvtSts(TWAIN.STS.SUCCESS), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Init ourselves... if (m_blScanStart) { TWAIN.TW_CAPABILITY twcapability; // Don't come in here again until the start of the next scan batch... m_blScanStart = false; // Make a note of it... WriteOutput(Environment.NewLine + "Entered state 5..." + Environment.NewLine); // Get the current setting for the image transfer... twcapability = default(TWAIN.TW_CAPABILITY); twcapability.Cap = TWAIN.CAP.ICAP_XFERMECH; sts = m_twain.DatCapability(TWAIN.DG.CONTROL, TWAIN.MSG.GETCURRENT, ref twcapability); if (sts == TWAIN.STS.SUCCESS) { try { string[] asz = m_twain.CapabilityToCsv(twcapability).Split(new char[] { ',' }); m_twain.DsmMemFree(ref twcapability.hContainer); m_twsxXferMech = (TWAIN.TWSX)ushort.Parse(asz[3]); } catch { m_twsxXferMech = TWAIN.TWSX.NATIVE; } } else { m_twsxXferMech = TWAIN.TWSX.NATIVE; } } // We're waiting for that first image to show up, if we don't // see it, then return... if (!m_twain.IsMsgXferReady()) { // If we're on Windows we need to send event requests to the driver... if (TWAIN.GetPlatform() == TWAIN.Platform.WINDOWS) { TWAIN.TW_EVENT twevent = default(TWAIN.TW_EVENT); twevent.pEvent = Marshal.AllocHGlobal(256); // over allocate for MSG structure if (twevent.pEvent != IntPtr.Zero) { m_twain.DatEvent(TWAIN.DG.CONTROL, TWAIN.MSG.PROCESSEVENT, ref twevent); Marshal.FreeHGlobal(twevent.pEvent); } } // Scoot... return (TWAIN.STS.SUCCESS); } // Transfer the image, at this point we're showing it on the form. The // application should queue this to some other thread or process as fast // as possible, so that we can get to the next image. // // This is the point where TWAIN tells us about things like jams and // multifeeds. // Init some more stuff... blXferDone = false; /////////////////////////////////////////////////////////////////////////////// // // Beginning of the image transfer section... // // A native transfer gives us a handle to a DIB, which is not // quite what we need to fill in a Bitmap in C#, so there's // some work going on under the hood that requires both processing // power and memory that may not make this the best choice. // However, all drivers support native, and the format is reasonably // easy to process... #region TWAIN.TWSX.NATIVE if (m_twsxXferMech == TWAIN.TWSX.NATIVE) { Bitmap bitmap = null; sts = m_twain.DatImagenativexfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref bitmap); if (sts != TWAIN.STS.XFERDONE) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 005", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGENATIVEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } else { msgPendingxfers = ReportImage("ScanCallback: 006", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGENATIVEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, null, null, null, 0); bitmap = null; blXferDone = true; } } #endregion // File transfers are not supported by all TWAIN drivers (it's an // optional transfer format in the TWAIN Specification)... #region TWAIN.TWSX.FILE else if (m_twsxXferMech == TWAIN.TWSX.FILE) { Bitmap bitmap; TWAIN.TW_SETUPFILEXFER twsetupfilexfer = default(TWAIN.TW_SETUPFILEXFER); // Get a copy of the current setup file transfer info... twsetupfilexfer = m_twsetupfilexfer; // ***WARNING*** // Override the current setting with one that supports the pixeltype and // compression of the current image. The choices are JPEG or TIFF. Note // that this only works for drivers that report final value in state 6... if (m_blAutomaticJpegOrTiff) { // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 007", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Assume TIFF, unless we detect JPEG (JFIF)... twsetupfilexfer.Format = TWAIN.TWFF.TIFF; if (twimageinfo.Compression == (ushort)TWAIN.TWCP.JPEG) { twsetupfilexfer.Format = TWAIN.TWFF.JFIF; } } // If specified, m_szImagePath wins over DAT_SETUPFILEXFER... string szFile = m_twsetupfilexfer.FileName.Get(); if ((m_szImagePath != null) && (m_szImagePath != "")) { szFile = m_szImagePath; } // Build the base... szFile = System.IO.Path.Combine(szFile, m_iImageXferCount.ToString("D6")); // Add the image transfer count and the extension... switch (twsetupfilexfer.Format) { default: twsetupfilexfer.FileName.Set(szFile + ".xxx"); break; case TWAIN.TWFF.BMP: twsetupfilexfer.FileName.Set(szFile + ".bmp"); break; case TWAIN.TWFF.DEJAVU: twsetupfilexfer.FileName.Set(szFile + ".dejavu"); break; case TWAIN.TWFF.EXIF: twsetupfilexfer.FileName.Set(szFile + ".exif"); break; case TWAIN.TWFF.FPX: twsetupfilexfer.FileName.Set(szFile + ".fpx"); break; case TWAIN.TWFF.JFIF: twsetupfilexfer.FileName.Set(szFile + ".jpg"); break; case TWAIN.TWFF.JP2: twsetupfilexfer.FileName.Set(szFile + ".jp2"); break; case TWAIN.TWFF.JPX: twsetupfilexfer.FileName.Set(szFile + ".jpx"); break; case TWAIN.TWFF.PDF: twsetupfilexfer.FileName.Set(szFile + ".pdf"); break; case TWAIN.TWFF.PDFA: twsetupfilexfer.FileName.Set(szFile + ".pdf"); break; case TWAIN.TWFF.PICT: twsetupfilexfer.FileName.Set(szFile + ".pict"); break; case TWAIN.TWFF.PNG: twsetupfilexfer.FileName.Set(szFile + ".png"); break; case TWAIN.TWFF.SPIFF: twsetupfilexfer.FileName.Set(szFile + ".spiff"); break; case TWAIN.TWFF.TIFF: twsetupfilexfer.FileName.Set(szFile + ".tif"); break; case TWAIN.TWFF.TIFFMULTI: twsetupfilexfer.FileName.Set(szFile + ".tif"); break; case TWAIN.TWFF.XBM: twsetupfilexfer.FileName.Set(szFile + ".xbm"); break; } // Setup the file transfer... sts = m_twain.DatSetupfilexfer(TWAIN.DG.CONTROL, TWAIN.MSG.SET, ref twsetupfilexfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 008", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.SETUPFILEXFER.ToString(), TWAIN.MSG.SET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Transfer the image... sts = m_twain.DatImagefilexfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET); if (sts != TWAIN.STS.XFERDONE) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 009", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } else { try { szFilename = twsetupfilexfer.FileName.Get(); Image image = Image.FromFile(szFilename); bitmap = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 010", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFilename, m_twain.ImageinfoToCsv(twimageinfo), null, 0); bitmap = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 011", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, szFilename, m_twain.ImageinfoToCsv(twimageinfo), null, 0); return (TWAIN.STS.SUCCESS); } } } #endregion // Memory transfers are supported by all TWAIN drivers, and offer // the fastest and most efficient way of moving image data from // the scanner into the application. However, the data format may // be nothing more than a stream of compressed raster data, which // is impossible to interpret until the meta-data is collected // using DAT_IMAGEINFO or DAT_EXTIMAGEINFO. And under TWAIN the // meta-data cannot be collected until after the image data is // fully transferred, so under C# we have some challenges... // // And because there's a lot going on in here I've tossed in some // more region pragmas to break it up... #region TWAIN.TWSX.MEMORY else if (m_twsxXferMech == TWAIN.TWSX.MEMORY) { Bitmap bitmap; byte[] abImage = null; IntPtr intptrTotalAllocated; IntPtr intptrTotalXfer; IntPtr intptrOffset; TWAIN.TW_SETUPMEMXFER twsetupmemxfer = default(TWAIN.TW_SETUPMEMXFER); TWAIN.TW_IMAGEMEMXFER twimagememxfer = default(TWAIN.TW_IMAGEMEMXFER); TWAIN.TW_MEMORY twmemory = default(TWAIN.TW_MEMORY); const int iSpaceForHeader = 512; // Get the preferred transfer size from the driver... sts = m_twain.DatSetupmemxfer(TWAIN.DG.CONTROL, TWAIN.MSG.GET, ref twsetupmemxfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 012", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.SETUPMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate our unmanaged memory... twmemory.Flags = (uint)TWAIN.TWMF.APPOWNS | (uint)TWAIN.TWMF.POINTER; twmemory.Length = twsetupmemxfer.Preferred; twmemory.TheMem = Marshal.AllocHGlobal((int)twsetupmemxfer.Preferred); if (twmemory.TheMem == IntPtr.Zero) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 013", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Loop through all the strips of data, at the end of this the byte // array abImage has all of the data that we got from the scanner... #region Tranfer the image from the driver... intptrTotalAllocated = (IntPtr)iSpaceForHeader; intptrOffset = (IntPtr)iSpaceForHeader; intptrTotalXfer = IntPtr.Zero; sts = TWAIN.STS.SUCCESS; while (sts == TWAIN.STS.SUCCESS) { byte[] abTmp; // Append the new data to the end of the data we've transferred so far... intptrOffset = (IntPtr)((int)iSpaceForHeader + (int)intptrTotalXfer); // Get a strip of image data... twimagememxfer.Memory.Flags = twmemory.Flags; twimagememxfer.Memory.Length = twmemory.Length; twimagememxfer.Memory.TheMem = twmemory.TheMem; sts = m_twain.DatImagememxfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimagememxfer); if (sts == TWAIN.STS.XFERDONE) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else if (sts == TWAIN.STS.SUCCESS) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 014", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate memory for our new strip... intptrTotalAllocated = (IntPtr)((UInt64)intptrTotalAllocated + (UInt64)twimagememxfer.BytesWritten); abTmp = new byte[(int)intptrTotalAllocated]; if (abTmp == null) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 015", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Copy the existing data, if we have it... if (abImage != null) { Buffer.BlockCopy(abImage, 0, abTmp, 0, (int)intptrTotalAllocated - (int)twimagememxfer.BytesWritten); } // Switch pointers... abImage = abTmp; abTmp = null; // Copy the new strip into place... Marshal.Copy(twimagememxfer.Memory.TheMem, abImage, (int)intptrOffset, (int)twimagememxfer.BytesWritten); } #endregion // Increment our counter... m_iImageCount += 1; // Do this if the data is JPEG compressed... #region JPEG Images (grayscale and color)... if ((TWAIN.TWCP)twimagememxfer.Compression == TWAIN.TWCP.JPEG) { string szFile = ""; try { // Write the data to disk... if ((m_szImagePath != null) && Directory.Exists(m_szImagePath)) { // Save the image... szFile = Path.Combine(m_szImagePath, "img" + m_iImageCount.ToString("D6") + ".jpg"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write(abImage, iSpaceForHeader, abImage.Length - iSpaceForHeader); } // Show the image from disk... Image image = Image.FromFile(szFile); Bitmap bitmapFile = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 016", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.XFERDONE), bitmapFile, szFile, null, abImage, iSpaceForHeader); blXferDone = true; } // Display the image from memory... else { // Turn the data into a bitmap... memorystream = new MemoryStream(abImage, iSpaceForHeader, abImage.Length - iSpaceForHeader); Image image = Image.FromStream(memorystream); bitmap = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 017", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, null, null, abImage, iSpaceForHeader); bitmap = null; memorystream = null; blXferDone = true; } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 018", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Do this if the data is GROUP4 compressed, the way to add support for // this is to create a bitonal TIFF header, place it in a memory stream // along with the data, and then let .NET decode it... #region Group4 Images (black and white images)... else if ((TWAIN.TWCP)twimagememxfer.Compression == TWAIN.TWCP.GROUP4) { IntPtr intptrTiff; TiffBitonalG4 tiffbitonalg4; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 019", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffbitonalg4 = new TiffBitonalG4((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffbitonalg4)); // Copy the header into the memory... Marshal.StructureToPtr(tiffbitonalg4, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonalg4), Marshal.SizeOf(tiffbitonalg4)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonalg4), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffbitonalg4)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 020", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from disk (to reduce our memory footprint)... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send it off to the application... msgPendingxfers = ReportImage("ScanCallback: 021", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 022", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 023", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // We have no compression, we need to handle the data one raster or // one pixel at a time to make sure we get the alignment right, and that // means doing different stuff for black and white vs gray vs color... #region Uncompressed images (all pixel types)... else if ((TWAIN.TWCP)twimagememxfer.Compression == TWAIN.TWCP.NONE) { // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 024", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Handle uncompressed bitonal images... #region Handle uncompressed bitonal images... if (twimageinfo.BitsPerPixel == 1) { try { IntPtr intptrTiff; TiffBitonalUncompressed tiffbitonaluncompressed; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 025", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffbitonaluncompressed = new TiffBitonalUncompressed((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffbitonaluncompressed)); // Copy the header into the memory... Marshal.StructureToPtr(tiffbitonaluncompressed, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonaluncompressed), Marshal.SizeOf(tiffbitonaluncompressed)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffbitonaluncompressed), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffbitonaluncompressed)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 026", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from the file, make sure we discard the image so the file isn't locked... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send the stuff to the application... ReportImage("ScanCallback: 027", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 028", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Cleanup... bitmap = null; memorystream = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 029", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 030", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Handle uncompressed color images... #region Handle uncompressed color images... else if (twimageinfo.BitsPerPixel == 24) { try { IntPtr intptrTiff; TiffColorUncompressed tiffcoloruncompressed; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 031", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffcoloruncompressed = new TiffColorUncompressed((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffcoloruncompressed)); // Copy the header into the memory... Marshal.StructureToPtr(tiffcoloruncompressed, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffcoloruncompressed), Marshal.SizeOf(tiffcoloruncompressed)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffcoloruncompressed), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffcoloruncompressed)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 032", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from the file, make sure we discard the image so the file isn't locked... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send the stuff to the application... ReportImage("ScanCallback: 033", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 034", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Cleanup... bitmap = null; memorystream = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 035", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 036", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Handle uncompressed grayscale images... #region Handle uncompressed grayscale images... else if (twimageinfo.BitsPerPixel == 8) { try { IntPtr intptrTiff; TiffGrayscaleUncompressed tiffgrayscaleuncompressed; string szFile = ""; // We need the image info for this one... if (twimageinfo.BitsPerPixel == 0) { sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 037", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Create a TIFF header... tiffgrayscaleuncompressed = new TiffGrayscaleUncompressed((uint)twimageinfo.ImageWidth, (uint)twimageinfo.ImageLength, (uint)twimageinfo.XResolution.Whole, (uint)intptrTotalXfer); // Create memory for the TIFF header... intptrTiff = Marshal.AllocHGlobal(Marshal.SizeOf(tiffgrayscaleuncompressed)); // Copy the header into the memory... Marshal.StructureToPtr(tiffgrayscaleuncompressed, intptrTiff, true); // Copy the memory into the byte array (we left room for it), giving us a // TIFF image starting at (iSpaceForHeader - Marshal.SizeOf(tiffbitonal)) // in the byte array... Marshal.Copy(intptrTiff, abImage, iSpaceForHeader - Marshal.SizeOf(tiffgrayscaleuncompressed), Marshal.SizeOf(tiffgrayscaleuncompressed)); // Create a TIFF image and load it... try { bool blDeleteWhenDone = false; string szImagePath = m_szImagePath; // We don't have a valid user supplied folder, so use the temp // directory... if ((m_szImagePath == null) || !Directory.Exists(m_szImagePath)) { blDeleteWhenDone = true; szImagePath = Path.GetTempPath(); } // Write the image to disk... try { szFile = Path.Combine(szImagePath, "img" + m_iImageCount.ToString("D6") + ".tif"); using (FileStream filestream = new FileStream(szFile, FileMode.Create, FileAccess.Write)) { filestream.Write ( abImage, iSpaceForHeader - Marshal.SizeOf(tiffgrayscaleuncompressed), abImage.Length - (iSpaceForHeader - Marshal.SizeOf(tiffgrayscaleuncompressed)) ); } } catch { WriteOutput("Unable to save image to disk <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 038", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } // Free the memory... Marshal.FreeHGlobal(intptrTiff); intptrTiff = IntPtr.Zero; // Build the bitmap from the file, make sure we discard the image so the file isn't locked... Image image = Image.FromFile(szFile); bitmap = new Bitmap(image); image.Dispose(); image = null; // Send the stuff to the application... ReportImage("ScanCallback: 039", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, szFile, m_twain.ImageinfoToCsv(twimageinfo), abImage, iSpaceForHeader); // Delete it if it's temp... if (blDeleteWhenDone) { try { File.Delete(szFile); } catch { WriteOutput("Failed to delete temporary image file <" + szFile + ">" + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 040", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FILEWRITEERROR), null, szFile, null, null, 0); return (TWAIN.STS.SUCCESS); } } // Cleanup... bitmap = null; memorystream = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 041", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 042", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } #endregion // Uh-oh... #region Uh-oh... else { WriteOutput("Scanning error: unsupported pixeltype..." + Environment.NewLine); m_iImageCount -= 1; m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 043", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } #endregion } #endregion // Uh-oh... #region Uh-oh else { WriteOutput("Scanning error: unsupported compression..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 044", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } #endregion } #endregion // Memory file transfers combine the best of file transfers and // memory transgers, however at this time it's not clear that any // TWAIN drivers have ever supported it... #region TWAIN.TWSX.MEMFILE else if (m_twsxXferMech == TWAIN.TWSX.MEMFILE) { Bitmap bitmap; byte[] abImage = null; IntPtr intptrTotalAllocated; IntPtr intptrTotalXfer; IntPtr intptrOffset; TWAIN.TW_SETUPMEMXFER twsetupmemxfer = default(TWAIN.TW_SETUPMEMXFER); TWAIN.TW_IMAGEMEMXFER twimagememxfer = default(TWAIN.TW_IMAGEMEMXFER); // Get the preferred transfer size from the driver... sts = m_twain.DatSetupmemxfer(TWAIN.DG.CONTROL, TWAIN.MSG.GET, ref twsetupmemxfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 045", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.SETUPMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate our unmanaged memory... twimagememxfer.Memory.Flags = (uint)TWAIN.TWMF.APPOWNS | (uint)TWAIN.TWMF.POINTER; twimagememxfer.Memory.Length = twsetupmemxfer.Preferred; twimagememxfer.Memory.TheMem = Marshal.AllocHGlobal((int)twsetupmemxfer.Preferred); if (twimagememxfer.Memory.TheMem == IntPtr.Zero) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 046", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Loop through all the strips of data... intptrTotalAllocated = IntPtr.Zero; intptrTotalXfer = IntPtr.Zero; sts = TWAIN.STS.SUCCESS; while (sts == TWAIN.STS.SUCCESS) { byte[] abTmp; // Append the new data to the end of the data we've transferred so far... intptrOffset = (IntPtr)intptrTotalXfer; // Get a strip of image data... sts = m_twain.DatImagememfilexfer(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimagememxfer); if (sts == TWAIN.STS.XFERDONE) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else if (sts == TWAIN.STS.SUCCESS) { intptrTotalXfer = (IntPtr)((UInt64)intptrTotalXfer + (UInt64)twimagememxfer.BytesWritten); } else { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 047", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Allocate memory for our new strip... intptrTotalAllocated = (IntPtr)((UInt64)intptrTotalAllocated + (UInt64)twimagememxfer.BytesWritten); abTmp = new byte[(int)intptrTotalAllocated]; if (abTmp == null) { sts = TWAIN.STS.LOWMEMORY; WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 048", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // Copy the existing data, if we have it... if (abImage != null) { Buffer.BlockCopy(abImage, 0, abTmp, 0, (int)intptrTotalAllocated - (int)twimagememxfer.BytesWritten); } // Switch pointers... abImage = abTmp; abTmp = null; // Copy the new strip into place... Marshal.Copy(twimagememxfer.Memory.TheMem, abImage, (int)intptrOffset, (int)twimagememxfer.BytesWritten); } // Okay, turn the data into a bitmap... memorystream = new MemoryStream(abImage); try { Image image = Image.FromStream(memorystream); bitmap = new Bitmap(image); image.Dispose(); image = null; msgPendingxfers = ReportImage("ScanCallback: 049", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), bitmap, null, null, null, 0); bitmap = null; blXferDone = true; } catch { WriteOutput("Scanning error: unable to load image..." + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 050", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } memorystream = null; } #endregion // Uh-oh... #region Uh-oh... else { WriteOutput("Scan: unrecognized ICAP_XFERMECH value..." + m_twsxXferMech + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 051", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEMEMFILEXFER.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(TWAIN.STS.FAILURE), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } #endregion // // End of the image transfer section... // /////////////////////////////////////////////////////////////////////////////// // Give us a blank line... WriteOutput(Environment.NewLine); // If we're doing a file tranfer, then output the file we just did... if (szFilename != "") { WriteOutput("File: " + szFilename + Environment.NewLine); } // Let's get some meta data. TWAIN only guarantees that this data // is accurate in state 7 after TWRC_XFERDONE has been received... if (blXferDone) { if (twimageinfo.BitsPerPixel == 0) { twimageinfo = default(TWAIN.TW_IMAGEINFO); sts = m_twain.DatImageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twimageinfo); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("ImageInfo failed: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 052", TWAIN.DG.IMAGE.ToString(), TWAIN.DAT.IMAGEINFO.ToString(), TWAIN.MSG.GET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } } WriteOutput("ImageInfo: " + m_twain.ImageinfoToCsv(twimageinfo) + Environment.NewLine); } // And let's get some more meta data, this time using extended image // info, which is a little more complex. This is just being done to // show how to make the call, as a general rule an applications should // use one or the other, not both... if (blXferDone) { TWAIN.TW_EXTIMAGEINFO twextimageinfo = default(TWAIN.TW_EXTIMAGEINFO); TWAIN.TW_INFO twinfo = default(TWAIN.TW_INFO); twextimageinfo.NumInfos = 0; twinfo.InfoId = (ushort)TWAIN.TWEI.DOCUMENTNUMBER; twextimageinfo.Set(twextimageinfo.NumInfos++, ref twinfo); twinfo.InfoId = (ushort)TWAIN.TWEI.PAGESIDE; twextimageinfo.Set(twextimageinfo.NumInfos++, ref twinfo); sts = m_twain.DatExtimageinfo(TWAIN.DG.IMAGE, TWAIN.MSG.GET, ref twextimageinfo); if (sts == TWAIN.STS.SUCCESS) { string szResult = "ExtImageInfo: "; twextimageinfo.Get(0, ref twinfo); if (twinfo.ReturnCode == (ushort)TWAIN.STS.SUCCESS) { szResult += "DocumentNumber=" + twinfo.Item + " "; } twextimageinfo.Get(1, ref twinfo); if (twinfo.ReturnCode == (ushort)TWAIN.STS.SUCCESS) { szResult += "PageSide=" + "TWCS_" + (TWAIN.TWCS)twinfo.Item + " "; } WriteOutput(szResult + Environment.NewLine); } } // Increment the image transfer count... if (blXferDone) { m_iImageXferCount += 1; } // Tell TWAIN that we're done with this image, this is the one place // that we go downstate without using the Rollback function, so that // we can examine the TW_PENDINGXFERS structure... TWAIN.TW_PENDINGXFERS twpendingxfersEndXfer = default(TWAIN.TW_PENDINGXFERS); sts = m_twain.DatPendingxfers(TWAIN.DG.CONTROL, TWAIN.MSG.ENDXFER, ref twpendingxfersEndXfer); if (sts != TWAIN.STS.SUCCESS) { WriteOutput("Scanning error: " + sts + Environment.NewLine); m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 053", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.ENDXFER.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } // We've been asked to do extra work... switch (msgPendingxfers) { // No work needed here... default: case TWAINCSToolkit.MSG.ENDXFER: break; // Reset, we're exiting from scanning... case TWAINCSToolkit.MSG.RESET: m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 054", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.RESET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); // Stop the feeder... case TWAINCSToolkit.MSG.STOPFEEDER: TWAIN.TW_PENDINGXFERS twpendingxfersStopFeeder = default(TWAIN.TW_PENDINGXFERS); sts = m_twain.DatPendingxfers(TWAIN.DG.CONTROL, TWAIN.MSG.STOPFEEDER, ref twpendingxfersStopFeeder); if (sts != TWAIN.STS.SUCCESS) { // If we can't stop gracefully, then just abort... m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 055", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.RESET.ToString(), CvtSts(sts), null, null, null, null, 0); return (TWAIN.STS.SUCCESS); } break; } // If count goes to zero, then the session is complete, and the // driver goes to state 5, otherwise it goes to state 6 in // preperation for the next image. We'll also return a value of // zero if the transfer hits an error, like a paper jam. And then, // just to keep it interesting, we also need to pay attention to // whether or not we have a UI running. If we don't, then state 5 // is our target, otherwise we want to go to state 4 (programmatic // mode)... if (twpendingxfersEndXfer.Count == 0) { WriteOutput(Environment.NewLine + "Scanning done: " + TWAIN.STS.SUCCESS + Environment.NewLine); // Any attempt to scan will look like a new session to us... m_blScanStart = true; // We saved this value for you when MSG_ENABLEDS was called, if the // UI is up, then goto state 5... m_twain.Rollback(m_stateAfterScan); ReportImage("ScanCallback: 056", TWAIN.DG.CONTROL.ToString(), TWAIN.DAT.PENDINGXFERS.ToString(), TWAIN.MSG.RESET.ToString(), CvtSts(sts), null, null, null, null, 0); } // All done... return (TWAIN.STS.SUCCESS); }
/////////////////////////////////////////////////////////////////////////////// // Public Methods: PdfRaster /////////////////////////////////////////////////////////////////////////////// #region Public Methods: PdfRaster /// <summary> /// Add image header to the image data from a PDF/raster.. /// </summary> /// <param name="a_abImage">the image data</param> /// <param name="a_rasterpixelformat">color, grayscale, black and white</param> /// <param name="a_rastercompression">none. group4, jpeg</param> /// <param name="a_lResolution">resolution in dots per inch</param> /// <param name="a_lWidth">width in pixels</param> /// <param name="a_lHeight">height in pixels</param> /// <returns>true on success</returns> public static bool AddImageHeader ( out byte[] a_abImage, byte[] a_abStripData, PdfRasterReader.Reader.PdfRasterReaderPixelFormat a_rasterpixelformat, PdfRasterReader.Reader.PdfRasterReaderCompression a_rastercompression, long a_lResolution, long a_lWidth, long a_lHeight ) { int iCount; int iHeader; IntPtr intptr; TiffBitonalUncompressed tiffbitonaluncompressed; TiffBitonalG4 tiffbitonalg4; TiffGrayscaleUncompressed tiffgrayscaleuncompressed; TiffColorUncompressed tiffcoloruncompressed; // Init stuff... a_abImage = null; // Add a header, if needed... switch (a_rasterpixelformat) { default: return(false); case PdfRasterReader.Reader.PdfRasterReaderPixelFormat.PDFRASREAD_BITONAL: switch (a_rastercompression) { default: return(false); case PdfRasterReader.Reader.PdfRasterReaderCompression.PDFRASREAD_UNCOMPRESSED: iCount = (int)(((a_lWidth + 7) / 8) * a_lHeight); //it's packed tiffbitonaluncompressed = new TiffBitonalUncompressed((uint)a_lWidth, (uint)a_lHeight, (uint)a_lResolution, (uint)iCount); iHeader = Marshal.SizeOf(tiffbitonaluncompressed); a_abImage = new byte[iHeader + iCount]; intptr = Marshal.AllocHGlobal(iHeader); Marshal.StructureToPtr(tiffbitonaluncompressed, intptr, true); Marshal.Copy(intptr, a_abImage, 0, iHeader); Marshal.FreeHGlobal(intptr); Buffer.BlockCopy(a_abStripData, 0, a_abImage, iHeader, iCount); break; case PdfRasterReader.Reader.PdfRasterReaderCompression.PDFRASEARD_CCITTG4: iCount = a_abStripData.Length; tiffbitonalg4 = new TiffBitonalG4((uint)a_lWidth, (uint)a_lHeight, (uint)a_lResolution, (uint)iCount); iHeader = Marshal.SizeOf(tiffbitonalg4); a_abImage = new byte[iHeader + iCount]; intptr = Marshal.AllocHGlobal(iHeader); Marshal.StructureToPtr(tiffbitonalg4, intptr, true); Marshal.Copy(intptr, a_abImage, 0, iHeader); Marshal.FreeHGlobal(intptr); Buffer.BlockCopy(a_abStripData, 0, a_abImage, iHeader, iCount); break; } break; case PdfRasterReader.Reader.PdfRasterReaderPixelFormat.PDFRASREAD_GRAYSCALE: switch (a_rastercompression) { default: return(false); case PdfRasterReader.Reader.PdfRasterReaderCompression.PDFRASREAD_UNCOMPRESSED: iCount = (int)(a_lWidth * a_lHeight); tiffgrayscaleuncompressed = new TiffGrayscaleUncompressed((uint)a_lWidth, (uint)a_lHeight, (uint)a_lResolution, (uint)iCount); iHeader = Marshal.SizeOf(tiffgrayscaleuncompressed); a_abImage = new byte[iHeader + iCount]; intptr = Marshal.AllocHGlobal(iHeader); Marshal.StructureToPtr(tiffgrayscaleuncompressed, intptr, true); Marshal.Copy(intptr, a_abImage, 0, iHeader); Marshal.FreeHGlobal(intptr); Buffer.BlockCopy(a_abStripData, 0, a_abImage, iHeader, iCount); break; case PdfRasterReader.Reader.PdfRasterReaderCompression.PDFRASREAD_JPEG: iCount = a_abStripData.Length; iHeader = 0; a_abImage = new byte[iCount]; Buffer.BlockCopy(a_abStripData, 0, a_abImage, iHeader, iCount); break; } break; case PdfRasterReader.Reader.PdfRasterReaderPixelFormat.PDFRASREAD_RGB: switch (a_rastercompression) { default: return(false); case PdfRasterReader.Reader.PdfRasterReaderCompression.PDFRASREAD_UNCOMPRESSED: iCount = (int)((a_lWidth * 3) * a_lHeight * 3); tiffcoloruncompressed = new TiffColorUncompressed((uint)a_lWidth, (uint)a_lHeight, (uint)a_lResolution, (uint)iCount); iHeader = Marshal.SizeOf(tiffcoloruncompressed); a_abImage = new byte[iHeader + iCount]; intptr = Marshal.AllocHGlobal(iHeader); Marshal.StructureToPtr(tiffcoloruncompressed, intptr, true); Marshal.Copy(intptr, a_abImage, 0, iHeader); Marshal.FreeHGlobal(intptr); Buffer.BlockCopy(a_abStripData, 0, a_abImage, iHeader, iCount); break; case PdfRasterReader.Reader.PdfRasterReaderCompression.PDFRASREAD_JPEG: iCount = a_abStripData.Length; iHeader = 0; a_abImage = new byte[iCount]; Buffer.BlockCopy(a_abStripData, 0, a_abImage, iHeader, iCount); break; } break; } // All done... return(true); }