System.Windows.Form.WebBrowser will not render svg - c#

I can render the text, but cannot render the svg element. When I save the html I am trying to render as a svg file, Internet Explorer will render the complete image the way it should, so I fairly confident my input data is ok.
here is where I launch the thread that starts the webbrowser
Browserinfo bi = new Browserinfo(height, width, rsltPath, stype);
bi.Temphtml = temphtml.Replace("\\", "/");
Thread webBrowseThread = new Thread(new ParameterizedThreadStart(PerformWebBrowseOp));
webBrowseThread.SetApartmentState(ApartmentState.STA);
webBrowseThread.Start(bi);
bi.Temphtml is the file that I save the input data to. This is the way the file looks (with the guts removed)
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><svg xmlns="http://www.w3.org/2000/svg"
..svg data...
</svg></body></html>
here is the actual code:
private void PerformWebBrowseOp(object obj)
{
Browserinfo bi = (Browserinfo)obj;
using (Bitmap bitmap = new Bitmap(bi.Width, bi.Height))
{
using (WebBrowser webBrowser = new WebBrowser())
{
webBrowser.Navigate("file:///"+bi.Temphtml);
while (webBrowser.Document.Body == null)
{
Application.DoEvents();
}
IHTMLDocument2 rawDoc = (IHTMLDocument2)webBrowser.Document.DomDocument;
//rawDoc.write(sbody);
IHTMLElement rawBody = rawDoc.body;
IHTMLElementRender2 render = (IHTMLElementRender2)rawBody;
Rectangle bitmapRect = new Rectangle(0, 0, bi.Width, bi.Height);
webBrowser.DrawToBitmap(bitmap, bitmapRect);
System.Drawing.Imaging.Encoder qualityEncoder = System.Drawing.Imaging.Encoder.Quality;
var quality = (long)100;
using (EncoderParameter ratio = new EncoderParameter(qualityEncoder, quality))
{
using (EncoderParameters codecParams = new EncoderParameters(1))
{
codecParams.Param[0] = ratio;
ImageCodecInfo ici = null;
if (bi.ImgType == "jpg")
{
ici = GetImageEncoder(ImageFormat.Jpeg);
}
else
{
ici = GetImageEncoder(ImageFormat.Png);
}
bitmap.Save(bi.SavePath, ici, codecParams);
}
}
}
}
((AutoResetEvent)waithandle[0]).Set();
}
In the interest of keeping the discussion focused the following notes might be helpful:
I initially used Batik and had it working, but Java is not installed on our webserver and the boss refuses to install it. Back to the drawing board!
I downloaded svg.dll, and got it partially working. It will render part of the svg element, but doesn't render the text.
I think I'm missing an interface or should be calling a com object, but haven't any idea how or what to do.

