Imagen thumbnail sin archivo temporal en ASP.NET
Hoy vamos a sacudir el polvo de este abandonado esporádicamente actualizado blog con un truquillo bastante interesante a la hora de mostrar una versión miniatura de una imagen en nuestra web.
Lo que en general hacíamos es crear la versión miniatura en memoria que indefectiblemente debe existir físicamente en disco para luego enlazarla con nuestro control <img> ó <asp:image>.
El tip consiste en en enlazar nuestro control de imagen contra un archivo del tipo “Generic Handler” (extensión “.ashx”). Básicamente nos permite responder una petición HTTP con un archivo distinto a la típica página conformada por HTML y en este caso, por supuesto, vamos a utilizarlo para responder la petición con nuestra imagen miniatura.
A mi parecer hay 2 problemas que evitamos con esto:
- Problemas de permisos sobre el subdirectorio y/o archivos temporales que creamos ya que estamos hablando del contexto web.
- También debemos buscar la forma de eliminar dicho archivo temporal en algún momento para que no se amontonen en cantidades astronómicas.
Antes ACLARO:
- En este ejemplo no se encripta la ruta de la imagen pasada como argumento al archivo ashx. Esto debería hacerse, ya que en el render HTML enviado al cliente se observa dicha ruta completa con un simple “View Source” en nuestro navegador favorito.
- Debemos agregar la referencia a System.Windows.Forms. Más adelante, en el código, verán que utilizo el método TextRenderer.DrawText para dibujar un string dentro de la imagen indicando que ha habido un error.
Ahora sí. Vamos a escribir una pagina de ejemplo donde se muestra 2 versiones de una misma imagen.
La primera (izquierda) muestra la imagen con sus dimensiones originales, en este caso 400 x 400 pixeles. La segunda versión de la imagen (derecha) una versión alternativa dimensionada a 200 x 200 pixeles. Así se verá …
Por lo tanto tendremos esta pagina donde se muestran las imágenes (en mi ejemplo ImagenSinTemp.aspx) y también tendremos el archivo correspondiente al handler que muestra la miniatura (en mi ejemplo HandlerImagenes.ashx)
El diseño del archivo principal (el que contiene las imágenes) queda de la siguiente manera:
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="ImagenSinTemp.aspx.vb" Inherits="Sitio20.ImagenSinTemp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Ejemplo miniaturistico</title> </head> <body> <form id="frmImagen" runat="server"> <table width="620" border="1"> <tr> <td align="center"> Esta es la imagen original<br /> (400 x 400)</td> <td align="center"> Replica mas pequeña<br /> (200 x 200)</td> </tr> <tr> <td align="center"> <asp:Image ID="imgOriginal" runat="server" ImageUrl="~/ejemplo.jpg" /></td> <td align="center"> <asp:Image ID="imgMiniatura" runat="server" ImageUrl="HandlerImagenes.ashx" /></td> </tr> </table> </form> </body> </html>
Su code-behind simplemente en este caso tengo el Load de la pagina que setea la propiedad ImagenUrl de la imagen que muestra la versión miniatura. Nótese que se setea tanto la ruta completa del archivo como las dimensiones que deseo que tenga.
Partial Public Class ImagenSinTemp Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load imgMiniatura.ImageUrl = "HandlerImagenes.ashx?" & _ "archivo=" & HttpUtility.UrlEncode("D:\Desarrollo\NET\Sitio20\Ejemplo.jpg") & _ "&ancho=200" & _ "&alto=200" End Sub End Class
Y a continuación el código correspondiente al handler de imagenes:
Imports System.Web Imports System.Web.Services Imports System.IO Imports System.Drawing Imports System.Drawing.Imaging Public Class HandlerImagenes Implements System.Web.IHttpHandler Private Const __ANCHO_DEFECTO As Integer = 200 Private Const __ALTO_DEFECTO As Integer = 200 ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return False End Get End Property Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest Dim sArchivo As String = String.Empty Dim iAncho As Integer = __ANCHO_DEFECTO Dim iAlto As Integer = __ALTO_DEFECTO context.Response.Clear() context.Response.ClearContent() If Not String.IsNullOrEmpty(context.Request("archivo")) Then If (File.Exists(context.Request("archivo"))) Then sArchivo = HttpUtility.UrlDecode(context.Request("archivo")) If Not String.IsNullOrEmpty(context.Request("ancho")) Then iAncho = context.Request("ancho") Else ResponderError(context, "No se ha especificado ancho", iAlto, iAncho) End If If Not String.IsNullOrEmpty(context.Request("alto")) Then iAlto = context.Request("alto") Else ResponderError(context, "No se ha especificado alto", iAlto, iAncho) End If ReponderImagen(context, sArchivo, iAncho, iAlto) Else ResponderError(context, "El archivo especificado no existe", iAlto, iAncho) End If Else ResponderError(context, "No se ha indicado archivo", iAlto, iAncho) End If context.Response.End() End Sub Public Shared Sub ReponderImagen(ByVal pContexto As HttpContext, _ ByVal pArchivo As String, _ ByVal pAncho As Integer, _ ByVal pAlto As Integer) Dim bmpOut As System.Drawing.Bitmap = Nothing Dim tempStream As MemoryStream = New MemoryStream() Try Dim loBMP As New Bitmap(pArchivo) Dim loFormat As ImageFormat = loBMP.RawFormat Dim lnRatio As Decimal Dim lnNewWidth As Integer = 0 Dim lnNewHeight As Integer = 0 If loBMP.Width < pAncho AndAlso loBMP.Height < pAlto Then loBMP.Save(tempStream, bmpOut.RawFormat) Else If loBMP.Width > loBMP.Height Then lnRatio = CDec(pAncho) / loBMP.Width lnNewWidth = pAncho Dim lnTemp As Decimal = loBMP.Height * lnRatio lnNewHeight = CInt(Math.Truncate(lnTemp)) Else lnRatio = CDec(pAlto) / loBMP.Height lnNewHeight = pAlto Dim lnTemp As Decimal = loBMP.Width * lnRatio lnNewWidth = CInt(Math.Truncate(lnTemp)) End If bmpOut = New Bitmap(lnNewWidth, lnNewHeight) Dim g As Graphics = Graphics.FromImage(bmpOut) g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic g.FillRectangle(Brushes.White, 0, 0, lnNewWidth, lnNewHeight) g.DrawImage(loBMP, 0, 0, lnNewWidth, lnNewHeight) loBMP.Dispose() bmpOut.Save(tempStream, ImageFormat.Jpeg) pContexto.Response.ContentType = "image/jpg" pContexto.Response.BinaryWrite(tempStream.ToArray()) End If Catch ex As Exception ResponderError(pContexto, "Error al crear imagen: " & ex.Message, pAlto, pAncho) End Try End Sub Public Shared Sub ResponderError(ByVal pContexto As HttpContext, ByVal pTexto As String, ByVal pAlto As Integer, ByVal pAncho As Integer) Dim tempStream As MemoryStream = New MemoryStream() Dim bitmap As New Bitmap(pAlto, pAncho) Dim g As Graphics = Graphics.FromImage(bitmap) pContexto.Response.ClearContent() pContexto.Response.ContentType = "image/jpg" g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit g.Clear(Color.White) System.Windows.Forms.TextRenderer.DrawText(g, pTexto, _ New Font("Tahoma", 14, FontStyle.Regular, GraphicsUnit.Pixel), New Point(0, 0), _ Color.Black) bitmap.Save(tempStream, ImageFormat.Jpeg) pContexto.Response.BinaryWrite(tempStream.ToArray) End Sub End Class
La propiedad IsReausable y el método ProcessRequest corresponden a la implementación de la interfaz IHttpHandler.
El bastante simple. En ProcessRequest se analizan los parámetros recibidos devolviendo la imagen con la cadena que indica el error donde corresponde y en caso de estar todo correcto el método ReponderImagen es quien se encarga de crear la imagen miniatura.
Queda a criterio de ustedes personalizar cómo se muestra el mensaje de error acorde a sus propias necesidades.
Saludos!
Llenar ComboBox con miembros de una enumeración
Algo que nos permite .NET y (a mi parecer) no muy conocido es la posibilidad de enlazar la propiedad DataSource de un ComboBox contra un Enum y de esta forma completar el control con sus miembros.
La utilidad para este código puede estar en formularios donde necesitemos setear propiedades de objetos al iniciar un proceso (dando por sentado que varias veces tenemos propiedades de tipo Enum)
Pongamos como ejemplo la siguiente enumeración:
Private Enum eVerduras As Byte Tomate = &H0 Lechuga = &H1 Papa = &H2 Zapallo = &H3 Remolacha = &H4 End Enum
Para poblar el combo con sus miembros debemos llamar al método GetValues del espacio de nombres System.Enum pasando el tipo de nuestra enumeración.
cmbVerduras.DataSource = System.Enum.GetValues(GetType(eVerduras))

