Ticket PDF en C#

Hoy veremos como es la creación de un Ticket y que salga en un documento PDF.
Nota: Se trata de una adaptación a la lógica de otro autor que encontré en internet.

Se requiere utilizar la librería iTextSharp la cual puede buscarse en internet.

Aqui una imagen de como es la apariencia:
Para facilitarles el proceso, aqui les dejo el código en una clase y mas abajo les explico acerca de su utlización:

  1. using System;
  2. using System.Collections;
  3. using iTextSharp;
  4. using iTextSharp.text;
  5. using iTextSharp.text.pdf;
  6. using System.IO;
  7. namespace TyroDeveloperDLL{
  8. public class TicketPDF{
  9. public TicketPDF(){
  10. myDocument.AddAuthor("TyroDeveloper");
  11. myDocument.AddCreator("JUAN GABRIEL CASTILLO TURRUBIATES");
  12. myDocument.AddTitle("Ticket de Venta");
  13. }
  14. PdfWriter writer = null;
  15. PdfContentByte cb=null;
  16. ArrayList headerLines = new ArrayList();
  17. ArrayList subHeaderLines = new ArrayList();
  18. ArrayList items = new ArrayList();
  19. ArrayList totales = new ArrayList();
  20. ArrayList footerLines = new ArrayList();
  21. private string headerImage = "";
  22. bool _DrawItemHeaders = true;
  23. int count = 0;
  24. string path = "";
  25. string file_name = "";
  26. int maxChar = 40;
  27. int maxCharDescription = 20;
  28. int imageHeight = 0;
  29. float leftMargin = 0;
  30. float topMargin = 5;
  31. static int fontSize = 7;
  32. static BaseFont bfCourier =
  33. BaseFont.CreateFont(BaseFont.COURIER, BaseFont.CP1252, false);
  34. static Font font = new
  35. Font(bfCourier, fontSize, Font.NORMAL, Color.BLACK);
  36. Document myDocument = new
  37. Document(PageSize.LETTER); //Aqui se ponen todos los objetos
  38. string line = "";
  39. #region Properties
  40. public String Path {
  41. get { return path; }
  42. set { path = value; }
  43. }
  44. public String FileName {
  45. get { return file_name; }
  46. set { file_name = value; }
  47. }
  48. public String FullFileName {
  49. get { return(String.Format("{0}{1}", path, file_name)); }
  50. }
  51. public String HeaderImage
  52. {
  53. get { return headerImage; }
  54. set { if (headerImage != value) headerImage = value; }
  55. }
  56. public int MaxChar
  57. {
  58. get { return maxChar; }
  59. set { if (value != maxChar) maxChar = value; }
  60. }
  61. public bool DrawItemHeaders{
  62. set { _DrawItemHeaders = value; }
  63. }
  64. public int MaxCharDescription
  65. {
  66. get { return maxCharDescription; }
  67. set { if (value != maxCharDescription) maxCharDescription = value; }
  68. }
  69. public int FontSize
  70. {
  71. get { return fontSize; }
  72. set { if (value != fontSize) fontSize = value; }
  73. }
  74. public Font FontName
  75. {
  76. get { return font; }
  77. set { if (value != font) font = value; }
  78. }
  79. #endregion
  80. public void AddHeaderLine(string line)
  81. {
  82. headerLines.Add(line);
  83. }
  84. public void AddSubHeaderLine(string line){
  85. subHeaderLines.Add(line);
  86. }
  87. public void AddItem(string cantidad, string item, string price){
  88. TicketOrderItem newItem = new TicketOrderItem('?');
  89. items.Add(newItem.GenerateItem(cantidad, item, price));
  90. }
  91. public void AddTotal(string name, string price){
  92. TicketOrderTotal newTotal = new TicketOrderTotal('?');
  93. totales.Add(newTotal.GenerateTotal(name, price));
  94. }
  95. public void AddFooterLine(string line){
  96. footerLines.Add(line);
  97. }
  98. private string AlignRightText(int lenght){
  99. string espacios = "";
  100. int spaces = maxChar - lenght;
  101. for (int x = 0; x < spaces; x++)
  102. espacios += " ";
  103. return espacios;
  104. }
  105. private string DottedLine(){
  106. string dotted = "";
  107. for (int x = 0; x < maxChar; x++)
  108. dotted += "=";
  109. return dotted;
  110. }
  111. public bool Print() {
  112. try{
  113. //aqui para generar el PDF
  114. writer = PdfWriter.GetInstance(myDocument,
  115. new FileStream(path + file_name, FileMode.Create));
  116. myDocument.Open();
  117. cb = writer.DirectContent;
  118. cb.SetFontAndSize(font.BaseFont, fontSize);
  119. cb.BeginText();
  120. DrawImage();
  121. DrawHeader();
  122. DrawSubHeader();
  123. DrawItems();
  124. DrawTotales();
  125. DrawFooter();
  126. cb.EndText();
  127. myDocument.Close();
  128. return true;
  129. }
  130. catch (Exception ex) {
  131. throw (ex);
  132. }
  133. }
  134. private float YPosition(){
  135. return (myDocument.PageSize.Height -
  136. (topMargin + (count * font.CalculatedSize + imageHeight)));
  137. }
  138. private void DrawImage() {
  139. try{
  140. if ((headerImage != null) && (headerImage != "")){
  141. if (File.Exists(headerImage)) {
  142. Image logo = Image.GetInstance(headerImage);
  143. double height = ((double)logo.Height / 58) * 15;
  144. imageHeight = (int)Math.Round(height) + 3;
  145. logo.SetAbsolutePosition(0,
  146. myDocument.PageSize.Height - imageHeight);
  147. logo.ScaleToFit(logo.Width,imageHeight);
  148. myDocument.Add(logo);
  149. }
  150. }
  151. }
  152. catch (Exception ex){throw (ex);}
  153. }
  154. private void DrawHeader(){
  155. try{
  156. foreach (string header in headerLines){
  157. if (header.Length > maxChar) {
  158. int currentChar = 0;
  159. int headerLenght = header.Length;
  160. while (headerLenght > maxChar){
  161. line = header.Substring(currentChar, maxChar);
  162. cb.SetTextMatrix(leftMargin, YPosition());
  163. cb.ShowText(line);
  164. count++;
  165. currentChar += maxChar;
  166. headerLenght -= maxChar;
  167. }
  168. line = header;
  169. cb.SetTextMatrix(leftMargin, YPosition());
  170. cb.ShowText(line.Substring(currentChar,
  171. line.Length - currentChar));
  172. count++;
  173. }
  174. else{
  175. line = header;
  176. cb.SetTextMatrix(leftMargin, YPosition());
  177. cb.ShowText(line);
  178. count++;
  179. }
  180. }
  181. DrawEspacio();
  182. }
  183. catch (Exception ex) { throw (ex); }
  184. }
  185. private void DrawSubHeader() {
  186. try{
  187. line = DottedLine();
  188. cb.SetTextMatrix(leftMargin, YPosition());
  189. cb.ShowText(line);
  190. DrawEspacio();
  191. foreach (string subHeader in subHeaderLines){
  192. if (subHeader.Length > maxChar){
  193. int currentChar = 0;
  194. int subHeaderLenght = subHeader.Length;
  195. while (subHeaderLenght > maxChar){
  196. line = subHeader;
  197. cb.SetTextMatrix(leftMargin, YPosition());
  198. cb.ShowText(line.Substring(currentChar, maxChar));
  199. count++;
  200. currentChar += maxChar;
  201. subHeaderLenght -= maxChar;
  202. }
  203. line = subHeader;
  204. cb.SetTextMatrix(leftMargin, YPosition());
  205. cb.ShowText(line.Substring(currentChar,
  206. line.Length - currentChar));
  207. count++;
  208. line = DottedLine();
  209. cb.SetTextMatrix(leftMargin, YPosition());
  210. cb.ShowText(line);
  211. DrawEspacio();
  212. }
  213. else
  214. {
  215. line = subHeader;
  216. cb.SetTextMatrix(leftMargin, YPosition());
  217. cb.ShowText(line);
  218. count++;
  219. line = DottedLine();
  220. cb.SetTextMatrix(leftMargin, YPosition());
  221. cb.ShowText(line);
  222. count++;
  223. }
  224. }
  225. DrawEspacio();
  226. }
  227. catch (Exception ex) { throw (ex); }
  228. }
  229. private void DrawItems() {
  230. TicketOrderItem ordIt = new TicketOrderItem('?');
  231. if (_DrawItemHeaders){
  232. cb.SetTextMatrix(leftMargin, YPosition());
  233. cb.ShowText("CANT DESCRIPCION IMPORTE");
  234. }
  235. count++;
  236. DrawEspacio();
  237. foreach (string item in items){
  238. line = ordIt.GetItemCantidad(item);
  239. cb.SetTextMatrix(leftMargin, YPosition());
  240. cb.ShowText(line);
  241. line = ordIt.GetItemPrice(item);
  242. line = AlignRightText(line.Length) + line;
  243. cb.SetTextMatrix(leftMargin, YPosition());
  244. cb.ShowText(line);
  245. string name = ordIt.GetItemName(item);
  246. leftMargin = 0;
  247. if (name.Length > maxCharDescription) {
  248. int currentChar = 0;
  249. int itemLenght = name.Length;
  250. while (itemLenght > maxCharDescription){
  251. line = ordIt.GetItemName(item);
  252. cb.SetTextMatrix(leftMargin, YPosition());
  253. cb.ShowText(" " + line.Substring(currentChar,
  254. maxCharDescription));
  255. count++;
  256. currentChar += maxCharDescription;
  257. itemLenght -= maxCharDescription;
  258. }
  259. line = ordIt.GetItemName(item);
  260. cb.SetTextMatrix(leftMargin, YPosition());
  261. cb.ShowText(" " + line.Substring(currentChar,
  262. maxCharDescription));
  263. count++;
  264. }
  265. else{
  266. cb.SetTextMatrix(leftMargin, YPosition());
  267. cb.ShowText(" " + ordIt.GetItemName(item));
  268. count++;
  269. }
  270. }
  271. leftMargin = 0;
  272. DrawEspacio();
  273. line = DottedLine();
  274. cb.SetTextMatrix(leftMargin, YPosition());
  275. cb.ShowText(line);
  276. count++;
  277. DrawEspacio();
  278. }
  279. private void DrawTotales(){
  280. TicketOrderTotal ordTot = new TicketOrderTotal('?');
  281. foreach (string total in totales){
  282. line = ordTot.GetTotalCantidad(total);
  283. line = AlignRightText(line.Length) + line;
  284. cb.SetTextMatrix(leftMargin, YPosition());
  285. cb.ShowText(line);
  286. leftMargin = 0;
  287. line = "" + ordTot.GetTotalName(total);
  288. cb.SetTextMatrix(leftMargin, YPosition());
  289. cb.ShowText(line);
  290. count++;
  291. }
  292. leftMargin = 0;
  293. DrawEspacio();
  294. DrawEspacio();
  295. }
  296. private void DrawFooter(){
  297. foreach (string footer in footerLines){
  298. if (footer.Length > maxChar){
  299. int currentChar = 0;
  300. int footerLenght = footer.Length;
  301. while (footerLenght > maxChar){
  302. line = footer;
  303. cb.SetTextMatrix(leftMargin, YPosition());
  304. cb.ShowText(line.Substring(currentChar, maxChar));
  305. count++;
  306. currentChar += maxChar;
  307. footerLenght -= maxChar;
  308. }
  309. line = footer;
  310. cb.SetTextMatrix(leftMargin, YPosition());
  311. cb.ShowText(line.Substring(currentChar, maxChar));
  312. count++;
  313. }
  314. else {
  315. line = footer;
  316. cb.SetTextMatrix(leftMargin, YPosition());
  317. cb.ShowText(line);
  318. count++;
  319. }
  320. }
  321. leftMargin = 0;
  322. DrawEspacio();
  323. }
  324. private void DrawEspacio(){
  325. line = "";
  326. cb.SetTextMatrix(leftMargin, YPosition());
  327. cb.SetFontAndSize(font.BaseFont, fontSize);
  328. cb.ShowText(line);
  329. count++;
  330. }
  331. }
  332. public class TicketOrderItem{
  333. char[] delimitador = new char[] { '?' };
  334. public TicketOrderItem(char delimit){
  335. delimitador = new char[] { delimit };
  336. }
  337. public string GetItemCantidad(string TicketOrderItem){
  338. string[] delimitado = TicketOrderItem.Split(delimitador);
  339. return delimitado[0];
  340. }
  341. public string GetItemName(string TicketOrderItem){
  342. string[] delimitado = TicketOrderItem.Split(delimitador);
  343. return delimitado[1];
  344. }
  345. public string GetItemPrice(string TicketOrderItem) {
  346. string[] delimitado = TicketOrderItem.Split(delimitador);
  347. return delimitado[2];
  348. }
  349. public string GenerateItem(string cantidad,
  350. string itemName, string price){
  351. return cantidad + delimitador[0] +
  352. itemName + delimitador[0] + price;
  353. }
  354. }
  355. public class TicketOrderTotal{
  356. char[] delimitador = new char[] { '?' };
  357. public TicketOrderTotal(char delimit){
  358. delimitador = new char[] { delimit };
  359. }
  360. public string GetTotalName(string totalItem){
  361. string[] delimitado = totalItem.Split(delimitador);
  362. return delimitado[0];
  363. }
  364. public string GetTotalCantidad(string totalItem){
  365. string[] delimitado = totalItem.Split(delimitador);
  366. return delimitado[1];
  367. }
  368. public string GenerateTotal(string totalName,
  369. string price){
  370. return totalName + delimitador[0] + price;
  371. }
  372. }
  373. }