Simple answer, you cant do it that way. However you can do it with librsvg. I've copied my code below which works on the png but does not for jpeg. If anyone can see whats wrong with the jpeg part, please let me know.
The files you will need are:
freetype6.dll
intl.dll
libcairo-2.dll
libcroco-0.6-3.dll
libexpat-1.dll
libfontconfig-1.dll
libgdk-win32-2.0-0.dll
libgdk_pixbuf-2.0-0.dll
libgio-2.0-0.dll
libglib-2.0-0.dll
libgmodule-2.0-0.dll
libgobject-2.0-0.dll
libgthread-2.0-0.dll
libgtk-win32-2.0-0.dll
libpango-1.0-0.dll
libpangocairo-1.0-0.dll
libpangoft2-1.0-0.dll
libpangowin32-1.0-0.dll
libpixbufloader-svg.dll
libpng14-14.dll
librsvg-2-2.dll
libxml2-2.dll
zlib1.dll
The extract of the svg code from your html page should look like this.
<svg xmlns="http://www.w3.org/2000/svg" width="945" height="325"....</svg>
In other words, get rid of all the html except what is in the svg tags
here is the C# routine
private void RasterizeSvg(string tempsvg, string rsltPath, int _width, int _height, string formattype)
{
string slib = ConfigurationManager.AppSettings["LibrSvgPath"];
if (slib == null)
{
slib = #"C:\Librsvg";
}
bool callSuccessful = SetDllDirectory(slib);
if (!callSuccessful)
{
throw new Exception("Could not set DLL directory");
}
// g_type_init is critical for the png to save correctly
g_type_init();
rsvg_init();
IntPtr error = IntPtr.Zero;
IntPtr rsvghandle = rsvg_handle_new_from_file(tempsvg, out error);
if (error != IntPtr.Zero)
{
throw new Exception(Marshal.ReadInt32(error).ToString());
}
rsvg_handle_close(rsvghandle, out error);
if (error != IntPtr.Zero)
{
throw new Exception(Marshal.ReadInt32(error).ToString());
}
IntPtr cairosurface = cairo_image_surface_create(cairo_format_t.CAIRO_FORMAT_RGB24, _width, _height);
IntPtr cairocontext = cairo_create(cairosurface);
bool brslt = rsvg_handle_render_cairo(rsvghandle, cairocontext);
cairo_destroy(cairocontext);
if (formattype == "png")
{
cairo_surface_write_to_png(cairosurface, rsltPath);
cairo_surface_destroy(cairosurface);
}
else
{
//jpeg not working. the problem is how to convert from a cairo surface to a pixbuf
// once we figure that out, we can implement the jpeg conversion
/*option 1*/
//IntPtr pixbuf = cairo_create(cairosurface);
//cairo_set_source_surface(pixbuf, cairosurface, 0, 0);
//cairo_rectangle(pixbuf, 0, 0, _width, _height);
//cairo_fill(pixbuf);
//error = IntPtr.Zero;
/*option 2*/
IntPtr pixbuf = rsvg_handle_get_pixbuf(rsvghandle);
rsvg_handle_close(rsvghandle, out error);
cairo_surface_destroy(cairosurface);
gdk_init(0, "");
callSuccessful = gdk_pixbuf_save(pixbuf, rsltPath, formattype, out error);
gdk_exit(out error);
if (!callSuccessful)
{
throw new Exception(error.ToInt32().ToString());
}
/* need to release pixbuf memory*/
}
g_object_unref(rsvghandle);
}
As I said at the top, the jpeg doesnt work yet. But it will render out to png.
Finally here are some declarations you will need
enum cairo_format_t
{
CAIRO_FORMAT_INVALID = -1,
CAIRO_FORMAT_ARGB32 = 0,
CAIRO_FORMAT_RGB24 = 1,
CAIRO_FORMAT_A8 = 2,
CAIRO_FORMAT_A1 = 3,
CAIRO_FORMAT_RGB16_565 = 4,
CAIRO_FORMAT_RGB30 = 5
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern bool SetDllDirectory(string pathname);
[DllImport("libgobject-2.0-0.dll", SetLastError = true)]
static extern void g_type_init();
[DllImport("libgobject-2.0-0.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void g_object_unref(IntPtr obj);
[DllImport("librsvg-2-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern bool rsvg_handle_render_cairo(IntPtr handle, IntPtr cairorenderer);
[DllImport("librsvg-2-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr rsvg_handle_get_pixbuf(IntPtr _rsvghandle);
[DllImport("librsvg-2-2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr rsvg_handle_new_from_file(string file_name, out IntPtr error);
[DllImport("librsvg-2-2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern bool rsvg_handle_close(IntPtr _rsvghandle, out IntPtr error);
[DllImport("librsvg-2-2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void rsvg_init();
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr cairo_create(IntPtr cairo_surface_t);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr cairo_image_surface_create(cairo_format_t _pixformat, int width, int height);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void cairo_surface_write_to_png(IntPtr cairo_surface_t, string filename);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void cairo_set_source_surface(IntPtr destbuf, IntPtr srcsuface, double x, double y);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void cairo_rectangle(IntPtr buf, double x, double y, double width, double height);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void cairo_fill(IntPtr destbuf);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void cairo_destroy(IntPtr buf);
[DllImport("libcairo-2.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void cairo_surface_destroy(IntPtr buf);
[DllImport("libgdk_pixbuf-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern bool gdk_pixbuf_save(IntPtr pixbuf, string filename, string type, out IntPtr error);
[DllImport("libgdk_pixbuf-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern IntPtr gdk_pixbuf_get_from_drawable(IntPtr dest, IntPtr src, IntPtr cmap, int srcx, int srcy, int destx, int desty, int width, int height);
[DllImport("libgdk-win32-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void gdk_init(int argcnt, string args);
[DllImport("libgdk-win32-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void gdk_exit(out IntPtr error);

Related

Checking print job status with Winspool Drv

recently I was testing printing using winspool drv and I managed to be able to print successfully through a string (extracted from prn file). Now I want to check the status of my print job. I have read the winspool api documentation but there isn't much of C# examples, so I am kind of lost.
I chanced upon this link and I am facing issues implementing it .
This is my current code
#region Send to Printer
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean SetDefaultPrinter(String name);
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false; // Assume failure unless you specifically succeed.
di.pDocName = "Document";
di.pDataType = "RAW";
// Open the printer.
if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
{
if (hPrinter.ToInt32() == 0)
return false; //Printer not found!!
// Start a document.
if (StartDocPrinter(hPrinter, 1, di))
{
// Start a page.
if (StartPagePrinter(hPrinter))
{
// Write your bytes.
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
// If you did not succeed, GetLastError may give more information
// about why not.
if (bSuccess == false)
{
dwError = Marshal.GetLastWin32Error();
}
return bSuccess;
} public static bool SendStringToPrinter(string szPrinterName, string szString)
{
bool bSuccess = false;
try
{
Byte[] bytes = new Byte[szString.Length];
//your unmanaged pointer
IntPtr pUnmanagedBytes = new IntPtr(0);
//read content to array
bytes = System.Text.Encoding.UTF8.GetBytes(szString);
bytes = System.Text.Encoding.UTF8.GetPreamble().Concat(bytes).ToArray();
// Allocate some unmanaged memory for those bytes.
pUnmanagedBytes = Marshal.AllocCoTaskMem(bytes.Length);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, pUnmanagedBytes, bytes.Length);
//send the unmanage bytes to the printer
bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, bytes.Length);
//Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(pUnmanagedBytes);
}
catch (Exception ex)
{ string s = ex.Message.ToString(); }
return bSuccess;
}
to call the method
SendStringToPrinter(printername, prnstring);
Appreciate if anyone could help thx!
In the code you already have, StartDocPrinter returns a job ID you can query using GetJob.
See here and here for possibly useful info.

Convert text to Latin encoding and decode back problem for Vietnamese

I'm trying to convert Vietnamese to Latin. It is a requirement to send the byte to ESC/P printer (see C# ESC/POS Print Vietnamese for reason why).
But my question is very simple, look at this code:
Encoding enc = Encoding.GetEncoding(1258); //vietnamese code page
string content = "Cơm chiên với các loại gia vị truyền";
string newStr = Encoding.GetEncoding("Latin1").GetString(enc.GetBytes(content));
string origStr = enc.GetString(Encoding.GetEncoding("Latin1").GetBytes(newStr)); //origStr is becomes "Cơm chiên v?i các lo?i gia v? truy?n"
Why is the origStr becomes Cơm chiên v?i các lo?i gia v? truy?n instead of contain the same content with content? But, it works fine in Chinese or Thai. You can test the code below...
For Chinese Simplified
Encoding enc = Encoding.GetEncoding(936); //chinese simplified code page
string content = "印尼炒饭";
string newStr = Encoding.GetEncoding("Latin1").GetString(enc.GetBytes(content));
string origStr = enc.GetString(Encoding.GetEncoding("Latin1").GetBytes(newStr)); //origStr is correct "印尼炒饭"
For Thai
Encoding enc = Encoding.GetEncoding(874); //Thai code page
string content = "ข้าวผัดอินโดนีเซียกับเครื่องเทศแบบดั้ง";
string newStr = Encoding.GetEncoding("Latin1").GetString(enc.GetBytes(content));
string origStr = enc.GetString(Encoding.GetEncoding("Latin1").GetBytes(newStr)); //origStr is correct "ข้าวผัดอินโดนีเซียกับเครื่องเทศแบบดั้ง"
Any idea why? Why Vietnamese won't work? How to make it work so I can send it to Printer?
Thanks for any help :)
PS.
Here is the code I used to send to printer
[DllImport("Winspool.drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("Winspool.drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("Winspool.drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("Winspool.drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("Winspool.drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("Winspool.drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("Winspool.drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
public static bool SendBytesToPrinter(string printerName, IntPtr pBytes, int dwCount, string docName = null, string dataType = "RAW")
{
DOCINFOA di = new DOCINFOA();
di.pDocName = string.IsNullOrWhiteSpace(docName) ? string.Empty : docName;
di.pDataType = string.IsNullOrWhiteSpace(dataType) ? "RAW" : dataType;
IntPtr hPrinter = new IntPtr(0); int dwError = 0, dwWritten = 0; bool bSuccess = false;
if (OpenPrinter(printerName.Normalize(), out hPrinter, IntPtr.Zero))
{
if (StartDocPrinter(hPrinter, 1, di))
{
if (StartPagePrinter(hPrinter))
{
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
if (bSuccess == false)
dwError = Marshal.GetLastWin32Error();
return bSuccess;
}
public static bool SendBytesToPrinter(string printerName, byte[] bytes, string docName)
{
int dwCount = bytes.Length;
IntPtr ptrBytes = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(byte)) * bytes.Length);
try
{
Marshal.Copy(bytes, 0, ptrBytes, bytes.Length);
SendBytesToPrinter(printerName, ptrBytes, dwCount, docName);
}
finally { Marshal.FreeCoTaskMem(ptrBytes); }
return true;
}
public static bool SendStringToPrinter(string printerName, string str, string docName)
{
int dwCount = str.Length;
IntPtr ptrBytes = Marshal.StringToCoTaskMemAnsi(str);
try { SendBytesToPrinter(printerName, ptrBytes, dwCount, docName); }
finally { Marshal.FreeCoTaskMem(ptrBytes); }
return true;
}
Usually this happens when you provide the encoder with data not present in the codepage.
So if you would e.g. try to convert a character with a byte value > 255 (which is not present in codepage 1258) you would get the fallback which is the ? character in this case. My guess is, that you have invalid data in the input string in the first case (maybe characters that look the same but are not the correct ones).
My next step would be to inspect the characters and their corresponding byte values.

Convert Arabic Text to Hex C#

I am building POS Application that print Arabic words into a receipt printer. I used Lprinter class to send printing commands to the printer. my problem is that I can not print Arabic directly to the printer, so after multiple workarounds and search, I found a way to print Arabic on my printer. First I made couple modifiaction to Lprinter class where I changed
CharSet.Ansi
to
CharSet.Auto
also, I copied the print method and changed Marshal.StringToCoTaskMemAnsi to Marshal.StringToCoTaskMemUni. so when I print Arabic, I would call the new method.
This is actually worked for me. now I am trying to convert Arabic text to hex in order to send it to the printer.
I tried the following:
byte[] bytes1 = Encoding.UTF8.GetBytes("ا");
Array.Reverse(bytes1);
var hexString = BitConverter.ToString(bytes1);
hexString = hexString.Replace("-", "");
MyPrinter.PrintAr(hexString);
However, I get the actual hex, as I could not insert \x to allow the printer identify that this is hex rather than text.
FYI, when I send
MyPrinter.PrintAr("\xFEEF\xFE8F\xFEA3");
it actually print Arabic.
I would really appreciate your help in how I could accurately transform Arabic text to hex and then add \x or \u before each hex.
Below is the LPrinter class after modification:
class LPrinter
{
// Structure and API declarions:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPWStr)] public string pDocName;
[MarshalAs(UnmanagedType.LPWStr)] public string pOutputFile;
[MarshalAs(UnmanagedType.LPWStr)] public string pDataType;
}
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPWStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
/*=================================================*/
private IntPtr HandlePrinter;
private PrinterSettings ps;
public LPrinter()
{
HandlePrinter = IntPtr.Zero;
ps = new PrinterSettings();
}
public string PrinterName
{
get
{
return ps.PrinterName;
}
set
{
ps.PrinterName = value;
}
}
public bool ChoosePrinter()
{
PrintDialog pd = new PrintDialog();
pd.PrinterSettings = ps;
if (pd.ShowDialog() == DialogResult.OK)
{
ps = pd.PrinterSettings;
return true;
}
else return false;
}
public bool Open(string DocName)
{
// see if printer is already open
if (HandlePrinter != IntPtr.Zero) return false;
// opens the printer
bool risp = OpenPrinter(ps.PrinterName, out HandlePrinter, IntPtr.Zero);
if (risp == false) return false;
// starts a print job
DOCINFOA MyDocInfo = new DOCINFOA();
MyDocInfo.pDocName = DocName;
MyDocInfo.pOutputFile = null;
MyDocInfo.pDataType = "RAW";
if (StartDocPrinter(HandlePrinter, 1, MyDocInfo))
{
StartPagePrinter(HandlePrinter); //starts a page
return true;
}
else return false;
}
public bool Close()
{
if (HandlePrinter == IntPtr.Zero) return false;
if (!EndPagePrinter(HandlePrinter)) return false;
if (!EndDocPrinter(HandlePrinter)) return false;
if (!ClosePrinter(HandlePrinter)) return false;
HandlePrinter = IntPtr.Zero;
return true;
}
public bool Print(string outputstring)
{
if (HandlePrinter == IntPtr.Zero) return false;
//IntPtr buf = Marshal.StringToCoTaskMemAnsi(outputstring);
IntPtr buf = Marshal.StringToCoTaskMemUni(outputstring);
Int32 done = 0;
bool ok = WritePrinter(HandlePrinter, buf, outputstring.Length, out done);
Marshal.FreeCoTaskMem(buf);
if (!ok) return false;
else return true;
}
public bool PrintAr(string outputstring)
{
if (HandlePrinter == IntPtr.Zero) return false;
// IntPtr buf = Marshal.StringToCoTaskMemAnsi(outputstring);
IntPtr buf = Marshal.StringToCoTaskMemUni(outputstring);
Int32 done = 0;
bool ok = WritePrinter(HandlePrinter, buf, outputstring.Length, out done);
Marshal.FreeCoTaskMem(buf);
if (!ok) return false;
else return true;
}
}

Automated Printing with Crystal Reports and PDFs

I am trying to automate one of our daily print jobs.
On the old VBA program...
We are creating the crystal report, grabbing an 8 1/2 x 11 pdf, then grabbing an 11 x 17 pdf
The program then prints these in consecutive order. They are all sent to the same printer, but the 11 x 17 pdf uses another driver specifically for the paper size. Both pdfs are shelled in adobe and printed.
We are now trying to do the same thing in C#, except without shelling. There are still two drivers set up for the printer, and I have been trying to send the raw data directly to them, but I still have one issue...
The 11 x 17 pdfs are not "true" 11 x 17's (sometimes 12.8 x 18.4, etc.). This causes the printer to stop printing (even appear offline) until you select a tray from the physical printer and click start.
I have played with the driver enough to believe that the driver is not the issue, but the program is to blame. I am trying to use winspool to complete this process, but don't know if this is the right approach.`
namespace WorkOrderMass.Helper
{
public class RawPrinterHelper
{[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
// SendBytesToPrinter()
// When the function is given a printer name and an unmanaged array
// of bytes, the function sends those bytes to the print queue.
// Returns true on success, false on failure.
public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
PrinterSettings ps = new PrinterSettings();
bool bSuccess = false; // Assume failure unless you specifically succeed.
di.pDocName = "PDF Document";
di.pDataType = "RAW";
PrintDocument pd = new PrintDocument();
pd.DefaultPageSettings.PaperSize = new PaperSize("PaperA3", 840, 1180);
pd.Print();
// Open the printer.
if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
{
// Start a document.
if (StartDocPrinter(hPrinter, 1, di))
{
// Start a page.
if (StartPagePrinter(hPrinter))
{
// Write your bytes.
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
System.Threading.Thread.Sleep(5000);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
// If you did not succeed, GetLastError may give more information
// about why not.
if (bSuccess == false)
{
dwError = Marshal.GetLastWin32Error();
}
return bSuccess;
}
public static bool SendFileToPrinter(string szPrinterName, string szFileName)
{
// Open the file.
bool bSuccess = true;
using (FileStream fs = new FileStream(szFileName, FileMode.Open))
{
using (BinaryReader br = new BinaryReader(fs))
{
Byte[] bytes = new Byte[fs.Length];
// Your unmanaged pointer.
IntPtr pUnmanagedBytes = new IntPtr(0);
int nLength;
nLength = Convert.ToInt32(fs.Length);
// Read the contents of the file into the array.
bytes = br.ReadBytes(nLength);
// Allocate some unmanaged memory for those bytes.
pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
// Send the unmanaged bytes to the printer.
bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
// Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(pUnmanagedBytes);
}
}
return bSuccess;
}
}
}`
Answer: Program is now using SumantraPDF to silently print the PDF's.

Lighting USB OpenDMX FTD2XX DMXking

Couple of quick questions. I have a DMX king USB lighting controller that I'm trying to control.
It's based on the Open DMX protocol (from Entec) who make available a c# class.
I've got the device plugged into an RGB can, and if I test the USB device with their driver, it connects to COM4 and when I switch their software into transmit mode, I can then set individual DMX channels.
Using their OpenDMX class, with a few modifications (the core is the same, i've just added some extra error checking, I can locate the device, query it's information etc.
When I open the device I get a handle.
I can write to that device with FT_Write but no matter what I do, no lights actually come on.
Here's a few relevant code snippets:
public static byte[] buffer;
[DllImport("FTD2XX.dll")]
public static extern FT_STATUS FT_Open(UInt32 uiPort, ref uint ftHandle);
[DllImport("FTD2XX.dll")]
public static extern FT_STATUS FT_Write(uint ftHandle, IntPtr lpBuffer, UInt32 dwBytesToRead, ref UInt32 lpdwBytesWritten);
public static void writeData()
{
while (!done)
{
try
{
initOpenDMX();
status = FT_SetBreakOn(handle);
status = FT_SetBreakOff(handle);
bytesWritten = write(handle, buffer, buffer.Length);
if (bytesWritten == 0)
{
break;
}
System.Threading.Thread.Sleep(25);
}
catch (Exception)
{
break;
}
}
Connected = false;
done = false;
}
All the status come back as FT_Ok, and bytesWritten comes back as 512 (the number of channels on this USB controller)
I keep thinking I've missed something like setting the device into a transmit mode or similar (it only has one DMX socket)
public static void initOpenDMX()
{
status = FT_ResetDevice(handle);
status = FT_SetDivisor(handle, (char)12); // set baud rate
status = FT_SetDataCharacteristics(handle, BITS_8, STOP_BITS_2, PARITY_NONE);
status = FT_SetFlowControl(handle, (char)FLOW_NONE, 0, 0);
status = FT_ClrRts(handle);
status = FT_SetLatencyTimer(handle, (byte)40);
status = FT_Purge(handle, PURGE_TX);
status = FT_Purge(handle, PURGE_RX);
}
I've also tried the Entec OpenDMX class without any modifications from me and it doesn't seem to do anything either.
Just want to stress that their control software is working fine, so the light and controller are compatible. I think something is missing in the way I'm using FTD2xx.
There's no errors coming through (everything is FT_OK) so this suggests the DLL is working - especially since I can query the device using the FT_ListDevices and FT_GetDeviceInfo methods.
Any ideas?
Gareth
To resolve this, I emailed the manufacturer.
It turned out that the device wasn't OpenDMX, in fact it was a DMXProUSB
The protocol was pretty similar, and it was based on the FTDI chip which is why the code partly worked, but it has a microcontroller in it.
I converted the C++ example controller file to C# and got it all working.
If this ever comes up again, I am happy to share the resulting c# code for the DMXProUSB however with no support.
I have emailed the code to the manfacturer (dmxking) and have placed a copy on github: https://github.com/agrath/Sniper.Lighting.Dmx
Thanks for your help
I ran Hippy's VB version through a mechanical VB to C# translator and found one key
difference. FT_WRITE uses a string to pass data to the unmanaged code. The C# class uses
an IPtr pointing to a byte array.
This version works for me:
using System;
using System.Runtime.InteropServices;
using System.Threading;
// based on Hippy's VB Example
// http://members.westnet.com.au/rowanmac/opendmx.html#tx
// Working link: https://web.archive.org/web/20150217155014/http://members.westnet.com.au:80/rowanmac/opendmx.html
namespace Test
{
class Program
{
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Open(short intDeviceNumber, ref int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Close(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetDivisor(int lngHandle, int div);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Read(int lngHandle, string lpszBuffer, int lngBufferSize, ref int lngBytesReturned);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Write(int lngHandle, string lpszBuffer, int lngBufferSize, ref int lngBytesWritten);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Write(int lngHandle, IntPtr lpBuffer, int lngBufferSize, ref int lngBytesWritten);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetBaudRate(int lngHandle, int lngBaudRate);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetDataCharacteristics(int lngHandle, byte byWordLength, byte byStopBits, byte byParity);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetFlowControl(int lngHandle, short intFlowControl, byte byXonChar, byte byXoffChar);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_ResetDevice(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetDtr(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_ClrDtr(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetRts(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_ClrRts(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetModemStatus(int lngHandle, ref int lngModemStatus);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_Purge(int lngHandle, int lngMask);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetStatus(int lngHandle, ref int lngRxBytes, ref int lngTxBytes, ref int lngEventsDWord);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetQueueStatus(int lngHandle, ref int lngRxBytes);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_GetEventStatus(int lngHandle, ref int lngEventsDWord);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetChars(int lngHandle, byte byEventChar, byte byEventCharEnabled, byte byErrorChar, byte byErrorCharEnabled);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetTimeouts(int lngHandle, int lngReadTimeout, int lngWriteTimeout);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetBreakOn(int lngHandle);
[DllImport("FTD2XX.DLL", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int FT_SetBreakOff(int lngHandle);
// FTDI Constants
const short FT_OK = 0;
const short FT_INVALID_HANDLE = 1;
const short FT_DEVICE_NOT_FOUND = 2;
const short FT_DEVICE_NOT_OPENED = 3;
const short FT_IO_ERROR = 4;
const short FT_INSUFFICIENT_RESOURCES = 5;
// Word Lengths
const byte FT_BITS_8 = 8;
// Stop Bits
const byte FT_STOP_BITS_2 = 2;
// Parity
const byte FT_PARITY_NONE = 0;
// Flow Control
const byte FT_FLOW_NONE = 0x0;
// Purge rx and tx buffers
const byte FT_PURGE_RX = 1;
const byte FT_PURGE_TX = 2;
public static int handle=0;
public static byte[] buffer = new byte[4]; // can be up to 512, shorter is faster
private static string lpszBuffer=""+ (char) 0 + (char) 64 + (char) 64+ (char) 0;
static void Main(string[] args)
{
init();
}
public static string init()
{
short n = 0;
// ==== ATTEMPT TO OPEN DEVICE ====
if (FT_Open(n, ref handle) != FT_OK)
{
return "FTTD Not Found";
}
// ==== PREPARE DEVICE FOR DMX TRANSMISSION ====
// reset the device
if (FT_ResetDevice(handle) != FT_OK)
{
return "Failed To Reset Device!";
}
// get an ID from the widget from jumpers
// GetID(ref n);
// set the baud rate
if (FT_SetDivisor(handle, 12) != FT_OK)
{
return "Failed To Set Baud Rate!";
}
// shape the line
if (FT_SetDataCharacteristics(handle, FT_BITS_8, FT_STOP_BITS_2, FT_PARITY_NONE) != FT_OK)
{
return "Failed To Set Data Characteristics!";
}
// no flow control
if (FT_SetFlowControl(handle, FT_FLOW_NONE, 0, 0) != FT_OK)
{
return "Failed to set flow control!";
}
// set bus transiever to transmit enable
if (FT_ClrRts(handle) != FT_OK)
{
return "Failed to set RS485 to send!";
}
// Clear TX & RX buffers
if (FT_Purge(handle, FT_PURGE_TX) != FT_OK)
{
return "Failed to purge TX buffer!";
}
// empty buffers
if (FT_Purge(handle, FT_PURGE_RX) != FT_OK)
{
return "Failed to purge RX buffer!";
}
setDmxValue(0, 0); // should always be zero
setDmxValue(1, 64);
setDmxValue(2, 64);
setDmxValue(3, 0);
Thread thread = new Thread(new ThreadStart(writeDataThread));
thread.Start();
return "Ok";
}
// init
public static void setDmxValue(int channel, byte value)
{
buffer[channel] = value;
lpszBuffer="";
for (int i = 0; i < buffer.Length; ++i)
{
lpszBuffer += (char)buffer[i];
}
}
public static void writeDataThread()
{
bool done = false;
int lngBytesWritten=0;
while (!done)
{
FT_SetBreakOn(handle);
FT_SetBreakOff(handle);
FT_Write(handle, lpszBuffer, buffer.Length, ref lngBytesWritten);
System.Threading.Thread.Sleep(50);
}
}
}
}
I wrote the C# class on the open dmx site.
It was based on Hippy's Open DMX driver written in VB.
// Old broken link:
http://members.westnet.com.au/rowanmac/opendmx.html#tx
// Working link: https://web.archive.org/web/20150217155014/http://members.westnet.com.au:80/rowanmac/opendmx.html
The C# Class in not initializing something on the FDDI chip.
This has been tormenting me for years.
I have my suspicions about FT_SetFlowControl. In the VB app, the second parameter is a short int. But any call to it's equivalent in the C# class will only work if the second parameter is cast to a char.
[DllImport("FTD2XX.dll")]
public static extern FT_STATUS FT_SetFlowControl(uint ftHandle, UInt16 usFlowControl, byte uXon, byte uXoff);
I fix the problem with Marshal.Copy
public static void writeDataThread(int Length)
{
int lngBytesWritten = 0;
IntPtr pnt = Marshal.AllocHGlobal(Length);
Marshal.Copy(buffer, 0, pnt, Length);
FT_SetBreakOn(handle);
FT_SetBreakOff(handle);
string StartCode = null;
FT_Write(handle, StartCode, 1, ref lngBytesWritten);
FT_Write(handle, pnt, Length, ref lngBytesWritten);
}
FTDI chips have GPIO pins in addition to the serial ports. Hopefully the documentation tells you whether these need to be set. The function is FT_SetBitMode. Documentation here.

Categories

Resources