public void ScanImageReadLine ( Object sender, DataReceivedEventArgs e ) { lock (objectScanImageReadLine) { bool blSuccess; bool blEndOfJob = false; int iImageNumber; // Log it, note that we may not get everything because .NET can // fumble the standard output. This isn't a problem, it's just // nice to show what we can get... if (e == null) { Log.Info("scanimage>>> (program exited)"); blEndOfJob = true; } else if (string.IsNullOrEmpty(e.Data)) { Log.Info("scanimage>>> (no data)"); return; } else { Log.Info("scanimage>>> " + e.Data); if (!e.Data.StartsWith("Scanned page")) { return; } } // Get the list of files... string[] aszPnm = Directory.GetFiles(m_szImagesFolder); if (aszPnm == null) { if (blEndOfJob) { SetEndOfJob("SUCCESS"); } return; } // Convert all .pnm files into .pdf and metadata... foreach (string szPnm in aszPnm) { string szMeta; byte[] abPnm; // Skip stuff that isn't .pnm... if (!szPnm.EndsWith(".pnm")) { continue; } // Load the .pnm data... Log.Info("scanimage>>> load " + szPnm); try { abPnm = File.ReadAllBytes(szPnm); } catch { // We'll assume that the file isn't ready for us yet, // so bail... Log.Info("scanimage>>> ReadAllBytes failed (file may be in use)..."); if (blEndOfJob) { SetEndOfJob("SUCCESS"); } return; } // Convert the first 40 bytes to ANSI... string szHeader = Encoding.ASCII.GetString(abPnm, 0, 40); string[] aszHeader = szHeader.Split(new char[] { '\n', '\r' }); // Get the pixelformat and the stride... int iStride = 0; int iWidth = 0; int iHeight = 0; int iImageOffset = 0; string szPixelFormat = aszHeader[0]; string szTwainDirectPixelFormat; switch (szPixelFormat) { default: TwainDirect.Support.Log.Info("scanimage>>> Not supported pixel format..." + szHeader); continue; case "P4": szTwainDirectPixelFormat = "bw1"; iImageOffset = (aszHeader[0].Length + 1) + (aszHeader[1].Length + 1) + (aszHeader[2].Length + 1); break; case "P5": szTwainDirectPixelFormat = "gray8"; iImageOffset = (aszHeader[0].Length + 1) + (aszHeader[1].Length + 1) + (aszHeader[2].Length + 1) + (aszHeader[3].Length + 1); break; case "P6": szTwainDirectPixelFormat = "rgb25"; iImageOffset = (aszHeader[0].Length + 1) + (aszHeader[1].Length + 1) + (aszHeader[2].Length + 1) + (aszHeader[3].Length + 1); break; } // Get the width and the height... string[] aszDim = aszHeader[2].Split(new char[] { ' ' }); int.TryParse(aszDim[0], out iWidth); int.TryParse(aszDim[1], out iHeight); // Get the image data... byte[] abImage = new byte[abPnm.Length - iImageOffset]; Buffer.BlockCopy(abPnm, iImageOffset, abImage, 0, abPnm.Length - iImageOffset); // Stupid bitonal data, you had a 50/50 shot! if (szPixelFormat == "P4") { for (int bb = 0; bb < abImage.Length; bb++) { abImage[bb] = (byte)~abImage[bb]; } } // Infer the stride from the image size / by the height... iStride = abImage.Length / iHeight; // Get a byte array from the image... Log.Info("scanimage>>> done: format=" + szPixelFormat + " " + iWidth + "x" + iHeight + " res=" + SaneTask.ms_szResolution + " stride=" + iStride); // So far so good, let's extract the image number... if (!int.TryParse(Path.GetFileNameWithoutExtension(szPnm).Replace("img", ""), out iImageNumber)) { Log.Info("scanimage>>> failed to get the image number..."); continue; } // Create the .pdf... string szPdfFile = szPnm.Remove(szPnm.Length - 4, 4) + ".pdf"; Log.Info("scanimage>>> creating pdf " + szPdfFile); blSuccess = PdfRaster.CreatePdfRaster ( szPdfFile, "", abImage, 0, szTwainDirectPixelFormat, "none", int.Parse(SaneTask.ms_szResolution), iWidth, iHeight ); if (!blSuccess) { Log.Error("ReportImage: unable to save the image file..." + szPdfFile); //m_blProcessing = false; //m_blCancel = false; SetEndOfJob("FILEWRITEERROR"); return; } Log.Info("scanimage>>> done..."); // TWAIN Direct metadata... szMeta = ""; // TWAIN Direct metadata.address begin... szMeta += " \"metadata\": {\n"; // TWAIN Direct metadata.address begin... szMeta += " \"address\": {\n"; // Imagecount (counts images)... szMeta += " \"imageNumber\": " + m_iImageCount + ",\n"; // Sheetcount (counts sheets, including ones lost to blank image dropout)... szMeta += " \"sheetNumber\": " + "1" + ",\n"; // The image came from a flatbed or a feederFront or whatever... if ((iImageNumber & 1) == 1) { szMeta += " \"source\": \"" + "feederFront" + "\"\n"; } else { szMeta += " \"source\": \"" + "feederRear" + "\"\n"; } // TWAIN Direct metadata.address end... szMeta += " },\n"; // TWAIN Direct metadata.image begin... szMeta += " \"image\": {\n"; // Add compression... szMeta += " \"compression\": \"" + "none" + "\",\n"; // Add pixel format... switch (szPixelFormat) { default: case "P4": szMeta += " \"pixelFormat\": \"" + "bw1" + "\",\n"; break; case "P5": szMeta += " \"pixelFormat\": \"" + "gray8" + "\",\n"; break; case "P6": szMeta += " \"pixelFormat\": \"" + "rgb24" + "\",\n"; break; } // Add height... szMeta += " \"pixelHeight\": " + iHeight + ",\n"; // X-offset... szMeta += " \"pixelOffsetX\": " + "0" + ",\n"; // Y-offset... szMeta += " \"pixelOffsetY\": " + "0" + ",\n"; // Add width... szMeta += " \"pixelWidth\": " + iWidth + ",\n"; // Add resolution... szMeta += " \"resolution\": " + SaneTask.ms_szResolution + ",\n"; // Add size... FileInfo fileinfo = new FileInfo(szPnm); szMeta += " \"size\": " + fileinfo.Length + "\n"; // TWAIN Direct metadata.image end... szMeta += " },\n"; // TWAIN Direct metadata.address begin... szMeta += " \"imageBlock\": {\n"; // Imagecount (counts images)... szMeta += " \"imageNumber\": " + m_iImageCount + ",\n"; // Segmentcount (long document or huge document)... szMeta += " \"imagePart\": " + "1" + ",\n"; // Segmentlast (long document or huge document)... szMeta += " \"moreParts\": " + "\"lastPartInFile\"" + "\n"; // TWAIN Direct metadata.address end... szMeta += " },\n"; // Open SWORD.metadata.status... szMeta += " \"status\": {\n"; // Add the status... szMeta += " \"success\": true\n"; // TWAIN Direct metadata.status end... szMeta += " }\n"; // TWAIN Direct metadata end... szMeta += " }\n"; // Get rid of the .pnm file... TwainDirect.Support.Log.Info("scanimage>>> deleting the pnm file..."); abImage = null; abPnm = null; try { File.Delete(szPnm); } catch { // Don't really care... } // Save the metadata to disk... try { string szMetaFile = szPnm.Remove(szPnm.Length - 4, 4) + ".meta"; File.WriteAllText(szMetaFile, szMeta); Log.Info("ReportImage: saved " + szMetaFile); } catch { Log.Error("ReportImage: unable to save the metadata file..."); //m_blProcessing = false; //m_blCancel = false; SetEndOfJob("FILEWRITEERROR"); return; } } // Looks like we're all done... if (blEndOfJob) { SetEndOfJob("SUCCESS"); m_processScanImage = null; } } }
/// <summary> /// Test the code... /// </summary> /// <returns></returns> public bool Test() { BinaryWriter binarywriter; PdfRaster pdfraster = new PdfRaster(); PdfRaster.t_OS os; byte[] bitonalData = new byte[((850 + 7) / 8) * 1100]; string OUTPUT_FILENAME = "raster.pdf"; byte[] _imdata = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, }; // Create the file... binarywriter = new BinaryWriter(File.Create(OUTPUT_FILENAME)); if (binarywriter == null) { TWAINWorkingGroup.Log.Error("unable to open %s for writing " + OUTPUT_FILENAME); return(false); } // Set up our worker functions... os = new PdfRaster.t_OS(); os.alloc = null; os.free = null; os.memset = null; os.memclear = null; os.allocsys = PdfRaster.pd_alloc_sys_new(os); os.writeout = myOutputWriter; os.writeoutcookie = binarywriter; // Construct a raster PDF encoder object enc = pdfraster.pd_raster_encoder_create(PdfRaster.PdfRasterConst.PDFRAS_API_LEVEL, os); PdfRaster.pd_raster_set_creator(enc, "raster_encoder_demo 1.0"); // First page - 4" x 5.5" at 2 DPI PdfRaster.pd_raster_set_resolution(enc, 2.0, 2.0); // start a new page pdfraster.pd_raster_encoder_start_page(enc, PdfRaster.RasterPixelFormat.PDFRAS_GRAYSCALE, PdfRaster.RasterCompression.PDFRAS_UNCOMPRESSED, 8); // write a strip of raster data to the current page // 11 rows high pdfraster.pd_raster_encoder_write_strip(enc, 11, _imdata, 0, (UInt32)_imdata.Length); // the page is done pdfraster.pd_raster_encoder_end_page(enc); // Next page: bitonal 8.5 x 11 at 100 DPI with a light dotted grid // generate page data for (int i = 0; i < bitonalData.Length; i++) { int y = (i / 107); int b = (i % 107); if ((y % 100) == 0) { bitonalData[i] = 0xAA; } else if (((b % 12) == 0) && ((y & 1) != 0)) { bitonalData[i] = 0x7F; } else { bitonalData[i] = 0xff; } } PdfRaster.pd_raster_set_resolution(enc, 100.0, 100.0); pdfraster.pd_raster_encoder_start_page(enc, PdfRaster.RasterPixelFormat.PDFRAS_BITONAL, PdfRaster.RasterCompression.PDFRAS_UNCOMPRESSED, 850); pdfraster.pd_raster_encoder_write_strip(enc, 1100, bitonalData, 0, (UInt32)bitonalData.Length); pdfraster.pd_raster_encoder_end_page(enc); // Third page: color 3.5" x 2" 50 DPI int stride = 3; byte[] colorData = new byte[175 * 100 * stride]; for (int i = 0; i < colorData.Length; i += stride) { int y = ((i / stride) / 175); int x = ((i / stride) % 175); colorData[i + 0] = (byte)((i / stride) % 255); colorData[i + 1] = (byte)((x + y) % 255); colorData[i + 2] = (byte)((x - y + 9999) % 255); } PdfRaster.pd_raster_set_resolution(enc, 50.0, 50.0); pdfraster.pd_raster_encoder_start_page(enc, PdfRaster.RasterPixelFormat.PDFRAS_RGB, PdfRaster.RasterCompression.PDFRAS_UNCOMPRESSED, 175); pdfraster.pd_raster_encoder_write_strip(enc, 100, colorData, 0, (UInt32)colorData.Length); pdfraster.pd_raster_encoder_end_page(enc); // the document is complete pdfraster.pd_raster_encoder_end_document(enc); // clean up binarywriter.Flush(); binarywriter.Close(); pdfraster.pd_raster_encoder_destroy(enc); // All done... return(true); }
/// <summary> /// Start polling for work... /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void m_buttonStart_Click(object sender, EventArgs e) { bool blSuccess; // Turn all the buttons off... SetButtons(ButtonState.Undefined); // Is PDF/raster happy and healthy? PdfRaster pdfraster = new PdfRaster(); if (!pdfraster.HealthCheck()) { DialogResult dialogresult = MessageBox.Show ( "We need to install the Visual Studio 2017 Redistributables for PDF/raster. May we continue?", "Warning", MessageBoxButtons.YesNo ); if (dialogresult == DialogResult.Yes) { pdfraster.InstallVisualStudioRedistributables(); } } // Start polling... Display(""); Display("Starting, please wait..."); if (Config.Get("imageBlockSize", 0) < 8192) { Display("Each image will be transferred in its own imageBlock..."); } else { Display("imageBlocks will not exceed " + Config.Get("imageBlockSize", 0) + " bytes..."); } string szNote = m_scanner.GetTwainLocalNote(); if (!string.IsNullOrEmpty(szNote)) { Display(m_scanner.GetTwainLocalTy() + " (" + szNote + ")"); } else { Display(m_scanner.GetTwainLocalTy()); } // Start monitoring the cloud... try { blSuccess = await m_scanner.MonitorTasksStart(); if (!blSuccess) { Log.Error("MonitorTasksStart failed..."); MessageBox.Show("Failed to start cloud monitoring, check the logs for more information.", Config.GetResource(m_resourcemanager, "strFormMainTitle")); SetButtons(ButtonState.WaitingForStart); return; } } catch (Exception exception) { Log.Error("MonitorTasksStart failed..."); MessageBox.Show("Failed to start the cloud monitoring, check the logs for more information." + Environment.NewLine + "Error: " + exception.Message, Config.GetResource(m_resourcemanager, "strFormMainTitle")); } if (m_scanner.IsTwainLocalStarted()) { Display("TWAIN Local is ready for use..."); } if (m_scanner.IsTwainCloudStarted()) { Display("TWAIN Cloud is ready for use..."); } // Set buttons... SetButtons(ButtonState.Started); }