Implementación:

Nota: Este es un ejemplo tal y cual lo utlicé para una aplicación web, el programador debe adaptar este código a sus necesidades.

  1. public bool ImprimeTicket(int prmFolioTicket, string user, string path,
  2. string fileName, string prmHeaderImage)
  3. {
  4. SqlConnection cnnReporte = new SqlConnection(clsMain.CnnStr);
  5. try
  6. {
  7. double varEFECTIVO = 0;
  8. double varCAMBIO = 0;
  9. string varTOTAL_LETRA = "";
  10. double varTOTAL = 0;
  11. double varDESCUENTO = 0;
  12. double varIVA = 0;
  13. TicketPDF ticket = new TicketPDF();
  14. ticket.Path = path;
  15. ticket.FileName = fileName;
  16. ticket.HeaderImage = prmHeaderImage;
  17. ticket.AddHeaderLine(clsMain.WebConfig("TICKET_HEADER_01"));
  18. ticket.AddHeaderLine(clsMain.WebConfig("TICKET_HEADER_02"));
  19. ticket.AddHeaderLine(clsMain.WebConfig("TICKET_HEADER_03"));
  20. ticket.AddHeaderLine(clsMain.WebConfig("TICKET_HEADER_04"));
  21. ticket.AddHeaderLine(clsMain.WebConfig("TICKET_HEADER_05"));
  22. cnnReporte.Open();
  23. SqlCommand cmdReporte =
  24. new SqlCommand("proc_VENTA_TICKET", cnnReporte);
  25. cmdReporte.CommandType = CommandType.StoredProcedure;
  26. //params
  27. cmdReporte.Parameters.Add("@ID_VENTA",
  28. SqlDbType.Int).Value = prmFolioTicket;
  29. SqlDataReader drReporte;
  30. drReporte = cmdReporte.ExecuteReader();
  31. while (drReporte.Read())
  32. {
  33. //El metodo AddSubHeaderLine es lo mismo al
  34. //de AddHeaderLine con la diferencia
  35. //de que al final de cada linea
  36. //agrega una linea punteada "=========="
  37. ticket.AddSubHeaderLine("Caja # " +
  38. drReporte["CAJA"].ToString() +
  39. " - Ticket # " + prmFolioTicket.ToString() + "");
  40. ticket.AddSubHeaderLine("Le atendió: " +
  41. drReporte["CAJERO"].ToString() + "");
  42. ticket.AddSubHeaderLine("Fecha y Hora: " +
  43. DateTime.Now.ToShortDateString() + " " +
  44. DateTime.Now.ToShortTimeString());
  45. ticket.AddSubHeaderLine("Cliente: " +
  46. drReporte["CLIENTE"].ToString() + "");
  47. varEFECTIVO = Convert.ToDouble(drReporte["EFECTIVO"]);
  48. varCAMBIO = Convert.ToDouble(drReporte["CAMBIO"]);
  49. varTOTAL_LETRA = drReporte["TOTAL_LETRA"].ToString();
  50. }
  51. ////Details
  52. drReporte.Close();
  53. cmdReporte.Parameters.Clear();
  54. cmdReporte.CommandText = "proc_VENTA_DETALLE_TICKET";
  55. //params
  56. cmdReporte.Parameters.Add("@ID_VENTA",
  57. SqlDbType.Int).Value = prmFolioTicket;
  58. drReporte = cmdReporte.ExecuteReader();
  59. while (drReporte.Read())
  60. {
  61. //El metodo AddItem requeire 3 parametros,
  62. //el primero es cantidad, el segundo es la descripcion
  63. //del producto y el tercero es el precio
  64. if (drReporte["PRODUCTO"].ToString().Length > 11)
  65. {
  66. ticket.AddItem(drReporte["CANTIDAD"].ToString(),
  67. drReporte["PRODUCTO"].ToString().Substring(0, 11),
  68. String.Format("{0:C}",
  69. Convert.ToDouble(drReporte["CANTIDAD"]) *
  70. Convert.ToDouble(drReporte["PRECIO_VENTA"])));
  71. }
  72. else
  73. {
  74. ticket.AddItem(drReporte["CANTIDAD"].ToString(),
  75. drReporte["PRODUCTO"].ToString(), String.Format("{0:C}",
  76. Convert.ToDouble(drReporte["CANTIDAD"]) *
  77. Convert.ToDouble(drReporte["PRECIO_VENTA"])));
  78. }
  79. varTOTAL += Convert.ToDouble(drReporte["TOTAL"]);
  80. varDESCUENTO += Convert.ToDouble(drReporte["DESCUENTO"]);
  81. varIVA +=
  82. Convert.ToDouble(drReporte["IMPUESTO_DOS"]);
  83. }
  84. drReporte.Close();
  85. cmdReporte.Dispose();
  86. cnnReporte.Close();
  87. cnnReporte.Dispose();
  88. //El metodo AddTotal requiere 2 parametros,
  89. //la descripcion del total, y el precio
  90. ticket.AddTotal("SUBTOTAL", String.Format("{0:C}",
  91. varTOTAL - varIVA));
  92. ticket.AddTotal("IVA", String.Format("{0:C}",
  93. varIVA));
  94. ticket.AddTotal("TOTAL", String.Format("{0:C}",
  95. varTOTAL + varDESCUENTO));
  96. ticket.AddTotal("DESCUENTO", String.Format("{0:C}",
  97. -varDESCUENTO));
  98. ticket.AddTotal("GRAN TOTAL", String.Format("{0:C}",
  99. varTOTAL));
  100. ticket.AddTotal("", "");//Ponemos un total
  101. //en blanco que sirve de espacio
  102. ticket.AddTotal("RECIBIDO", String.Format("{0:C}",
  103. varEFECTIVO));
  104. ticket.AddTotal("CAMBIO", String.Format("{0:C}",
  105. varCAMBIO));
  106. ticket.AddTotal("", "");//Ponemos un total
  107. //en blanco que sirve de espacio
  108. ticket.AddTotal("USTED AHORRA", String.Format("{0:C}",
  109. varDESCUENTO));
  110. //El metodo AddFooterLine funciona igual que la cabecera
  111. ticket.AddFooterLine(clsMain.WebConfig("TICKET_FOOTER_01"));
  112. ticket.AddFooterLine(clsMain.WebConfig("TICKET_FOOTER_02"));
  113. ticket.AddFooterLine(clsMain.WebConfig("TICKET_FOOTER_03"));
  114. ticket.AddFooterLine(clsMain.WebConfig("TICKET_FOOTER_04"));
  115. ticket.AddFooterLine(clsMain.WebConfig("TICKET_FOOTER_05"));
  116. //Generamos
  117. if (ticket.Print()) { return (true); }
  118. else { return false; }
  119. }
  120. catch (Exception ex) { throw (ex); }
  121. finally { cnnReporte.Close(); }
  122. }