Enumeracion en ComboBox
Como puede verse, los miembros aparecerán en el ComboBox ordenados según el valor numérico que tienen asignado en forma ascendente.
Para los mas quisquillosos detallistas podemos hacer que aparezcan ordenados por nombre.
Leer más...
iTextSharp – Escribir texto en columna.
Como segunda entrega en relación a iTextSharp hoy veremos como insertar texto encolumnado.
A continuación detallaré como podemos escribir texto dentro de un rectángulo definido en nuestra página.
Este texto será recortado apropiadamente por la biblioteca completando el rectángulo especificado.
Basicamente debemos llamar a 2 métodos del objeto ColumnText:
- En primer lugar debemos llamar a SetSimpleColumn donde definiremos el rectángulo de la columna.
- Luego, para escribir el texto llamamos a Go().
Alguien se preguntará a si mismo … “¿Porqué debo llamar a dos métodos distintos?“
La respuesta a ambas preguntas se responden con lo siguiente.
Razón 1:
Go() nos devuelve un Integer que deberemos tener en cuenta. Si nos devuelve ColumnText.NO_MORE_COLUMN significa que el texto no cupo dentro del rectángulo donde escribimos. En cambio si devuelve ColumnText.NO_MORE_TEXT significa que hemos escrito todo el texto que asignamos al objeto ColumnText.
Razón 2:
Go() tiene una sobrecarga con un Boolean como parámetro que indica si deseamos simular la escritura. Es decir, nos devolverá un valor (lo explicado en la razón 1) pero sin escribir el texto. Útil, por ejemplo, para darnos cuenta si debemos crear una nueva página en caso que queramos escribir un texto sin interrupciones.
De ahí que la definición del área y la escritura del texto se encuentren en métodos separados.
Un código simple de ejemplo como en el post anterior:
Private Structure stColumna Dim MargenDerecho As Single Dim MargenIzquierdo As Single End Structure Private Sub GenerarPDF_Columnas() Dim oDoc As New iTextSharp.text.Document(PageSize.A4, 0, 0, 0, 0) Dim pdfw As iTextSharp.text.pdf.PdfWriter Dim cb As PdfContentByte Dim sNombreArchivo As String = "C:\PdfColumnas.pdf" Dim ct As ColumnText Dim arrColumnas(1) As stColumna Dim iEstado As Integer = 0, iColumna As Integer = 0 Const INTERLINEADO As Single = 20 Const MARGEN_INFERIOR As Single = 80 Try pdfw = PdfWriter.GetInstance(oDoc, New FileStream(sNombreArchivo, _ FileMode.Create, FileAccess.Write, FileShare.None)) oDoc.Open() oDoc.NewPage() cb = pdfw.DirectContent ct = New ColumnText(cb) arrColumnas(0).MargenIzquierdo = 60 arrColumnas(0).MargenDerecho = 280 arrColumnas(1).MargenIzquierdo = 320 arrColumnas(1).MargenDerecho = 530 'Asignamos texto, texto y mas texto ... ct.AddText(New Phrase("PDF (del inglés Portable Document Format, Formato de Documento Portátil) es un formato de almacenamiento de documentos, desarrollado por la empresa Adobe Systems. Está especialmente ideado para documentos susceptibles de ser impresos, ya que especifica toda la información necesaria para la presentación final del documento, determinando todos los detalles de cómo va a quedar, no requiriéndose procesos anteriores de ajuste ni de maquetación.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Cada vez se utiliza más también como especificación de visualización, gracias a la gran calidad de las fuentes utilizadas y a las facilidades que ofrece para el manejo del documento, como búsquedas, hiperenlaces, etc.Recientemente pasó a ser un estándar ISO 32000.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Las versiones tempranas de los documentos PDF no tenían hipervínculos externos; por esta razón, su adopción en internet era considerablemente reducida y no tenía mucha popularidad. En esos tiempos, eran comunes las conexiones a internet a través de módem telefónico, y el tamaño de los documentos PDF era mucho más grande que otros tipos de documentos, como el texto plano (sin formato), por ejemplo; por lo tanto, la banda ancha fue un factor clave para su aceptación en internet.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Además, ya existían otros tipos de documentos que le hacían fuerte competencia al tipo de documentos PDF, como por ejemplo, los documentos 'PostScript' (.ps), los cuales, en esos tiempos, eran considerablemente comunes.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Con el tiempo, los documentos PDF fueron adquiriendo popularidad de varias formas diferentes, como publicidad. Este tipo de documentos empezó a popularizarse considerablemente, hasta convertirse en un estándar.[sin referencias] Este tipo de documento es vista como una página digital que está lista para imprimirse exactamente como su muestra en la pantalla, sin problemas de márgenes a la hora de imprimir, tal como sucede en otros documentos digitales.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("En los recientes años de su popularidad, han salido varias aplicaciones lectoras de este tipo de archivos. Su popularidad ha abierto la posibilidad de crear documentos PDF con programas de software libre, como lo hace en la actualidad OpenOffice.org. Otras aplicaciones, son incluso capaces de editarlos, sin necesidad de usar la típica aplicación para crear y editar documentos PDF de Adobe.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("El formato de archivos PDF ha cambiado varias veces, pues las nuevas versiones del Acrobat de adobe se han lanzado. Ha habido ocho versiones de PDF: 1.0 (1993), 1.1 (1994), 1.2 (1996), 1.3 (1999), 1.4 (2001), 1.5 (2003), 1.6 (2005), y 1.7 (2006), correspondiendo al Acrobat lanza 1.0 a 8.0.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Es multiplataforma, es decir, puede ser presentado por los principales sistemas operativos (Windows, Unix/Linux o Mac), sin que se modifiquen ni el aspecto ni la estructura del documento original", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Puede integrar cualquier combinación de texto, gráficos, imágenes e incluso música. Es uno de los formatos más extendidos en Internet para el intercambio de documentos. Por ello es muy utilizado por empresas, gobiernos e instituciones educativas. ", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Es una especificación abierta, para la que se han generado herramientas de Software Libre que permiten crear, visualizar o modificar documentos en formato PDF. Un ejemplo es la suite ofimática OpenOffice.org. ", FontFactory.GetFont(FontFactory.HELVETICA, 12))) ct.AddText(New Phrase("Puede cifrarse para proteger su contenido e incluso firmarlo digitalmente. El archivo PDF puede crearse desde varias aplicaciones exportando el archivo, como es el caso de los programas de OpenOffice.org.", FontFactory.GetFont(FontFactory.HELVETICA, 12))) 'Mientras haya texto While (iEstado <> ColumnText.NO_MORE_TEXT) 'Seteamos el rectángulo donde escribir ... ct.SetSimpleColumn(arrColumnas(iColumna).MargenDerecho, MARGEN_INFERIOR, _ arrColumnas(iColumna).MargenIzquierdo, _ oDoc.PageSize.Height, INTERLINEADO, Element.ALIGN_JUSTIFIED) ' ... y escribimos iEstado = ct.Go() 'Si la columna no fue suficiente: If (iEstado = ColumnText.NO_MORE_COLUMN) Then iColumna = iColumna + 1 'Si se alcanzó la cantidad de columnas por página If iColumna > (arrColumnas.Length - 1) Then 'Salto de pagina oDoc.NewPage() iColumna = 0 End If End If End While oDoc.Close() Catch ex As Exception 'Si hubo un error y el archivo existe ... If File.Exists(sNombreArchivo) Then 'Chequeo si el Doc esta abierto y asi poder 'desbloquear el archivo para su eliminacion. If oDoc.IsOpen Then oDoc.Close() '... lo elimino del disco. File.Delete(sNombreArchivo) End If Throw New Exception("Error al generar archivo PDF (" & ex.Message & ")") Finally 'Limpieza cb = Nothing pdfw = Nothing oDoc = Nothing End Try End Sub
También es posible definir el área donde escribir el texto como una forma irregular. Por ejemplo podríamos pedir que escriba el párrafo dentro de un área triangular. Dejo eso para un próximo post.
Introducción a iTextSharp, generar PDF desde VB.Net
Para quienes aún no tienen el gusto les presento iTextSharp, una biblioteca original de Java portada a .Net.
¿Su función? Arrimarle el paraíso a aquellos que buscan generar archivos con formato Adobe PDF desde la plataforma .Net. No menos importante destacar que se trata de un proyecto de código abierto totalmente libre. Existen infinidad de bibliotecas similares pagas pero como siempre tenemos nuestra alternativa gratuita
.
Puedes descargarla aquí.
Veamos como utilizarla:En primer lugar, una vez descargada la biblioteca, necesitamos incluir la referencia en nuestro proyecto.
- Clickeamos en “Add Reference” dentro del menú “Project”

- Seleccionamos nuestra biblioteca recien descargada.

- De modo que nos quede referenciado en nuestro proyecto.

Y simplemente para cerrar esta archi sencilla intro les dejo un breve ejemplo de cómo hacer uso del assembly para generar un documento PDF con texto …
Leer más…