Listo, espero que les sirva.

19 comentarios:

  1. Eres un tigre!!, Gracias Hermano.

    ResponderEliminar
  2. gracias, pídeme lo que quieras, hasta mi virginidad te doy.

    ResponderEliminar
  3. hola como puedo hacerle para que aparesca completo el nombre del producto... te lo agradeceria mnucho si me explicaras un poco

    ResponderEliminar
  4. jajjaja oye men muy buena pagina la neta y este queria saber como le puedo hacer mas o menos as de cuenta que tengo una base de datos de Mysql y que los datos de una tabla sean las ventas como podria irlos agregando ? tenia pensado hacerlo como tipo listbox y que mientras vayas agregando vantas vaya creciendo el largo del ticket y si quitas que logico disminuya el tamaño me podrias hechar una mano porfa?
    podrias contestar a este mail:
    dar5_0094@hotmail.com
    Porfavor men ayudame

    ResponderEliminar
  5. Hola estoy usando c# 2008 y marca de color rojo en:

    Path
    FileName
    HeaderImage
    AddHeaderLine

    y muchos mas que son de ticket.

    ResponderEliminar
  6. las veces que quieras!

    ResponderEliminar
  7. Este tipo de impresión puede mandar secuencias de escape a la impresora para cortar el ticket y abrir el cajón de dinero

    ResponderEliminar
    Respuestas
    1. no lo se. Sin embargo, cuando este conectada la miniprinter con el cajón de dinero. Se abrirá el cajon de manera automática

      Eliminar
  8. Quisiera saber si se pueden agregar n cantidad de items en el detalle
    y si es posible que sea impreso en una impresora de tickets

    ResponderEliminar
    Respuestas
    1. Si es posible... Es probable que haga un espacio cuando encuentre salto de página tamaño carta

      Eliminar
  9. Juan buenas tardes, tengo un detalle que en algunas impresoras que no puedo configurar el top y bottom al inicio deja un espacio de 12 cm en blanco antes de imprimir, como pudiera corregir ese detalle

    gracias por tu ayuda

    ResponderEliminar
  10. Hola
    tendrás algún ejemplo de como mandarlo imprimir a un miniprinter?
    me gustaría imprimirlo en mi impresora térmica no en un pdf
    mi correo es:
    kike_master@hotmail.com

    ResponderEliminar
  11. Respuestas
    1. Quizas te interese! Actualizado
      https://www.youtube.com/watch?v=I924aw5-CZk

      Eliminar
  12. Hola, la primera vez me funciona bien, posteriormente me dice que no se puede acceder al archivo cerrado, alguna idea?

    ResponderEliminar
  13. cuando hay varios detalles se corta en pdf

    ResponderEliminar
  14. Le hice algunos ajustes para adaptarlo a mis necesidades y funciona perfecto, lo estoy usando con .NET Core 3

    ResponderEliminar
  15. Saludos amigos, donde indico el nombre de la impresora.

    No me ubico en esta parte.Si me podrian ayudar gracias.

    public bool ImprimeTicket(int prmFolioTicket, string user, string path,string fileName, string prmHeaderImage)
    { }

    ResponderEliminar